{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
module Data.Morpheus.Execution.Internal.Decode
( withObject
, withMaybe
, withList
, withEnum
, withUnion
, decodeFieldWith
, decodeObjectExpQ
) where
import Data.Semigroup ((<>))
import Data.Text (unpack)
import Language.Haskell.TH (ExpQ, conE, mkName, uInfixE, varE)
import Data.Morpheus.Error.Internal (internalArgumentError, internalTypeMismatch)
import Data.Morpheus.Types.Internal.Data (DataField (..), Key)
import Data.Morpheus.Types.Internal.DataD (ConsD (..))
import Data.Morpheus.Types.Internal.Validation (Validation)
import Data.Morpheus.Types.Internal.Value (Object, Value (..))
decodeObjectExpQ :: ExpQ -> ConsD -> ExpQ
decodeObjectExpQ fieldDecoder ConsD {cName, cFields} = handleFields cFields
where
consName = conE (mkName cName)
handleFields fNames = uInfixE consName (varE '(<$>)) (applyFields fNames)
where
applyFields [] = fail "No Empty fields"
applyFields [x] = defField x
applyFields (x:xs) = uInfixE (defField x) (varE '(<*>)) (applyFields xs)
defField DataField {fieldName} = uInfixE (varE (mkName "o")) fieldDecoder [|fName|]
where
fName = unpack fieldName
withObject :: (Object -> Validation a) -> Value -> Validation a
withObject f (Object object) = f object
withObject _ isType = internalTypeMismatch "Object" isType
withMaybe :: Monad m => (Value -> m a) -> Value -> m (Maybe a)
withMaybe _ Null = pure Nothing
withMaybe decode x = Just <$> decode x
withList :: (Value -> Validation a) -> Value -> Validation [a]
withList decode (List li) = mapM decode li
withList _ isType = internalTypeMismatch "List" isType
withEnum :: (Key -> Validation a) -> Value -> Validation a
withEnum decode (Enum value) = decode value
withEnum _ isType = internalTypeMismatch "Enum" isType
withUnion :: (Key -> Object -> Object -> Validation a) -> Object -> Validation a
withUnion decoder unions =
case lookup "tag" unions of
Just (Enum key) ->
case lookup key unions of
Nothing -> internalArgumentError ("type \"" <> key <> "\" was not provided on object")
Just value -> withObject (decoder key unions) value
Just _ -> internalArgumentError "tag must be Enum"
Nothing -> internalArgumentError "tag not found on Input Union"
decodeFieldWith :: (Value -> Validation a) -> Key -> Object -> Validation a
decodeFieldWith decoder name object =
case lookup name object of
Nothing -> internalArgumentError ("Missing Field: \"" <> name <> "\"")
Just value -> decoder value