{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE UndecidableInstances #-}
module Data.Aeson.Deriving.Known where
import Data.Aeson
import qualified Data.HashMap.Strict as HashMap
import Data.Kind (Type)
import Data.Proxy
import Data.Text (pack)
import GHC.TypeLits (KnownSymbol, Symbol, symbolVal)
infix 3 :=
infix 6 ==>
data Null
data field := (value :: k)
data a ==> b
data WithDefault (val :: k)
class KnownJSON (a :: k) where jsonVal :: Proxy a -> Value
instance KnownSymbol str => KnownJSON (str :: Symbol) where jsonVal = String . pack . symbolVal
instance KnownBool b => KnownJSON (b :: Bool) where jsonVal = Bool . boolVal
instance KnownJSON Null where jsonVal Proxy = Null
instance KnownJSONList (xs :: [k]) => KnownJSON xs where jsonVal Proxy = toJSON $ listVal (Proxy @xs)
class KnownBool (b :: Bool) where boolVal :: Proxy b -> Bool
instance KnownBool 'True where boolVal Proxy = True
instance KnownBool 'False where boolVal Proxy = False
class KnownJSONList (xs :: [k]) where listVal :: Proxy xs -> [Value]
instance KnownJSONList '[] where listVal Proxy = []
instance (KnownJSON x, KnownJSONList xs) => KnownJSONList (x ': xs) where
listVal Proxy = jsonVal (Proxy @x) : listVal (Proxy @xs)
class KnownJSONObject (a :: k) where objectVal :: Proxy a -> Object
instance KnownJSONObject '[] where objectVal Proxy = mempty
instance (KnownJSONObject fields, KnownSymbol key, KnownJSON val)
=> KnownJSONObject ((key := val) ': fields) where
objectVal Proxy =
HashMap.insert
(pack . symbolVal $ Proxy @key)
(jsonVal $ Proxy @val)
(objectVal $ Proxy @fields)
class KnownJSONFunction (a :: Type) where functionVal :: Proxy a -> Value -> Value
instance (KnownJSON a, KnownJSON b) => KnownJSONFunction (a ==> b) where
functionVal Proxy x
| x == jsonVal (Proxy @a) = jsonVal (Proxy @b)
| otherwise = x
instance KnownJSON a => KnownJSONFunction (WithDefault a) where
functionVal Proxy = \case
Null -> jsonVal $ Proxy @a
x -> x