schemas-0.4.0.0: example/Person2.hs
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedLabels #-}
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE OverloadedStrings #-}
module Person2 where
import Control.Applicative
import Data.Generics.Labels ()
import Data.List.NonEmpty (NonEmpty)
import qualified Data.List.NonEmpty as NE
import Data.Maybe
import Data.String
import GHC.Exts (IsList(..))
import GHC.Generics
import qualified Generics.SOP as SOP
import Person
import Schemas
-- | The v2 of the Person schema adds a new optional field 'religion'
-- and renames 'studies' to 'education'
data Person2 = Person2
{ name :: String
, age :: Maybe Int
, addresses :: [String]
, religion :: (Maybe Religion) -- new
, education :: NonEmpty Education -- renamed
}
deriving (Generic, Eq, Show)
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
data Religion = Catholic | Anglican | Muslim | Hindu
deriving (Bounded, Enum, Eq, Show)
instance HasSchema Religion where
schema = enum (fromString . show) (fromList enumerate)
enumerate :: (Bounded a, Enum a) => [a]
enumerate = [minBound ..]
instance HasSchema Person2 where
schema =
record
$ Person2
<$> field "name" Person2.name
<*> ( optField "age" Person2.age
<|> fieldWith (dimap (fromMaybe (-1)) Just schema) "age" Person2.age
)
<*> field "addresses" Person2.addresses
<*> optField "religion" Person2.religion
<*> (field "education" Person2.education <|> (NE.:| []) <$> field
"studies"
(NE.head . Person2.education)
)
pepe2 :: Person2
pepe2 = Person2 "Pepe"
(Just 38)
["2 Edward Square", "La Mar 10"]
Nothing
[PhD "Computer Science", Degree "Engineering" ]
-- Person2 can be encoded in multiple ways, so the canonic encoding includes all ways
-- >>> import qualified Data.ByteString.Lazy.Char8 as B
-- >>> import Data.Aeson.Encode.Pretty
-- >>> import Data.Either
-- >>> B.putStrLn $ encodePretty $ fromRight undefined (encodeTo (schemaFor @Person2)) pepe2
-- {
-- "education": [
-- {
-- "PhD": "Computer Science"
-- },
-- {
-- "Degree": "Engineering"
-- }
-- ],
-- "addresses": [
-- "2 Edward Square",
-- "La Mar 10"
-- ],
-- "age": 38,
-- "name": "Pepe"
-- }
-- >>> import qualified Data.ByteString.Lazy.Char8 as B
-- >>> import Data.Aeson.Encode.Pretty
-- >>> B.putStrLn $ encodePretty $ encode pepe2
-- {
-- "education": [
-- {
-- "PhD": "Computer Science"
-- },
-- {
-- "Degree": "Engineering"
-- }
-- ],
-- "addresses": [
-- "2 Edward Square",
-- "La Mar 10"
-- ],
-- "age": 38,
-- "name": "Pepe"
-- }
-- Person2 is a subtype of Person therefore we can encode a Person2 as a Person
-- >>> import qualified Data.ByteString.Lazy.Char8 as B
-- >>> import Data.Aeson.Encode.Pretty
-- >>> B.putStrLn $ encodePretty $ fromRight undefined (encodeTo (schemaFor @Person)) pepe2
-- {
-- "addresses": [
-- "2 Edward Square",
-- "La Mar 10"
-- ],
-- "age": 38,
-- "studies": {
-- "PhD": "Computer Science"
-- },
-- "name": "Pepe"
-- }
-- We can also upgrade a Person into a Person2, because the new field is optional
-- >>> import Text.Pretty.Simple
-- >>> pPrintNoColor $ fromRight undefined (decodeFrom @Person2 (schemaFor @Person)) (encode pepe)
-- Right
-- ( Person2
-- { name = "Pepe"
-- , age = Just 38
-- , addresses =
-- [ "2 Edward Square"
-- , "La Mar 10"
-- ]
-- , religion = Nothing
-- , education = PhD { unPhD = "Computer Science" } :| []
-- }
-- )
-- >>> B.putStrLn $ encodePretty $ encode (schemaFor @Person2)
-- {
-- "Record": {
-- "education": {
-- "schema": {
-- "Array": {
-- "Union": [
-- {
-- "schema": {
-- "Prim": "String"
-- },
-- "constructor": "Degree"
-- },
-- {
-- "schema": {
-- "Prim": "String"
-- },
-- "constructor": "PhD"
-- },
-- {
-- "schema": {
-- "Record": {}
-- },
-- "constructor": "NoEducation"
-- }
-- ]
-- }
-- }
-- },
-- "religion": {
-- "schema": {
-- "Enum": [
-- "Catholic",
-- "Anglican",
-- "Muslim",
-- "Hindu"
-- ]
-- },
-- "isRequired": false
-- },
-- "addresses": {
-- "schema": {
-- "Array": {
-- "Prim": "String"
-- }
-- }
-- },
-- "age": {
-- "schema": {
-- "Prim": "Integer"
-- },
-- "isRequired": false
-- },
-- "name": {
-- "schema": {
-- "Prim": "String"
-- }
-- }
-- }
-- }
-- >>> import Data.Aeson.Encode.Pretty
-- >>> mapM_ (B.putStrLn . encodePretty . encode) (extractSchema $ schema @Person2)
-- {
-- "Record": {
-- "education": {
-- "schema": {
-- "Array": {
-- "Union": [
-- {
-- "schema": {
-- "Prim": "String"
-- },
-- "constructor": "Degree"
-- },
-- {
-- "schema": {
-- "Prim": "String"
-- },
-- "constructor": "PhD"
-- },
-- {
-- "schema": {
-- "Record": {}
-- },
-- "constructor": "NoEducation"
-- }
-- ]
-- }
-- }
-- },
-- "religion": {
-- "schema": {
-- "Enum": [
-- "Catholic",
-- "Anglican",
-- "Muslim",
-- "Hindu"
-- ]
-- },
-- "isRequired": false
-- },
-- "addresses": {
-- "schema": {
-- "Array": {
-- "Prim": "String"
-- }
-- }
-- },
-- "age": {
-- "schema": {
-- "Prim": "Integer"
-- },
-- "isRequired": false
-- },
-- "name": {
-- "schema": {
-- "Prim": "String"
-- }
-- }
-- }
-- }
-- {
-- "Record": {
-- "religion": {
-- "schema": {
-- "Enum": [
-- "Catholic",
-- "Anglican",
-- "Muslim",
-- "Hindu"
-- ]
-- },
-- "isRequired": false
-- },
-- "addresses": {
-- "schema": {
-- "Array": {
-- "Prim": "String"
-- }
-- }
-- },
-- "age": {
-- "schema": {
-- "Prim": "Integer"
-- },
-- "isRequired": false
-- },
-- "studies": {
-- "schema": {
-- "Union": [
-- {
-- "schema": {
-- "Prim": "String"
-- },
-- "constructor": "Degree"
-- },
-- {
-- "schema": {
-- "Prim": "String"
-- },
-- "constructor": "PhD"
-- },
-- {
-- "schema": {
-- "Record": {}
-- },
-- "constructor": "NoEducation"
-- }
-- ]
-- }
-- },
-- "name": {
-- "schema": {
-- "Prim": "String"
-- }
-- }
-- }
-- }
-- {
-- "Record": {
-- "education": {
-- "schema": {
-- "Array": {
-- "Union": [
-- {
-- "schema": {
-- "Prim": "String"
-- },
-- "constructor": "Degree"
-- },
-- {
-- "schema": {
-- "Prim": "String"
-- },
-- "constructor": "PhD"
-- },
-- {
-- "schema": {
-- "Record": {}
-- },
-- "constructor": "NoEducation"
-- }
-- ]
-- }
-- }
-- },
-- "religion": {
-- "schema": {
-- "Enum": [
-- "Catholic",
-- "Anglican",
-- "Muslim",
-- "Hindu"
-- ]
-- },
-- "isRequired": false
-- },
-- "addresses": {
-- "schema": {
-- "Array": {
-- "Prim": "String"
-- }
-- }
-- },
-- "age": {
-- "schema": {
-- "Prim": "Integer"
-- }
-- },
-- "name": {
-- "schema": {
-- "Prim": "String"
-- }
-- }
-- }
-- }
-- {
-- "Record": {
-- "religion": {
-- "schema": {
-- "Enum": [
-- "Catholic",
-- "Anglican",
-- "Muslim",
-- "Hindu"
-- ]
-- },
-- "isRequired": false
-- },
-- "addresses": {
-- "schema": {
-- "Array": {
-- "Prim": "String"
-- }
-- }
-- },
-- "age": {
-- "schema": {
-- "Prim": "Integer"
-- }
-- },
-- "studies": {
-- "schema": {
-- "Union": [
-- {
-- "schema": {
-- "Prim": "String"
-- },
-- "constructor": "Degree"
-- },
-- {
-- "schema": {
-- "Prim": "String"
-- },
-- "constructor": "PhD"
-- },
-- {
-- "schema": {
-- "Record": {}
-- },
-- "constructor": "NoEducation"
-- }
-- ]
-- }
-- },
-- "name": {
-- "schema": {
-- "Prim": "String"
-- }
-- }
-- }
-- }