{-# LANGUAGE DataKinds #-} {-# LANGUAGE DefaultSignatures #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-} module Data.Aeson.Default.Class ( Default(..) ) where import Data.Aeson import Data.Functor.Identity import Data.Kind import Data.Maybe import GHC.Generics data Mismatch = Mismatch {-| In most cases, use the default implementation for 'Generic' instances. Since 'Default' instances have implemented 'FromJSON' (t 'Maybe'), all 'Default' instances will automatically implement 'FromJSON' (t 'Identity'). -} class FromJSON (t Maybe) => Default (t :: (Type -> Type) -> Type) where -- | Get default value by the data constructor name. constrDef :: String -> t Identity {-| Apply the given default value, if the data constructor does not match, call 'constrDef' to get the correct value and then apply it again, if it does not match either, raise an error. There is a default implementation for 'Generic' instances. -} applyDef :: t Identity -> t Maybe -> t Identity default applyDef :: ( Generic (t Identity) , Generic (t Maybe) , GDefault (Rep (t Identity)) (Rep (t Maybe)) , GConsName (Rep (t Identity)) , GConsName (Rep (t Maybe)) ) => t Identity -> t Maybe -> t Identity applyDef i m | Right r <- gapplyDef (from i) (from m) = to r applyDef _ m = retry (constrDef $ gconsName $ from m) m where retry :: ( Generic (t Identity) , Generic (t Maybe) , GDefault (Rep (t Identity)) (Rep (t Maybe)) , GConsName (Rep (t Identity)) , GConsName (Rep (t Maybe)) ) => t Identity -> t Maybe -> t Identity retry i m | Right r <- gapplyDef (from i) (from m) = to r retry i m = error $ "Data.Aeson.Default: The data constructor (" ++ (gconsName (from i)) ++ ") of the default value you provide (or constrDef returns) " ++ " does not match expected (" ++ (gconsName (from m)) ++ ")." applyDefs :: t Maybe -> t Identity -- | Call 'constrDef' to get the default value, then call 'applyDef' to apply -- it. default applyDefs :: ( Generic (t Maybe) , GConsName (Rep (t Maybe)) ) => t Maybe -> t Identity applyDefs m = applyDef (constrDef $ gconsName $ from m) m instance Default t => FromJSON (t Identity) where parseJSON = (fmap applyDefs) . parseJSON -------------------------------------------------------------------------------- class GDefault f g where gapplyDef :: f (t Identity) -> g (t Maybe) -> Either Mismatch (f (t Identity)) -- Data type instance GDefault f g => GDefault (D1 c f) (D1 c g) where gapplyDef (M1 p) (M1 k) = M1 <$> gapplyDef p k -- Choice between data constructors instance ( GDefault f g , GDefault f' g' ) => GDefault (f :+: f') (g :+: g') where gapplyDef (L1 p) (L1 k) = L1 <$> gapplyDef p k gapplyDef (R1 p) (R1 k) = R1 <$> gapplyDef p k gapplyDef _ _ = Left Mismatch -- Data constructor instance ( Constructor c , GDefault f g ) => GDefault (C1 c f) (C1 c g) where gapplyDef (M1 p) (M1 k) = M1 <$> gapplyDef p k -- Enum type (nullary data constructor) instance Constructor c => GDefault (C1 c U1) (C1 c U1) where gapplyDef (M1 p) (M1 k) = Right $ M1 p -- Apply record selectors instance ( GDefault f g , GDefault f' g' ) => GDefault (f :*: f') (g :*: g') where gapplyDef (p :*: p') (k :*: k') = do x <- gapplyDef p k y <- gapplyDef p' k' return $ x :*: y -- Selector instance (Selector c , GDefault f g) => GDefault (S1 c f) (S1 c g) where gapplyDef (M1 p) (M1 k) = M1 <$> gapplyDef p k -- Not nested required field instance GDefault (K1 i f) (K1 i f) where gapplyDef (K1 p) (K1 k) = Right $ K1 k -- Not nested optional field (use type family) instance GDefault (K1 i f) (K1 i (Maybe f)) where gapplyDef (K1 p) (K1 k) = Right $ K1 $ fromMaybe p k -- Not nested optional field (not use type family) instance GDefault (K1 i (Identity f)) (K1 i (Maybe f)) where gapplyDef (K1 p) (K1 Nothing) = Right $ K1 p gapplyDef (K1 p) (K1 (Just k)) = Right $ K1 $ Identity k -- Nested required field instance Default t => GDefault (K1 i (t Identity)) (K1 i (t Maybe)) where gapplyDef (K1 p) (K1 k) = Right $ K1 $ applyDef p k -- Nested optional field instance Default t => GDefault (K1 i (t Identity)) (K1 i (Maybe (t Maybe))) where gapplyDef (K1 p) (K1 Nothing) = Right $ K1 p gapplyDef (K1 p) (K1 (Just k)) = Right $ K1 $ applyDef p k class GConsName f where gconsName :: f p -> String instance GConsName f => GConsName (D1 c f) where gconsName (M1 x) = gconsName x instance (GConsName f, GConsName g)=> GConsName (f :+: g) where gconsName (L1 x) = gconsName x gconsName (R1 x) = gconsName x instance Constructor c => GConsName (C1 c f) where gconsName = conName