{-# LANGUAGE DataKinds #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE UndecidableInstances #-}
module Deriving.Aeson
( CustomJSON(..)
, FieldLabelModifier
, ConstructorTagModifier
, OmitNothingFields
, RejectUnknownFields
, TagSingleConstructors
, NoAllNullaryToStringTag
, UnwrapUnaryRecords
, SumTaggedObject
, SumUntaggedValue
, SumObjectWithSingleField
, SumTwoElemArray
, StripPrefix
, CamelTo
, CamelToKebab
, CamelToSnake
, Rename
, AesonOptions(..)
, StringModifier(..)
, FromJSON
, ToJSON
, Generic
)where
import Data.Aeson
import Data.Coerce
import Data.Kind
import Data.List (stripPrefix)
import Data.Maybe (fromMaybe)
import Data.Proxy
import GHC.Generics
import GHC.TypeLits
newtype CustomJSON t a = CustomJSON { forall {k} (t :: k) a. CustomJSON t a -> a
unCustomJSON :: a }
instance (AesonOptions t, Generic a, GFromJSON Zero (Rep a)) => FromJSON (CustomJSON t a) where
parseJSON :: Value -> Parser (CustomJSON t a)
parseJSON = (coerce :: forall a b. Coercible a b => a -> b
coerce forall a. a -> a -> a
`asTypeOf` forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall {k} (t :: k) a. a -> CustomJSON t a
CustomJSON) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a.
(Generic a, GFromJSON Zero (Rep a)) =>
Options -> Value -> Parser a
genericParseJSON (forall {k} (xs :: k). AesonOptions xs => Options
aesonOptions @t)
{-# INLINE parseJSON #-}
instance (AesonOptions t, Generic a, GToJSON Zero (Rep a), GToEncoding Zero (Rep a)) => ToJSON (CustomJSON t a) where
toJSON :: CustomJSON t a -> Value
toJSON = forall a.
(Generic a, GToJSON' Value Zero (Rep a)) =>
Options -> a -> Value
genericToJSON (forall {k} (xs :: k). AesonOptions xs => Options
aesonOptions @t) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall {k} (t :: k) a. CustomJSON t a -> a
unCustomJSON
{-# INLINE toJSON #-}
toEncoding :: CustomJSON t a -> Encoding
toEncoding = forall a.
(Generic a, GToJSON' Encoding Zero (Rep a)) =>
Options -> a -> Encoding
genericToEncoding (forall {k} (xs :: k). AesonOptions xs => Options
aesonOptions @t) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall {k} (t :: k) a. CustomJSON t a -> a
unCustomJSON
{-# INLINE toEncoding #-}
data FieldLabelModifier t
data ConstructorTagModifier t
data OmitNothingFields
data RejectUnknownFields
data TagSingleConstructors
data NoAllNullaryToStringTag
data UnwrapUnaryRecords
data StripPrefix t
data CamelTo (separator :: Symbol)
type CamelToSnake = CamelTo "_"
type CamelToKebab = CamelTo "-"
data Rename (from :: Symbol) (to :: Symbol)
class StringModifier t where
getStringModifier :: String -> String
instance KnownSymbol k => StringModifier (StripPrefix k) where
getStringModifier :: String -> String
getStringModifier = forall a. a -> Maybe a -> a
fromMaybe forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall a. Eq a => [a] -> [a] -> Maybe [a]
stripPrefix (forall (n :: Symbol) (proxy :: Symbol -> *).
KnownSymbol n =>
proxy n -> String
symbolVal (forall {k} (t :: k). Proxy t
Proxy @k))
instance StringModifier '[] where
getStringModifier :: String -> String
getStringModifier = forall a. a -> a
id
instance (StringModifier a, StringModifier as) => StringModifier (a ': as) where
getStringModifier :: String -> String
getStringModifier = forall {k} (t :: k). StringModifier t => String -> String
getStringModifier @as forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall {k} (t :: k). StringModifier t => String -> String
getStringModifier @a
instance (StringModifier a, StringModifier b) => StringModifier (a, b) where
getStringModifier :: String -> String
getStringModifier = forall {k} (t :: k). StringModifier t => String -> String
getStringModifier @b forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall {k} (t :: k). StringModifier t => String -> String
getStringModifier @a
instance (StringModifier a, StringModifier b, StringModifier c) => StringModifier (a, b, c) where
getStringModifier :: String -> String
getStringModifier = forall {k} (t :: k). StringModifier t => String -> String
getStringModifier @c forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall {k} (t :: k). StringModifier t => String -> String
getStringModifier @b forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall {k} (t :: k). StringModifier t => String -> String
getStringModifier @a
instance (StringModifier a, StringModifier b, StringModifier c, StringModifier d) => StringModifier (a, b, c, d) where
getStringModifier :: String -> String
getStringModifier = forall {k} (t :: k). StringModifier t => String -> String
getStringModifier @d forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall {k} (t :: k). StringModifier t => String -> String
getStringModifier @c forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall {k} (t :: k). StringModifier t => String -> String
getStringModifier @b forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall {k} (t :: k). StringModifier t => String -> String
getStringModifier @a
instance (KnownSymbol separator, NonEmptyString separator) => StringModifier (CamelTo separator) where
getStringModifier :: String -> String
getStringModifier = Char -> String -> String
camelTo2 Char
char
where
char :: Char
char = case forall (n :: Symbol) (proxy :: Symbol -> *).
KnownSymbol n =>
proxy n -> String
symbolVal (forall {k} (t :: k). Proxy t
Proxy @separator) of
Char
c : String
_ -> Char
c
String
_ -> forall a. HasCallStack => String -> a
error String
"Impossible"
instance (KnownSymbol from, KnownSymbol to) => StringModifier (Rename from to) where
getStringModifier :: String -> String
getStringModifier String
s = if String
s forall a. Eq a => a -> a -> Bool
== forall (n :: Symbol) (proxy :: Symbol -> *).
KnownSymbol n =>
proxy n -> String
symbolVal (forall {k} (t :: k). Proxy t
Proxy @from) then forall (n :: Symbol) (proxy :: Symbol -> *).
KnownSymbol n =>
proxy n -> String
symbolVal (forall {k} (t :: k). Proxy t
Proxy @to) else String
s
type family NonEmptyString (xs :: Symbol) :: Constraint where
NonEmptyString "" = TypeError ('Text "Empty string separator provided for camelTo separator")
NonEmptyString _ = ()
data SumTaggedObject t c
data SumUntaggedValue
data SumObjectWithSingleField
data SumTwoElemArray
class AesonOptions xs where
aesonOptions :: Options
instance AesonOptions '[] where
aesonOptions :: Options
aesonOptions = Options
defaultOptions
instance AesonOptions xs => AesonOptions (UnwrapUnaryRecords ': xs) where
aesonOptions :: Options
aesonOptions = (forall {k} (xs :: k). AesonOptions xs => Options
aesonOptions @xs) { unwrapUnaryRecords :: Bool
unwrapUnaryRecords = Bool
True }
instance AesonOptions xs => AesonOptions (OmitNothingFields ': xs) where
aesonOptions :: Options
aesonOptions = (forall {k} (xs :: k). AesonOptions xs => Options
aesonOptions @xs) { omitNothingFields :: Bool
omitNothingFields = Bool
True }
instance AesonOptions xs => AesonOptions (RejectUnknownFields ': xs) where
aesonOptions :: Options
aesonOptions = (forall {k} (xs :: k). AesonOptions xs => Options
aesonOptions @xs) { rejectUnknownFields :: Bool
rejectUnknownFields = Bool
True }
instance (StringModifier f, AesonOptions xs) => AesonOptions (FieldLabelModifier f ': xs) where
aesonOptions :: Options
aesonOptions = let next :: Options
next = forall {k} (xs :: k). AesonOptions xs => Options
aesonOptions @xs in
Options
next { fieldLabelModifier :: String -> String
fieldLabelModifier = Options -> String -> String
fieldLabelModifier Options
next forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall {k} (t :: k). StringModifier t => String -> String
getStringModifier @f }
instance (StringModifier f, AesonOptions xs) => AesonOptions (ConstructorTagModifier f ': xs) where
aesonOptions :: Options
aesonOptions = let next :: Options
next = forall {k} (xs :: k). AesonOptions xs => Options
aesonOptions @xs in
Options
next { constructorTagModifier :: String -> String
constructorTagModifier = Options -> String -> String
constructorTagModifier Options
next forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall {k} (t :: k). StringModifier t => String -> String
getStringModifier @f }
instance AesonOptions xs => AesonOptions (TagSingleConstructors ': xs) where
aesonOptions :: Options
aesonOptions = (forall {k} (xs :: k). AesonOptions xs => Options
aesonOptions @xs) { tagSingleConstructors :: Bool
tagSingleConstructors = Bool
True }
instance AesonOptions xs => AesonOptions (NoAllNullaryToStringTag ': xs) where
aesonOptions :: Options
aesonOptions = (forall {k} (xs :: k). AesonOptions xs => Options
aesonOptions @xs) { allNullaryToStringTag :: Bool
allNullaryToStringTag = Bool
False }
instance (KnownSymbol t, KnownSymbol c, AesonOptions xs) => AesonOptions (SumTaggedObject t c ': xs) where
aesonOptions :: Options
aesonOptions = (forall {k} (xs :: k). AesonOptions xs => Options
aesonOptions @xs) { sumEncoding :: SumEncoding
sumEncoding = String -> String -> SumEncoding
TaggedObject (forall (n :: Symbol) (proxy :: Symbol -> *).
KnownSymbol n =>
proxy n -> String
symbolVal (forall {k} (t :: k). Proxy t
Proxy @t)) (forall (n :: Symbol) (proxy :: Symbol -> *).
KnownSymbol n =>
proxy n -> String
symbolVal (forall {k} (t :: k). Proxy t
Proxy @c)) }
instance (AesonOptions xs) => AesonOptions (SumUntaggedValue ': xs) where
aesonOptions :: Options
aesonOptions = (forall {k} (xs :: k). AesonOptions xs => Options
aesonOptions @xs) { sumEncoding :: SumEncoding
sumEncoding = SumEncoding
UntaggedValue }
instance (AesonOptions xs) => AesonOptions (SumObjectWithSingleField ': xs) where
aesonOptions :: Options
aesonOptions = (forall {k} (xs :: k). AesonOptions xs => Options
aesonOptions @xs) { sumEncoding :: SumEncoding
sumEncoding = SumEncoding
ObjectWithSingleField }
instance (AesonOptions xs) => AesonOptions (SumTwoElemArray ': xs) where
aesonOptions :: Options
aesonOptions = (forall {k} (xs :: k). AesonOptions xs => Options
aesonOptions @xs) { sumEncoding :: SumEncoding
sumEncoding = SumEncoding
TwoElemArray }