packages feed

deriving-aeson (empty) → 0

raw patch · 6 files changed

+199/−0 lines, 6 filesdep +aesondep +basedep +bytestringsetup-changed

Dependencies added: aeson, base, bytestring, deriving-aeson

Files

+ CHANGELOG.md view
@@ -0,0 +1,5 @@+# Revision history for deriving-aeson++## 0 -- 2020-02-26++* First version. Released on an unsuspecting world.
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Fumiaki Kinoshita (c) 2020++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Fumiaki Kinoshita nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ deriving-aeson.cabal view
@@ -0,0 +1,29 @@+cabal-version:       2.4+name:                deriving-aeson+version:             0+synopsis:            Type driven generic aeson instance customisation+description:         This package provides a newtype wrapper with+  FromJSON/ToJSON instances customisable via a phantom type parameter.+  The instances can be rendered to the original type using DerivingVia.+bug-reports:         https://github.com/fumieval/deriving-aeson+license:             BSD-3-Clause+license-file:        LICENSE+author:              Fumiaki Kinoshita+maintainer:          fumiexcel@gmail.com+copyright:           Copyright (c) 2020 Fumiaki Kinoshita+category:            JSON, Generics+extra-source-files:  CHANGELOG.md++library+  exposed-modules:     Deriving.Aeson+  build-depends:       base >= 4.12 && <5, aeson+  hs-source-dirs: src+  default-language:    Haskell2010+  ghc-options: -Wall++test-suite test+  type: exitcode-stdio-1.0+  main-is: test.hs+  build-depends: base, aeson, deriving-aeson, bytestring+  hs-source-dirs: tests+  default-language:    Haskell2010
+ src/Deriving/Aeson.hs view
@@ -0,0 +1,113 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE AllowAmbiguousTypes #-}+{-# LANGUAGE UndecidableInstances #-}+--------------------+-- | Type-directed aeson instance CustomJSONisation+--------------------+module Deriving.Aeson+  ( CustomJSON(..)+  , FieldLabelModifier+  , ConstrctorTagModifier+  , OmitNothingFields+  , TagSingleConstructors+  , NoAllNullaryToStringTag+  -- * Name modifiers+  , StripPrefix+  , CamelToKebab+  , CamelToSnake+  -- * Interface+  , AesonOptions(..)+  , StringModifier(..)+  -- * Reexports+  , FromJSON+  , ToJSON+  , Generic+  )where++import Data.Aeson+import Data.Coerce+import Data.List (stripPrefix)+import Data.Maybe (fromMaybe)+import Data.Proxy+import GHC.Generics+import GHC.TypeLits++-- | A newtype wrapper which gives FromJSON/ToJSON instances with modified options.+newtype CustomJSON t a = CustomJSON { unCustomJSON :: a }++instance (AesonOptions t, Generic a, GFromJSON Zero (Rep a)) => FromJSON (CustomJSON t a) where+  parseJSON = (coerce `asTypeOf` fmap CustomJSON) . genericParseJSON (aesonOptions @t)+  {-# INLINE parseJSON #-}++instance (AesonOptions t, Generic a, GToJSON Zero (Rep a)) => ToJSON (CustomJSON t a) where+  toJSON = genericToJSON (aesonOptions @t) . unCustomJSON+  {-# INLINE toJSON #-}++-- | Function applied to field labels. Handy for removing common record prefixes for example.+data FieldLabelModifier t++-- | Function applied to constructor tags which could be handy for lower-casing them for example.+data ConstrctorTagModifier t++-- | Record fields with a Nothing value will be omitted from the resulting object.+data OmitNothingFields++-- | Encode types with a single constructor as sums, so that allNullaryToStringTag and sumEncoding apply.+data TagSingleConstructors++-- | the encoding will always follow the 'sumEncoding'.+data NoAllNullaryToStringTag++-- | Strip prefix @t@. If it doesn't have the prefix, keep it as-is.+data StripPrefix t++-- | CamelCase to snake_case+data CamelToSnake++-- | CamelCase to kebab-case+data CamelToKebab++-- | Reify a function which modifies names+class StringModifier t where+  getStringModifier :: String -> String++instance KnownSymbol k => StringModifier (StripPrefix k) where+  getStringModifier = fromMaybe <*> stripPrefix (symbolVal (Proxy @k))++-- | Left-to-right (@'flip' '.'@) composition+instance (StringModifier a, StringModifier b) => StringModifier (a, b) where+  getStringModifier = getStringModifier @b . getStringModifier @a++instance StringModifier CamelToKebab where+  getStringModifier = camelTo2 '-'++instance StringModifier CamelToSnake where+  getStringModifier = camelTo2 '_'++-- | Reify 'Options' from a type-level list+class AesonOptions xs where+  aesonOptions :: Options++instance AesonOptions '[] where+  aesonOptions = defaultOptions++instance AesonOptions xs => AesonOptions (OmitNothingFields ': xs) where+  aesonOptions = (aesonOptions @xs) { omitNothingFields = True }++instance (StringModifier f, AesonOptions xs) => AesonOptions (FieldLabelModifier f ': xs) where+  aesonOptions = (aesonOptions @xs) { fieldLabelModifier = getStringModifier @f }++instance (StringModifier f, AesonOptions xs) => AesonOptions (ConstrctorTagModifier f ': xs) where+  aesonOptions = (aesonOptions @xs) { constructorTagModifier = getStringModifier @f }++instance AesonOptions xs => AesonOptions (TagSingleConstructors ': xs) where+  aesonOptions = (aesonOptions @xs) { tagSingleConstructors = True }++instance AesonOptions xs => AesonOptions (NoAllNullaryToStringTag ': xs) where+  aesonOptions = (aesonOptions @xs) { allNullaryToStringTag = False }
+ tests/test.hs view
@@ -0,0 +1,20 @@+{-# LANGUAGE DerivingVia, DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+module Main where++import Data.Aeson+import qualified Data.ByteString.Lazy.Char8 as BL+import Deriving.Aeson++data User = User+  { userId :: Int+  , userName :: String+  , userAPIToken :: Maybe String+  } deriving Generic+  deriving (FromJSON, ToJSON)+  via CustomJSON '[OmitNothingFields, FieldLabelModifier (StripPrefix "user", CamelToSnake)] User++testData :: [User]+testData = [User 42 "Alice" Nothing, User 43 "Bob" (Just "xyz")]++main = BL.putStrLn $ encode $ testData