{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE OverloadedStrings #-}
module CodeGen.Deserialize
( MkDatatype (..)
, MkField (..)
, MkRequired (..)
, MkType (..)
, MkDatatypeName (..)
, MkNamed (..)
, MkMultiple (..)
) where
import Data.Aeson as Aeson
import Data.Aeson.Types
import Data.Char
import GHC.Generics hiding (Constructor, Datatype)
import Data.Text (Text, unpack)
import Data.List.NonEmpty (NonEmpty (..))
import qualified Data.HashMap.Strict as HM
data MkDatatype
= SumType
{ datatypeName :: MkDatatypeName
, isName :: MkNamed
, datatypeSubtypes :: [MkType]
}
| ProductType
{ datatypeName :: MkDatatypeName
, isName :: MkNamed
, datatypeFields :: NonEmpty (String, MkField)
}
| LeafType
{ datatypeName :: MkDatatypeName
, isName :: MkNamed
}
deriving (Eq, Ord, Show, Generic, ToJSON)
instance FromJSON MkDatatype where
parseJSON = withObject "MkDatatype" $ \v -> do
type' <- v .: "type"
named <- v .: "named"
subtypes <- v .:? "subtypes"
case subtypes of
Nothing -> do
fields <- v .:? "fields"
case fmap HM.toList fields of
Just (field:fields) -> ProductType type' named <$> parseKVPairs (field :| fields)
Just [] -> pure (LeafType type' named)
_ -> pure (LeafType type' named)
Just subtypes -> pure (SumType type' named subtypes)
parseKVPairs :: NonEmpty (Text, Value) -> Parser (NonEmpty (String, MkField))
parseKVPairs = traverse go
where go :: (Text, Value) -> Parser (String, MkField)
go (t,v) = do
v' <- parseJSON v
pure (unpack t, v')
data MkField = MkField
{ fieldRequired :: MkRequired
, fieldTypes :: [MkType]
, fieldMultiple :: MkMultiple
}
deriving (Eq, Ord, Show, Generic, ToJSON)
instance FromJSON MkField where
parseJSON = genericParseJSON customOptions
data MkRequired = Optional | Required
deriving (Eq, Ord, Show, Generic, ToJSON)
instance FromJSON MkRequired where
parseJSON = withBool "Required" (\p -> pure (if p then Required else Optional))
data MkType = MkType
{ fieldType :: MkDatatypeName
, isNamed :: MkNamed
}
deriving (Eq, Ord, Show, Generic, ToJSON)
instance FromJSON MkType where
parseJSON = genericParseJSON customOptions
newtype MkDatatypeName = DatatypeName String
deriving (Eq, Ord, Show, Generic, ToJSON)
instance FromJSON MkDatatypeName where
parseJSON = genericParseJSON customOptions
data MkNamed = Anonymous | Named
deriving (Eq, Ord, Show, Generic, ToJSON)
instance FromJSON MkNamed where
parseJSON = withBool "Named" (\p -> pure (if p then Named else Anonymous))
data MkMultiple = Single | Multiple
deriving (Eq, Ord, Show, Generic, ToJSON)
instance FromJSON MkMultiple where
parseJSON = withBool "Multiple" (\p -> pure (if p then Multiple else Single))
customOptions :: Aeson.Options
customOptions = Aeson.defaultOptions
{
fieldLabelModifier = initLower . dropPrefix
, constructorTagModifier = initLower
}
dropPrefix :: String -> String
dropPrefix = Prelude.dropWhile isLower
initLower :: String -> String
initLower (c:cs) = toLower c : cs
initLower "" = ""