{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveLift #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE TemplateHaskell #-}
module Data.Morpheus.Types.Internal.AST.Value
( Value(..)
, ScalarValue(..)
, Object
, GQLValue(..)
, replaceValue
, decodeScientific
, convertToJSONName
, convertToHaskellName
, RawValue
, ValidValue
, RawObject
, ValidObject
, Variable(..)
, ResolvedValue
, ResolvedObject
, VariableContent(..)
)
where
import qualified Data.Aeson as A
( FromJSON(..)
, ToJSON(..)
, Value(..)
, object
, pairs
, (.=)
)
import qualified Data.HashMap.Strict as M
( toList )
import Data.Scientific ( Scientific
, floatingOrInteger
)
import Data.Semigroup ( (<>) )
import Data.Text ( Text
, unpack
)
import qualified Data.Text as T
import qualified Data.Vector as V
( toList )
import GHC.Generics ( Generic )
import Instances.TH.Lift ( )
import Language.Haskell.TH.Syntax ( Lift(..) )
import Data.Morpheus.Types.Internal.AST.Base
( Collection
, Ref(..)
, Name
, RAW
, VALID
, Position
, Stage
, RESOLVED
, TypeRef
)
isReserved :: Name -> Bool
isReserved "case" = True
isReserved "class" = True
isReserved "data" = True
isReserved "default" = True
isReserved "deriving" = True
isReserved "do" = True
isReserved "else" = True
isReserved "foreign" = True
isReserved "if" = True
isReserved "import" = True
isReserved "in" = True
isReserved "infix" = True
isReserved "infixl" = True
isReserved "infixr" = True
isReserved "instance" = True
isReserved "let" = True
isReserved "module" = True
isReserved "newtype" = True
isReserved "of" = True
isReserved "then" = True
isReserved "type" = True
isReserved "where" = True
isReserved "_" = True
isReserved _ = False
{-# INLINE isReserved #-}
convertToJSONName :: Text -> Text
convertToJSONName hsName
| not (T.null hsName) && isReserved name && (T.last hsName == '\'') = name
| otherwise = hsName
where name = T.init hsName
convertToHaskellName :: Text -> Text
convertToHaskellName name | isReserved name = name <> "'"
| otherwise = name
data ScalarValue
= Int Int
| Float Float
| String Text
| Boolean Bool
deriving (Show, Generic,Lift)
instance A.ToJSON ScalarValue where
toJSON (Float x) = A.toJSON x
toJSON (Int x) = A.toJSON x
toJSON (Boolean x) = A.toJSON x
toJSON (String x) = A.toJSON x
instance A.FromJSON ScalarValue where
parseJSON (A.Bool v) = pure $ Boolean v
parseJSON (A.Number v) = pure $ decodeScientific v
parseJSON (A.String v) = pure $ String v
parseJSON notScalar = fail $ "Expected Scalar got :" <> show notScalar
type family VAR (a:: Stage) :: Stage
type instance VAR RAW = RESOLVED
type instance VAR RESOLVED = RESOLVED
type instance VAR VALID = VALID
data VariableContent (stage:: Stage) where
DefaultValue ::Maybe ResolvedValue -> VariableContent RESOLVED
ValidVariableValue ::{ validVarContent::ValidValue }-> VariableContent VALID
instance Lift (VariableContent a) where
lift (DefaultValue x) = [| DefaultValue x |]
lift (ValidVariableValue x) = [| ValidVariableValue x |]
deriving instance Show (VariableContent a)
data Variable (stage :: Stage) = Variable
{ variableType :: TypeRef
, variablePosition :: Position
, variableValue :: VariableContent (VAR stage)
} deriving (Show,Lift)
data Value (valid :: Stage) where
ResolvedVariable::Ref -> Variable VALID -> Value RESOLVED
VariableValue ::Ref -> Value RAW
Object ::Object a -> Value a
List ::[Value a] -> Value a
Enum ::Name -> Value a
Scalar ::ScalarValue -> Value a
Null ::Value a
type Object a = Collection (Value a)
type ValidObject = Object VALID
type RawObject = Object RAW
type ResolvedObject = Object RESOLVED
type RawValue = Value RAW
type ValidValue = Value VALID
type ResolvedValue = Value RESOLVED
deriving instance Lift (Value a)
instance Show (Value a) where
show Null = "null"
show (Enum x) = "" <> unpack x
show (Scalar x) = show x
show (ResolvedVariable Ref { refName } Variable { variableValue }) =
"($" <> unpack refName <> ": " <> show variableValue <> ") "
show (VariableValue Ref { refName }) = "$" <> unpack refName <> " "
show (Object keys ) = "{" <> foldl toEntry "" keys <> "}"
where
toEntry :: String -> (Name, Value a) -> String
toEntry "" (key, value) = unpack key <> ":" <> show value
toEntry txt (key, value) = txt <> ", " <> unpack key <> ":" <> show value
show (List list) = "[" <> foldl toEntry "" list <> "]"
where
toEntry :: String -> Value a -> String
toEntry "" value = show value
toEntry txt value = txt <> ", " <> show value
instance A.ToJSON (Value a) where
toJSON (ResolvedVariable _ Variable { variableValue = ValidVariableValue x })
= A.toJSON x
toJSON (VariableValue Ref { refName }) =
A.String $ "($ref:" <> refName <> ")"
toJSON Null = A.Null
toJSON (Enum x ) = A.String x
toJSON (Scalar x ) = A.toJSON x
toJSON (List x ) = A.toJSON x
toJSON (Object fields) = A.object $ map toEntry fields
where toEntry (name, value) = name A..= A.toJSON value
toEncoding (ResolvedVariable _ Variable { variableValue = ValidVariableValue x })
= A.toEncoding x
toEncoding (VariableValue Ref { refName }) =
A.toEncoding $ "($ref:" <> refName <> ")"
toEncoding Null = A.toEncoding A.Null
toEncoding (Enum x ) = A.toEncoding x
toEncoding (Scalar x ) = A.toEncoding x
toEncoding (List x ) = A.toEncoding x
toEncoding (Object []) = A.toEncoding $ A.object []
toEncoding (Object x ) = A.pairs $ foldl1 (<>) $ map encodeField x
where encodeField (key, value) = convertToJSONName key A..= value
decodeScientific :: Scientific -> ScalarValue
decodeScientific v = case floatingOrInteger v of
Left float -> Float float
Right int -> Int int
replaceValue :: A.Value -> Value a
replaceValue (A.Bool v) = gqlBoolean v
replaceValue (A.Number v) = Scalar $ decodeScientific v
replaceValue (A.String v) = gqlString v
replaceValue (A.Object v) = gqlObject $ map replace (M.toList v)
where
replace (key, val) = (key, replaceValue val)
replaceValue (A.Array li) = gqlList (map replaceValue (V.toList li))
replaceValue A.Null = gqlNull
instance A.FromJSON (Value a) where
parseJSON = pure . replaceValue
class GQLValue a where
gqlNull :: a
gqlScalar :: ScalarValue -> a
gqlBoolean :: Bool -> a
gqlString :: Text -> a
gqlList :: [a] -> a
gqlObject :: [(Name, a)] -> a
instance GQLValue (Value a) where
gqlNull = Null
gqlScalar = Scalar
gqlBoolean = Scalar . Boolean
gqlString = Scalar . String
gqlList = List
gqlObject = Object