morpheus-graphql-code-gen 0.21.0 → 0.22.0
raw patch · 17 files changed
+1064/−1455 lines, 17 filesdep +file-embeddep +morpheus-graphql-code-gen-utilsdep +morpheus-graphql-serverdep ~morpheus-graphql-core
Dependencies added: file-embed, morpheus-graphql-code-gen-utils, morpheus-graphql-server
Dependency ranges changed: morpheus-graphql-core
Files
- app/Main.hs +14/−15
- changelog.md +0/−2
- morpheus-graphql-code-gen.cabal +14/−12
- src/Data/Morpheus/CodeGen.hs +6/−6
- src/Data/Morpheus/CodeGen/Internal/AST.hs +0/−139
- src/Data/Morpheus/CodeGen/Internal/Name.hs +0/−90
- src/Data/Morpheus/CodeGen/Internal/TH.hs +0/−191
- src/Data/Morpheus/CodeGen/Interpreting/Transform.hs +0/−608
- src/Data/Morpheus/CodeGen/Printing/GQLType.hs +0/−70
- src/Data/Morpheus/CodeGen/Printing/Render.hs +0/−87
- src/Data/Morpheus/CodeGen/Printing/Terms.hs +0/−78
- src/Data/Morpheus/CodeGen/Printing/Type.hs +0/−150
- src/Data/Morpheus/CodeGen/Server.hs +30/−7
- src/Data/Morpheus/CodeGen/Server/Internal/AST.hs +127/−0
- src/Data/Morpheus/CodeGen/Server/Interpreting/Transform.hs +613/−0
- src/Data/Morpheus/CodeGen/Server/Printing/Document.hs +142/−0
- src/Data/Morpheus/CodeGen/Server/Printing/TH.hs +118/−0
app/Main.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE NoImplicitPrelude #-} module Main@@ -64,25 +65,21 @@ main = defaultParser >>= runApp runApp :: App -> IO ()-runApp- App- { operations,- options = Options {version, root}- }- | version = putStrLn currentVersion- | otherwise = runOperation operations- where- runOperation About =- putStrLn $ "Morpheus GraphQL CLI, version " <> currentVersion- runOperation Build {source} = traverse_ (processFile root) source+runApp App {..}+ | version options = putStrLn currentVersion+ | otherwise = runOperation operations+ where+ runOperation About =+ putStrLn $ "Morpheus GraphQL CLI, version " <> currentVersion+ runOperation Build {source} = traverse_ (processFile options) source -processFile :: FilePath -> FilePath -> IO ()-processFile root path =+processFile :: Options -> FilePath -> IO ()+processFile Options {root, namespaces} path = print (path, hsPath) >> L.readFile path >>= saveDocument hsPath . fmap (printServerTypeDefinitions PrinterConfig {moduleName})- . parseServerTypeDefinitions CodeGenConfig {namespace = False}+ . parseServerTypeDefinitions CodeGenConfig {namespace = namespaces} where hsPath = processFileName path moduleName = intercalate "." $ splitDirectories $ dropExtensions $ makeRelative root hsPath@@ -111,7 +108,8 @@ data Options = Options { version :: Bool,- root :: String+ root :: String,+ namespaces :: Bool } deriving (Show) @@ -129,6 +127,7 @@ Options <$> switch (long "version" <> short 'v' <> help "show Version number") <*> option readOutput (long "root" <> short 'r' <> help "Root directory of the Haskell project")+ <*> switch (long "namespaces" <> short 'n' <> help "namespaces type fields to avoid name conflicts") readOutput :: ReadM String readOutput = eitherReader Right
changelog.md view
@@ -1,5 +1,3 @@ # Changelog see latest changes on [Github](https://github.com/morpheusgraphql/morpheus-graphql/releases)--## 0.18.0 - 08.11.2021
morpheus-graphql-code-gen.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack name: morpheus-graphql-code-gen-version: 0.21.0+version: 0.22.0 synopsis: Morpheus GraphQL CLI description: code generator for Morpheus GraphQL category: web, graphql, cli@@ -28,16 +28,12 @@ library exposed-modules: Data.Morpheus.CodeGen- Data.Morpheus.CodeGen.Internal.AST- Data.Morpheus.CodeGen.Internal.TH- other-modules:- Data.Morpheus.CodeGen.Internal.Name- Data.Morpheus.CodeGen.Interpreting.Transform- Data.Morpheus.CodeGen.Printing.GQLType- Data.Morpheus.CodeGen.Printing.Render- Data.Morpheus.CodeGen.Printing.Terms- Data.Morpheus.CodeGen.Printing.Type Data.Morpheus.CodeGen.Server+ other-modules:+ Data.Morpheus.CodeGen.Server.Internal.AST+ Data.Morpheus.CodeGen.Server.Interpreting.Transform+ Data.Morpheus.CodeGen.Server.Printing.Document+ Data.Morpheus.CodeGen.Server.Printing.TH Paths_morpheus_graphql_code_gen hs-source-dirs: src@@ -46,7 +42,10 @@ base >=4.7.0 && <5.0.0 , bytestring >=0.10.4 && <0.12.0 , containers >=0.4.2.1 && <0.7.0- , morpheus-graphql-core >=0.21.0 && <0.22.0+ , file-embed >=0.0.10 && <1.0.0+ , morpheus-graphql-code-gen-utils >=0.22.0 && <0.23.0+ , morpheus-graphql-core >=0.22.0 && <0.23.0+ , morpheus-graphql-server >=0.22.0 && <0.23.0 , prettyprinter >=1.7.0 && <2.0.0 , relude >=0.3.0 && <2.0.0 , template-haskell >=2.0.0 && <3.0.0@@ -65,9 +64,12 @@ base >=4.7.0 && <5.0.0 , bytestring >=0.10.4 && <0.12.0 , containers >=0.4.2.1 && <0.7.0+ , file-embed >=0.0.10 && <1.0.0 , filepath >=1.1.0 && <1.5.0 , morpheus-graphql-code-gen- , morpheus-graphql-core >=0.21.0 && <0.22.0+ , morpheus-graphql-code-gen-utils >=0.22.0 && <0.23.0+ , morpheus-graphql-core >=0.22.0 && <0.23.0+ , morpheus-graphql-server >=0.22.0 && <0.23.0 , optparse-applicative >=0.12.0 && <0.18.0 , prettyprinter >=1.7.0 && <2.0.0 , relude >=0.3.0 && <2.0.0
src/Data/Morpheus/CodeGen.hs view
@@ -8,13 +8,13 @@ ) where -import Data.Morpheus.CodeGen.Internal.AST- ( CodeGenConfig (..),- )-import Data.Morpheus.CodeGen.Interpreting.Transform- ( parseServerTypeDefinitions,- ) import Data.Morpheus.CodeGen.Server ( PrinterConfig (..), printServerTypeDefinitions,+ )+import Data.Morpheus.CodeGen.Server.Internal.AST+ ( CodeGenConfig (..),+ )+import Data.Morpheus.CodeGen.Server.Interpreting.Transform+ ( parseServerTypeDefinitions, )
− src/Data/Morpheus/CodeGen/Internal/AST.hs
@@ -1,139 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE NoImplicitPrelude #-}--module Data.Morpheus.CodeGen.Internal.AST- ( ModuleDefinition (..),- CodeGenConfig (..),- ServerTypeDefinition (..),- GQLTypeDefinition (..),- CONST,- TypeKind (..),- TypeName,- TypeRef (..),- TypeWrapper (..),- unpackName,- DerivingClass (..),- FIELD_TYPE_WRAPPER (..),- ServerConstructorDefinition (..),- ServerFieldDefinition (..),- Kind (..),- ServerDirectiveUsage (..),- TypeValue (..),- )-where--import Data.Morpheus.Types.Internal.AST- ( CONST,- Description,- DirectiveLocation (..),- FieldName,- TypeKind (..),- TypeName,- TypeRef (..),- TypeWrapper (..),- Value,- unpackName,- )-import Prettyprinter (Doc, Pretty (..), punctuate, vsep, (<+>))-import Relude--data ModuleDefinition = ModuleDefinition- { moduleName :: Text,- imports :: [(Text, [Text])],- extensions :: [Text],- types :: [ServerTypeDefinition]- }--data FIELD_TYPE_WRAPPER- = MONAD- | SUBSCRIPTION- | PARAMETRIZED- | ARG TypeName- | TAGGED_ARG FieldName TypeRef- | GQL_WRAPPER TypeWrapper- deriving (Show)--data DerivingClass- = SHOW- | GENERIC- deriving (Show)--data ServerFieldDefinition = ServerFieldDefinition- { fieldType :: Text,- fieldName :: FieldName,- wrappers :: [FIELD_TYPE_WRAPPER]- }- deriving (Show)--data Kind = Scalar | Type deriving (Show)--data TypeValue- = TypeValueObject TypeName [(FieldName, TypeValue)]- | TypeValueNumber Double- | TypeValueString Text- | TypeValueBool Bool- | TypeValueList [TypeValue]- | TypedValueMaybe (Maybe TypeValue)- deriving (Show)--renderField :: (FieldName, TypeValue) -> Doc n-renderField (fName, fValue) = pretty (unpackName fName :: Text) <> "=" <+> pretty fValue--instance Pretty TypeValue where- pretty (TypeValueObject name xs) =- pretty (unpackName name :: Text)- <+> "{"- <+> vsep (punctuate "," (map renderField xs))- <+> "}"- pretty (TypeValueNumber x) = pretty x- pretty (TypeValueString x) = pretty (show x :: String)- pretty (TypeValueBool x) = pretty x- pretty (TypedValueMaybe (Just x)) = "Just" <+> pretty x- pretty (TypedValueMaybe Nothing) = "Nothing"- pretty (TypeValueList xs) = prettyList xs--data ServerDirectiveUsage- = TypeDirectiveUsage TypeValue- | FieldDirectiveUsage FieldName TypeValue- | EnumDirectiveUsage TypeName TypeValue- deriving (Show)--data GQLTypeDefinition = GQLTypeDefinition- { gqlKind :: Kind,- gqlTypeDescription :: Maybe Text,- gqlTypeDescriptions :: Map Text Description,- gqlTypeDirectiveUses :: [ServerDirectiveUsage],- gqlTypeDefaultValues :: Map Text (Value CONST)- }- deriving (Show)--data ServerConstructorDefinition = ServerConstructorDefinition- { constructorName :: TypeName,- constructorFields :: [ServerFieldDefinition]- }- deriving (Show)--data ServerTypeDefinition- = ServerTypeDefinition- { tName :: Text,- typeParameters :: [Text],- tCons :: [ServerConstructorDefinition],- tKind :: TypeKind,- derives :: [DerivingClass],- typeGQLType :: Maybe GQLTypeDefinition- }- | DirectiveTypeDefinition- { directiveConstructor :: ServerConstructorDefinition,- directiveDerives :: [DerivingClass],- directiveLocations :: [DirectiveLocation],- directiveGQLType :: GQLTypeDefinition- }- | ServerInterfaceDefinition- TypeName- TypeName- TypeName- deriving (Show)--newtype CodeGenConfig = CodeGenConfig- { namespace :: Bool- }
− src/Data/Morpheus/CodeGen/Internal/Name.hs
@@ -1,90 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--module Data.Morpheus.CodeGen.Internal.Name- ( toHaskellTypeName,- camelCaseTypeName,- toHaskellName,- camelCaseFieldName,- )-where--import Data.Char- ( toLower,- toUpper,- )-import Data.Morpheus.Types.Internal.AST- ( FieldName,- TypeName,- packName,- unpackName,- )-import qualified Data.Morpheus.Types.Internal.AST as N-import qualified Data.Text as T-import Relude hiding- ( ToString (..),- Type,- )--mapFstChar :: (Char -> Char) -> Text -> Text-mapFstChar f x- | T.null x = x- | otherwise = T.singleton (f $ T.head x) <> T.tail x--capitalize :: Text -> Text-capitalize = mapFstChar toUpper--camelCaseTypeName :: [N.Name t] -> TypeName -> TypeName-camelCaseTypeName list name =- packName $- T.concat $- map (capitalize . unpackName) (list <> [coerce name])--toHaskellTypeName :: TypeName -> Text-toHaskellTypeName "String" = "Text"-toHaskellTypeName "Boolean" = "Bool"-toHaskellTypeName "Float" = "Double"-toHaskellTypeName name = capitalize $ unpackName name-{-# INLINE toHaskellTypeName #-}--uncapitalize :: Text -> Text-uncapitalize = mapFstChar toLower--camelCaseFieldName :: TypeName -> FieldName -> FieldName-camelCaseFieldName nSpace name =- packName $- uncapitalize (unpackName nSpace)- <> capitalize (unpackName name)--toHaskellName :: FieldName -> String-toHaskellName name- | isReserved name = T.unpack (unpackName name <> "'")- | otherwise = T.unpack (uncapitalize (unpackName name))-{-# INLINE toHaskellName #-}---- handle reserved Names-isReserved :: FieldName -> 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 #-}
− src/Data/Morpheus/CodeGen/Internal/TH.hs
@@ -1,191 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE QuasiQuotes #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TemplateHaskellQuotes #-}-{-# LANGUAGE NoImplicitPrelude #-}--module Data.Morpheus.CodeGen.Internal.TH- ( _',- apply,- applyCons,- applyVars,- declareTypeRef,- funDSimple,- camelCaseFieldName,- camelCaseTypeName,- toCon,- toVar,- ToName (..),- toString,- typeInstanceDec,- v',- vars,- wrappedType,- )-where--import Data.Morpheus.CodeGen.Internal.Name- ( camelCaseFieldName,- camelCaseTypeName,- toHaskellName,- toHaskellTypeName,- )-import Data.Morpheus.Types.Internal.AST- ( FieldName,- TypeName,- TypeRef (..),- TypeWrapper (..),- unpackName,- )-import qualified Data.Text as T-import Language.Haskell.TH-import Relude hiding- ( ToString (..),- Type,- )--_' :: PatQ-_' = toVar (mkName "_")--v' :: ToVar Name a => a-v' = toVar (mkName "v")--wrappedType :: TypeWrapper -> Type -> Type-wrappedType (TypeList xs nonNull) = withNonNull nonNull . withList . wrappedType xs-wrappedType (BaseType nonNull) = withNonNull nonNull-{-# INLINE wrappedType #-}--declareTypeRef :: (TypeName -> Type) -> TypeRef -> Type-declareTypeRef f TypeRef {typeConName, typeWrappers} =- wrappedType typeWrappers (f typeConName)-{-# INLINE declareTypeRef #-}--withList :: Type -> Type-withList = AppT (ConT ''[])--withNonNull :: Bool -> Type -> Type-withNonNull True = id-withNonNull False = AppT (ConT ''Maybe)-{-# INLINE withNonNull #-}--cons :: ToCon a b => [a] -> [b]-cons = map toCon--vars :: ToVar a b => [a] -> [b]-vars = map toVar--class ToName a where- toName :: a -> Name--instance ToName String where- toName = mkName--instance ToName Name where- toName = id--instance ToName Text where- toName = toName . T.unpack--instance ToName TypeName where- toName = toName . toHaskellTypeName--instance ToName FieldName where- toName = mkName . toHaskellName--class ToString a b where- toString :: a -> b--instance ToString a b => ToString a (Q b) where- toString = pure . toString--instance ToString TypeName Lit where- toString = stringL . T.unpack . unpackName--instance ToString TypeName Pat where- toString = LitP . toString--instance ToString FieldName Lit where- toString = stringL . T.unpack . unpackName--instance ToString TypeName Exp where- toString = LitE . toString--instance ToString FieldName Exp where- toString = LitE . toString--class ToCon a b where- toCon :: a -> b--instance ToCon a b => ToCon a (Q b) where- toCon = pure . toCon--instance (ToName a) => ToCon a Type where- toCon = ConT . toName--instance (ToName a) => ToCon a Exp where- toCon = ConE . toName--class ToVar a b where- toVar :: a -> b--instance ToVar a b => ToVar a (Q b) where- toVar = pure . toVar--instance (ToName a) => ToVar a Type where- toVar = VarT . toName--instance (ToName a) => ToVar a Exp where- toVar = VarE . toName--instance (ToName a) => ToVar a Pat where- toVar = VarP . toName--class Apply a where- apply :: ToCon i a => i -> [a] -> a--instance Apply TypeQ where- apply = foldl' appT . toCon--instance Apply Type where- apply = foldl' AppT . toCon--instance Apply Exp where- apply = foldl' AppE . toCon--instance Apply ExpQ where- apply = foldl' appE . toCon--applyVars ::- ( ToName con,- ToName var,- Apply res,- ToCon con res,- ToVar var res- ) =>- con ->- [var] ->- res-applyVars name li = apply name (vars li)--applyCons :: (ToName con, ToName cons) => con -> [cons] -> Q Type-applyCons name li = apply name (cons li)--funDSimple :: Name -> [PatQ] -> ExpQ -> DecQ-funDSimple name args body = funD name [clause args (normalB body) []]--#if MIN_VERSION_template_haskell(2,15,0)--- fix breaking changes-typeInstanceDec :: Name -> Type -> Type -> Dec-typeInstanceDec typeFamily arg res = TySynInstD (TySynEqn Nothing (AppT (ConT typeFamily) arg) res)-#else----typeInstanceDec :: Name -> Type -> Type -> Dec-typeInstanceDec typeFamily arg res = TySynInstD typeFamily (TySynEqn [arg] res)-#endif
− src/Data/Morpheus/CodeGen/Interpreting/Transform.hs
@@ -1,608 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TupleSections #-}-{-# LANGUAGE UndecidableInstances #-}-{-# LANGUAGE NoImplicitPrelude #-}--module Data.Morpheus.CodeGen.Interpreting.Transform- ( parseServerTypeDefinitions,- )-where--import Data.ByteString.Lazy.Char8 (ByteString)-import Data.Morpheus.CodeGen.Internal.AST- ( CodeGenConfig (..),- DerivingClass (..),- FIELD_TYPE_WRAPPER (..),- GQLTypeDefinition (..),- Kind (..),- ServerConstructorDefinition (..),- ServerDirectiveUsage (..),- ServerFieldDefinition (..),- ServerTypeDefinition (..),- TypeValue (..),- )-import Data.Morpheus.CodeGen.Internal.Name- ( camelCaseFieldName,- toHaskellTypeName,- )-import Data.Morpheus.CodeGen.Internal.TH- ( ToName (toName),- camelCaseTypeName,- )-import Data.Morpheus.Core (internalSchema, parseDefinitions, render)-import Data.Morpheus.Error (gqlWarnings, renderGQLErrors)-import Data.Morpheus.Internal.Ext (GQLResult, Result (..))-import Data.Morpheus.Internal.Utils (IsMap, selectOr)-import Data.Morpheus.Types.Internal.AST- ( ANY,- Argument (..),- ArgumentDefinition (..),- CONST,- DataEnumValue (..),- Description,- Directive (Directive, directiveArgs, directiveName),- DirectiveDefinition (..),- FieldContent (..),- FieldDefinition (..),- FieldName,- FieldsDefinition,- GQLError,- IN,- OUT,- ObjectEntry (..),- OperationType (Subscription),- RawTypeDefinition (..),- TRUE,- Token,- TypeContent (..),- TypeDefinition (..),- TypeKind (..),- TypeName,- TypeRef (..),- UnionMember (..),- isPossibleInterfaceType,- isResolverType,- kindOf,- lookupWith,- unpackName,- )-import qualified Data.Morpheus.Types.Internal.AST as AST-import qualified Data.Morpheus.Types.Internal.AST as V-import Language.Haskell.TH- ( Dec (..),- Info (..),- Q,- TyVarBndr,- reify,- )-import Relude hiding (ByteString, get)--type ServerQ m = ReaderT (TypeContext CONST) m--class (Monad m, MonadFail m) => CodeGenMonad m where- isParametrizedType :: TypeName -> m Bool- printWarnings :: [GQLError] -> m ()--isParametrizedHaskellType :: Info -> Bool-isParametrizedHaskellType (TyConI x) = not $ null $ getTypeVariables x-isParametrizedHaskellType _ = False--#if MIN_VERSION_template_haskell(2,17,0)-getTypeVariables :: Dec -> [TyVarBndr ()]-#else-getTypeVariables :: Dec -> [TyVarBndr]-#endif-getTypeVariables (DataD _ _ args _ _ _) = args-getTypeVariables (NewtypeD _ _ args _ _ _) = args-getTypeVariables (TySynD _ args _) = args-getTypeVariables _ = []--instance CodeGenMonad Q where- isParametrizedType name = isParametrizedHaskellType <$> reify (toName name)- printWarnings = gqlWarnings--instance CodeGenMonad GQLResult where- isParametrizedType _ = pure False- printWarnings _ = pure ()--data TypeContext s = TypeContext- { toArgsTypeName :: FieldName -> TypeName,- typeDefinitions :: [TypeDefinition ANY s],- directiveDefinitions :: [DirectiveDefinition s],- currentTypeName :: Maybe TypeName,- currentKind :: Maybe TypeKind,- hasNamespace :: Bool- }--parseServerTypeDefinitions :: CodeGenMonad m => CodeGenConfig -> ByteString -> m [ServerTypeDefinition]-parseServerTypeDefinitions ctx txt =- case parseDefinitions txt of- Failure errors -> fail (renderGQLErrors errors)- Success {result, warnings} -> printWarnings warnings >> toTHDefinitions (namespace ctx) result--toTHDefinitions ::- CodeGenMonad m =>- Bool ->- [RawTypeDefinition] ->- m [ServerTypeDefinition]-toTHDefinitions namespace defs = concat <$> traverse generateTypes defs- where- typeDefinitions = [td | RawTypeDefinition td <- defs]- directiveDefinitions = [td | RawDirectiveDefinition td <- defs]- generateTypes :: CodeGenMonad m => RawTypeDefinition -> m [ServerTypeDefinition]- generateTypes (RawTypeDefinition typeDef) =- runReaderT- (genTypeDefinition typeDef)- TypeContext- { toArgsTypeName = mkArgsTypeName namespace (typeName typeDef),- typeDefinitions,- directiveDefinitions,- currentTypeName = Just (typeName typeDef),- currentKind = Just (kindOf typeDef),- hasNamespace = namespace- }- generateTypes (RawDirectiveDefinition DirectiveDefinition {..}) =- runReaderT- ( do- fields <- traverse renderDataField (argument <$> toList directiveDefinitionArgs)- pure- [ DirectiveTypeDefinition- { directiveConstructor = ServerConstructorDefinition (coerce directiveDefinitionName) fields,- directiveDerives = [SHOW, GENERIC],- directiveLocations = directiveDefinitionLocations,- directiveGQLType =- GQLTypeDefinition- { gqlKind = Type,- gqlTypeDescription = Nothing,- gqlTypeDescriptions = mempty,- gqlTypeDefaultValues = mempty,- gqlTypeDirectiveUses = []- }- }- ]- )- TypeContext- { toArgsTypeName = coerce,- typeDefinitions,- currentTypeName = Just (coerce directiveDefinitionName),- directiveDefinitions,- currentKind = Nothing,- hasNamespace = namespace- }- generateTypes _ = pure []--inType :: MonadReader (TypeContext s) m => Maybe TypeName -> m a -> m a-inType name = local (\x -> x {currentTypeName = name, currentKind = Nothing})--mkInterfaceName :: TypeName -> TypeName-mkInterfaceName = ("Interface" <>)--mkPossibleTypesName :: TypeName -> TypeName-mkPossibleTypesName = ("PossibleTypes" <>)--genTypeDefinition ::- CodeGenMonad m =>- TypeDefinition ANY CONST ->- ServerQ m [ServerTypeDefinition]-genTypeDefinition- typeDef@TypeDefinition- { typeName = originalTypeName,- typeContent,- typeDescription- } = genTypeContent originalTypeName typeContent >>= withType- where- typeName = case typeContent of- DataInterface {} -> mkInterfaceName originalTypeName- _ -> originalTypeName- tKind = kindOf typeDef- tName = toHaskellTypeName typeName- deriveGQL = do- gqlTypeDescriptions <- getDesc typeDef- gqlTypeDirectiveUses <- getDirs typeDef- pure $- Just- GQLTypeDefinition- { gqlTypeDescription = typeDescription,- gqlTypeDescriptions,- gqlTypeDirectiveUses,- gqlKind = derivingKind tKind,- gqlTypeDefaultValues =- fromList $- mapMaybe getDefaultValue $- getInputFields typeDef- }- typeParameters- | isResolverType tKind = ["m"]- | otherwise = []- derives = derivesClasses (isResolverType tKind)- -------------------------- withType (ConsIN tCons) = do- typeGQLType <- deriveGQL- pure [ServerTypeDefinition {..}]- withType (ConsOUT others tCons) = do- typeGQLType <- deriveGQL- pure (ServerTypeDefinition {..} : others)--derivingKind :: TypeKind -> Kind-derivingKind KindScalar = Scalar-derivingKind _ = Type--derivesClasses :: Bool -> [DerivingClass]-derivesClasses isResolver = GENERIC : [SHOW | not isResolver]--mkObjectCons :: TypeName -> [ServerFieldDefinition] -> [ServerConstructorDefinition]-mkObjectCons name = pure . ServerConstructorDefinition name--mkArgsTypeName :: Bool -> TypeName -> FieldName -> TypeName-mkArgsTypeName namespace typeName fieldName- | namespace = typeName <> argTName- | otherwise = argTName- where- argTName = camelCaseTypeName [fieldName] "Args"--isParametrizedResolverType :: CodeGenMonad m => TypeName -> [TypeDefinition ANY s] -> m Bool-isParametrizedResolverType "__TypeKind" _ = pure False-isParametrizedResolverType "Boolean" _ = pure False-isParametrizedResolverType "String" _ = pure False-isParametrizedResolverType "Int" _ = pure False-isParametrizedResolverType "Float" _ = pure False-isParametrizedResolverType name lib = case lookupWith typeName name lib of- Just x -> pure (isResolverType x)- Nothing -> isParametrizedType name--isSubscription :: TypeKind -> Bool-isSubscription (KindObject (Just Subscription)) = True-isSubscription _ = False--mkObjectField ::- CodeGenMonad m =>- FieldDefinition OUT CONST ->- ServerQ m ServerFieldDefinition-mkObjectField- FieldDefinition- { fieldName = fName,- fieldContent,- fieldType = TypeRef {typeConName, typeWrappers}- } = do- isParametrized <- lift . isParametrizedResolverType typeConName =<< asks typeDefinitions- genName <- asks toArgsTypeName- kind <- asks currentKind- fieldName <- renderFieldName fName- pure- ServerFieldDefinition- { fieldType = toHaskellTypeName typeConName,- wrappers =- mkFieldArguments fName genName (toArgList fieldContent)- <> [SUBSCRIPTION | fmap isSubscription kind == Just True]- <> [MONAD]- <> [GQL_WRAPPER typeWrappers]- <> [PARAMETRIZED | isParametrized],- ..- }--mkFieldArguments :: FieldName -> (FieldName -> TypeName) -> [ArgumentDefinition s] -> [FIELD_TYPE_WRAPPER]-mkFieldArguments _ _ [] = []-mkFieldArguments- _- _- [ ArgumentDefinition FieldDefinition {fieldName, fieldType}- ] = [TAGGED_ARG fieldName fieldType]-mkFieldArguments fName genName _ = [ARG (genName fName)]--toArgList :: Maybe (FieldContent bool cat s) -> [ArgumentDefinition s]-toArgList (Just (FieldArgs args)) = toList args-toArgList _ = []--data BuildPlan- = ConsIN [ServerConstructorDefinition]- | ConsOUT [ServerTypeDefinition] [ServerConstructorDefinition]--genInterfaceUnion :: Monad m => TypeName -> ServerQ m [ServerTypeDefinition]-genInterfaceUnion interfaceName =- mkInterface . map typeName . mapMaybe (isPossibleInterfaceType interfaceName)- <$> asks typeDefinitions- where- tKind = KindUnion- mkInterface [] = []- mkInterface [possibleTypeName] = [mkGuardWithPossibleType possibleTypeName]- mkInterface members =- [ mkGuardWithPossibleType tName,- ServerTypeDefinition- { tName = toHaskellTypeName tName,- tCons = map (mkUnionFieldDefinition tName) members,- tKind,- typeParameters = ["m"],- derives = derivesClasses True,- typeGQLType = Nothing- }- ]- mkGuardWithPossibleType = ServerInterfaceDefinition interfaceName (mkInterfaceName interfaceName)- tName = mkPossibleTypesName interfaceName--renderFieldName :: Monad m => FieldName -> ServerQ m FieldName-renderFieldName fieldName = do- TypeContext {hasNamespace, currentTypeName} <- ask- pure $- if hasNamespace- then maybe fieldName (`camelCaseFieldName` fieldName) currentTypeName- else fieldName--mkConsEnum :: Monad m => TypeName -> DataEnumValue CONST -> ServerQ m ServerConstructorDefinition-mkConsEnum name DataEnumValue {enumName} = do- namespace <- asks hasNamespace- pure- ServerConstructorDefinition- { constructorName =- if namespace- then camelCaseTypeName [name] enumName- else enumName,- constructorFields = []- }--renderDataField :: Monad m => FieldDefinition c CONST -> ServerQ m ServerFieldDefinition-renderDataField FieldDefinition {fieldType = TypeRef {typeConName, typeWrappers}, fieldName = fName} = do- fieldName <- renderFieldName fName- let wrappers = [GQL_WRAPPER typeWrappers]- let fieldType = toHaskellTypeName typeConName- pure ServerFieldDefinition {..}--genTypeContent ::- CodeGenMonad m =>- TypeName ->- TypeContent TRUE ANY CONST ->- ServerQ m BuildPlan-genTypeContent _ DataScalar {} = pure (ConsIN [])-genTypeContent typeName (DataEnum tags) = ConsIN <$> traverse (mkConsEnum typeName) tags-genTypeContent typeName (DataInputObject fields) =- ConsIN . mkObjectCons typeName <$> traverse renderDataField (toList fields)-genTypeContent _ DataInputUnion {} = fail "Input Unions not Supported"-genTypeContent typeName DataInterface {interfaceFields} =- ConsOUT- <$> ((<>) <$> genArgumentTypes interfaceFields <*> genInterfaceUnion typeName)- <*> ( do- let interfaceName = mkInterfaceName typeName- inType- (Just interfaceName)- ( mkObjectCons interfaceName- <$> traverse mkObjectField (toList interfaceFields)- )- )-genTypeContent typeName DataObject {objectFields} =- ConsOUT- <$> genArgumentTypes objectFields- <*> ( mkObjectCons typeName- <$> traverse mkObjectField (toList objectFields)- )-genTypeContent typeName (DataUnion members) =- pure $ ConsOUT [] (unionCon <$> toList members)- where- unionCon UnionMember {memberName} = mkUnionFieldDefinition typeName memberName--mkUnionFieldDefinition :: TypeName -> TypeName -> ServerConstructorDefinition-mkUnionFieldDefinition typeName memberName =- ServerConstructorDefinition- { constructorName,- constructorFields =- [ ServerFieldDefinition- { fieldName = coerce ("un" <> constructorName),- fieldType = toHaskellTypeName memberName,- wrappers = [PARAMETRIZED]- }- ]- }- where- constructorName = camelCaseTypeName [typeName] memberName--genArgumentTypes :: Monad m => FieldsDefinition OUT CONST -> ServerQ m [ServerTypeDefinition]-genArgumentTypes = fmap concat . traverse genArgumentType . toList--genArgumentType :: Monad m => FieldDefinition OUT CONST -> ServerQ m [ServerTypeDefinition]-genArgumentType- FieldDefinition- { fieldName,- fieldContent = Just (FieldArgs arguments)- }- | length arguments > 1 = do- tName <- (fieldName &) <$> asks toArgsTypeName- inType (Just tName) $ do- let argumentFields = argument <$> toList arguments- fields <- traverse renderDataField argumentFields- let tKind = KindInputObject- pure- [ ServerTypeDefinition- { tName = toHaskellTypeName tName,- tKind,- tCons = mkObjectCons tName fields,- derives = derivesClasses False,- typeParameters = [],- typeGQLType =- Just- ( GQLTypeDefinition- { gqlKind = Type,- gqlTypeDescription = Nothing,- gqlTypeDescriptions = fromList (mapMaybe mkFieldDescription argumentFields),- gqlTypeDefaultValues = fromList (mapMaybe getDefaultValue argumentFields),- gqlTypeDirectiveUses = []- }- )- }- ]-genArgumentType _ = pure []--mkFieldDescription :: FieldDefinition cat s -> Maybe (Text, Description)-mkFieldDescription FieldDefinition {..} = (unpackName fieldName,) <$> fieldDescription-------getDesc :: MonadFail m => TypeDefinition c CONST -> ServerQ m (Map Token Description)-getDesc = fmap fromList . get--getDirs :: MonadFail m => TypeDefinition c CONST -> ServerQ m [ServerDirectiveUsage]-getDirs x = do- contentD <- map snd <$> get x- typeD <- traverse transform (toList $ AST.typeDirectives x)- pure (contentD <> typeD)- where- transform v = TypeDirectiveUsage <$> directiveTypeValue v--class Meta a v where- get :: MonadFail m => a -> ServerQ m [(Token, v)]--instance (Meta a v) => Meta (Maybe a) v where- get (Just x) = get x- get _ = pure []--instance- ( Meta (FieldsDefinition IN s) v,- Meta (FieldsDefinition OUT s) v,- Meta (DataEnumValue s) v- ) =>- Meta (TypeDefinition c s) v- where- get TypeDefinition {typeContent} = get typeContent--instance- ( Meta (FieldsDefinition IN s) v,- Meta (FieldsDefinition OUT s) v,- Meta (DataEnumValue s) v- ) =>- Meta (TypeContent a c s) v- where- get DataObject {objectFields} = get objectFields- get DataInputObject {inputObjectFields} = get inputObjectFields- get DataInterface {interfaceFields} = get interfaceFields- get DataEnum {enumMembers} = concat <$> traverse get enumMembers- get _ = pure []--instance Meta (DataEnumValue CONST) Description where- get DataEnumValue {enumName, enumDescription = Just x} = pure [(unpackName enumName, x)]- get _ = pure []--instance Meta (DataEnumValue CONST) ServerDirectiveUsage where- get DataEnumValue {enumName, enumDirectives}- | null enumDirectives = pure []- | otherwise = traverse transform (toList enumDirectives)- where- transform x = (unpackName enumName,) . EnumDirectiveUsage enumName <$> directiveTypeValue x--instance- Meta (FieldDefinition c CONST) v =>- Meta (FieldsDefinition c CONST) v- where- get = fmap concat . traverse get . toList--instance Meta (FieldDefinition c CONST) Description where- get FieldDefinition {fieldName, fieldDescription = Just x} = pure [(unpackName fieldName, x)]- get _ = pure []--instance Meta (FieldDefinition c CONST) ServerDirectiveUsage where- get FieldDefinition {fieldName, fieldDirectives}- | null fieldDirectives = pure []- | otherwise = traverse transform (toList fieldDirectives)- where- transform x = (unpackName fieldName,) . FieldDirectiveUsage fieldName <$> directiveTypeValue x--getInputFields :: TypeDefinition c s -> [FieldDefinition IN s]-getInputFields TypeDefinition {typeContent = DataInputObject {inputObjectFields}} = toList inputObjectFields-getInputFields _ = []--getDefaultValue :: FieldDefinition c s -> Maybe (Text, V.Value s)-getDefaultValue- FieldDefinition- { fieldName,- fieldContent = Just DefaultInputValue {defaultInputValue}- } = Just (unpackName fieldName, defaultInputValue)-getDefaultValue _ = Nothing--nativeDirectives :: V.DirectivesDefinition CONST-nativeDirectives = AST.directiveDefinitions internalSchema--getDirective :: (MonadReader (TypeContext CONST) m, MonadFail m) => FieldName -> m (DirectiveDefinition CONST)-getDirective directiveName = do- dirs <- asks directiveDefinitions- case find (\DirectiveDefinition {directiveDefinitionName} -> directiveDefinitionName == directiveName) dirs of- Just dir -> pure dir- _ -> selectOr (fail $ "unknown directive" <> show directiveName) pure directiveName nativeDirectives--directiveTypeValue :: MonadFail m => Directive CONST -> ServerQ m TypeValue-directiveTypeValue Directive {..} = inType typeContext $ do- dirs <- getDirective directiveName- TypeValueObject typename <$> traverse (renderArgumentValue directiveArgs) (toList $ directiveDefinitionArgs dirs)- where- (typeContext, typename) = renderDirectiveTypeName directiveName--renderDirectiveTypeName :: FieldName -> (Maybe TypeName, TypeName)-renderDirectiveTypeName "deprecated" = (Nothing, "Deprecated")-renderDirectiveTypeName name = (Just (coerce name), coerce name)--renderArgumentValue ::- (IsMap FieldName c, MonadFail m) =>- c (Argument CONST) ->- ArgumentDefinition s ->- ReaderT (TypeContext CONST) m (FieldName, TypeValue)-renderArgumentValue args ArgumentDefinition {..} = do- let dirName = AST.fieldName argument- gqlValue <- selectOr (pure AST.Null) (pure . argumentValue) dirName args- typeValue <- mapWrappedValue (AST.fieldType argument) gqlValue- fName <- renderFieldName dirName- pure (fName, typeValue)--notFound :: MonadFail m => String -> String -> m a-notFound name at = fail $ "can't found " <> name <> "at " <> at <> "!"--lookupType :: MonadFail m => TypeName -> ServerQ m (TypeDefinition ANY CONST)-lookupType name = do- types <- asks typeDefinitions- case find (\t -> typeName t == name) types of- Just x -> pure x- Nothing -> notFound (show name) "type definitions"--lookupValueFieldType :: MonadFail m => TypeName -> FieldName -> ServerQ m TypeRef-lookupValueFieldType name fieldName = do- TypeDefinition {typeContent} <- lookupType name- case typeContent of- DataInputObject fields -> do- FieldDefinition {fieldType} <- selectOr (notFound (show fieldName) (show name)) pure fieldName fields- pure fieldType- _ -> notFound "input object" (show name)--mapField :: MonadFail m => TypeName -> ObjectEntry CONST -> ServerQ m (FieldName, TypeValue)-mapField tName ObjectEntry {..} = do- t <- lookupValueFieldType tName entryName- value <- mapWrappedValue t entryValue- pure (entryName, value)--expected :: MonadFail m => String -> V.Value CONST -> ServerQ m TypeValue-expected typ value = fail ("expected " <> typ <> ", found " <> show (render value) <> "!")--mapWrappedValue :: MonadFail m => TypeRef -> V.Value CONST -> ServerQ m TypeValue-mapWrappedValue (TypeRef name (AST.BaseType isRequired)) value- | isRequired = mapValue name value- | value == V.Null = pure (TypedValueMaybe Nothing)- | otherwise = TypedValueMaybe . Just <$> mapValue name value-mapWrappedValue (TypeRef name (AST.TypeList elems isRequired)) d = case d of- V.Null | not isRequired -> pure (TypedValueMaybe Nothing)- (V.List xs) -> TypedValueMaybe . Just . TypeValueList <$> traverse (mapWrappedValue (TypeRef name elems)) xs- value -> expected "list" value--mapValue :: MonadFail m => TypeName -> V.Value CONST -> ServerQ m TypeValue-mapValue name (V.List xs) = TypeValueList <$> traverse (mapValue name) xs-mapValue _ (V.Enum name) = pure $ TypeValueObject name []-mapValue name (V.Object fields) = TypeValueObject name <$> traverse (mapField name) (toList fields)-mapValue _ (V.Scalar x) = mapScalarValue x-mapValue t v = expected (show t) v--mapScalarValue :: MonadFail m => V.ScalarValue -> ServerQ m TypeValue-mapScalarValue (V.Int x) = pure $ TypeValueNumber (fromIntegral x)-mapScalarValue (V.Float x) = pure $ TypeValueNumber x-mapScalarValue (V.String x) = pure $ TypeValueString x-mapScalarValue (V.Boolean x) = pure $ TypeValueBool x-mapScalarValue (V.Value _) = fail "JSON objects are not supported!"
− src/Data/Morpheus/CodeGen/Printing/GQLType.hs
@@ -1,70 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE NoImplicitPrelude #-}--module Data.Morpheus.CodeGen.Printing.GQLType- ( renderGQLType,- )-where--import Data.Morpheus.CodeGen.Internal.AST- ( GQLTypeDefinition (..),- Kind (..),- ServerDirectiveUsage (..),- ServerTypeDefinition (..),- TypeKind,- )-import Data.Morpheus.CodeGen.Printing.Terms- ( optional,- parametrizedType,- )-import Prettyprinter-import Relude hiding (optional, show)-import Prelude (show)--renderTypeableConstraints :: [Text] -> Doc n-renderTypeableConstraints xs = tupled (map (("Typeable" <+>) . pretty) xs) <+> "=>"--defineTypeOptions :: Bool -> Text -> TypeKind -> Doc n-defineTypeOptions namespaces tName kind- | namespaces = "typeOptions _ = dropNamespaceOptions " <> pretty tName <> " (" <> pretty (show kind) <> ")"- | otherwise = "typeOptions _ options = options"--renderGQLType :: ServerTypeDefinition -> Doc n-renderGQLType ServerTypeDefinition {..} =- "instance"- <> optional renderTypeableConstraints typeParameters- <+> "GQLType"- <+> typeHead- <+> "where"- <> line- <> indent 2 (vsep (renderMethods typeHead typeGQLType <> [options]))- where- options = defineTypeOptions False tName tKind- typeHead =- if null typeParameters- then parametrizedType tName typeParameters- else tupled (pure $ parametrizedType tName typeParameters)-renderGQLType _ = ""--renderMethods :: Doc n -> Maybe GQLTypeDefinition -> [Doc n]-renderMethods _ Nothing = []-renderMethods- typeHead- (Just GQLTypeDefinition {..}) =- ["type KIND" <+> typeHead <+> "=" <+> renderKind gqlKind]- <> ["description _ =" <+> pretty (show gqlTypeDescription) | not (null gqlTypeDescription)]- <> ["getDescriptions _ =" <+> pretty (show gqlTypeDescriptions) | not (null gqlTypeDescriptions)]- <> ["directives _=" <+> renderDirectiveUsages gqlTypeDirectiveUses | not (null gqlTypeDirectiveUses)]--renderDirectiveUsages :: [ServerDirectiveUsage] -> Doc n-renderDirectiveUsages = align . vsep . punctuate " <>" . map renderDirectiveUsage--renderDirectiveUsage :: ServerDirectiveUsage -> Doc n-renderDirectiveUsage (TypeDirectiveUsage value) = "typeDirective" <+> pretty value-renderDirectiveUsage (FieldDirectiveUsage place value) = "fieldDirective" <+> pretty (show place) <+> pretty value-renderDirectiveUsage (EnumDirectiveUsage place value) = "enumDirective" <+> pretty (show place) <+> pretty value--renderKind :: Kind -> Doc n-renderKind Type = "TYPE"-renderKind Scalar = "SCALAR"
− src/Data/Morpheus/CodeGen/Printing/Render.hs
@@ -1,87 +0,0 @@-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE NoImplicitPrelude #-}--module Data.Morpheus.CodeGen.Printing.Render- ( renderDocument,- )-where--import Data.ByteString.Lazy.Char8 (ByteString)-import Data.Morpheus.CodeGen.Internal.AST- ( ModuleDefinition (..),- ServerTypeDefinition (..),- )-import Data.Morpheus.CodeGen.Printing.Terms- ( renderExtension,- renderImport,- )-import Data.Morpheus.CodeGen.Printing.Type- ( renderTypes,- )-import Data.Text- ( pack,- )-import qualified Data.Text.Lazy as LT- ( fromStrict,- )-import Data.Text.Lazy.Encoding (encodeUtf8)-import Prettyprinter- ( Doc,- line,- pretty,- vsep,- (<+>),- )-import Relude hiding (ByteString, encodeUtf8)--renderDocument :: String -> [ServerTypeDefinition] -> ByteString-renderDocument moduleName types =- encodeUtf8 $- LT.fromStrict $- pack $- show $- renderModuleDefinition- ModuleDefinition- { moduleName = pack moduleName,- imports =- [ ("Data.Data", ["Typeable"]),- ("Data.Morpheus.Kind", ["TYPE"]),- ("Data.Morpheus.Types", []),- ("Data.Morpheus", []),- ("Data.Text", ["Text"]),- ("GHC.Generics", ["Generic"]),- ("Data.Map", ["fromList", "empty"])- ],- extensions =- [ "DeriveAnyClass",- "DeriveGeneric",- "TypeFamilies",- "OverloadedStrings",- "DataKinds",- "DuplicateRecordFields"- ],- types- }--renderModuleDefinition :: ModuleDefinition -> Doc n-renderModuleDefinition- ModuleDefinition- { extensions,- moduleName,- imports,- types- } =- vsep (map renderExtension extensions)- <> line- <> line- <> "module"- <+> pretty moduleName- <+> "where"- <> line- <> line- <> vsep (map renderImport imports)- <> line- <> line- <> either (error . show) id (renderTypes types)
− src/Data/Morpheus/CodeGen/Printing/Terms.hs
@@ -1,78 +0,0 @@-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE NoImplicitPrelude #-}--module Data.Morpheus.CodeGen.Printing.Terms- ( renderExtension,- renderWrapped,- label,- parametrizedType,- TypeDoc (..),- appendType,- optional,- renderImport,- renderType,- renderName,- )-where--import Data.Morpheus.CodeGen.Internal.AST- ( TypeName,- TypeWrapper (..),- unpackName,- )-import qualified Data.Text as T-import Prettyprinter- ( Doc,- hsep,- list,- pretty,- tupled,- (<+>),- )-import Relude hiding (optional)--parametrizedType :: Text -> [Text] -> Doc ann-parametrizedType tName typeParameters = hsep $ map pretty $ tName : typeParameters---- TODO: this should be done in transformer-renderName :: TypeName -> Doc ann-renderName = pretty . T.unpack . unpackName--renderExtension :: Text -> Doc ann-renderExtension name = "{-#" <+> "LANGUAGE" <+> pretty name <+> "#-}"--data TypeDoc n = TypeDoc- { isComplex :: Bool,- unDoc :: Doc n- }--renderType :: TypeDoc n -> Doc n-renderType TypeDoc {isComplex, unDoc = doc} = if isComplex then tupled [doc] else doc--appendType :: TypeName -> TypeDoc n -> TypeDoc n-appendType t1 tyDoc = TypeDoc True $ renderName t1 <> " " <> renderType tyDoc--renderMaybe :: Bool -> TypeDoc n -> TypeDoc n-renderMaybe True = id-renderMaybe False = appendType "Maybe"--renderList :: TypeDoc n -> TypeDoc n-renderList = TypeDoc False . list . pure . unDoc--renderWrapped :: TypeWrapper -> TypeDoc n -> TypeDoc n-renderWrapped (TypeList wrapper notNull) = renderMaybe notNull . renderList . renderWrapped wrapper-renderWrapped (BaseType notNull) = renderMaybe notNull--label :: Text -> Doc ann-label typeName = "---- GQL " <> pretty typeName <> " -------------------------------\n"--optional :: ([a] -> Doc n) -> [a] -> Doc n-optional _ [] = ""-optional f xs = " " <> f xs--renderImport :: (Text, [Text]) -> Doc ann-renderImport (src, ls) =- "import"- <+> pretty src- <> optional (tupled . map pretty) ls
− src/Data/Morpheus/CodeGen/Printing/Type.hs
@@ -1,150 +0,0 @@-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE NoImplicitPrelude #-}--module Data.Morpheus.CodeGen.Printing.Type- ( renderTypes,- )-where--import Data.Morpheus.CodeGen.Internal.AST- ( DerivingClass (..),- FIELD_TYPE_WRAPPER (..),- ServerConstructorDefinition (..),- ServerFieldDefinition (..),- ServerTypeDefinition (..),- TypeKind (..),- TypeRef (..),- unpackName,- )-import Data.Morpheus.CodeGen.Printing.GQLType- ( renderGQLType,- )-import Data.Morpheus.CodeGen.Printing.Terms- ( TypeDoc (TypeDoc, unDoc),- appendType,- label,- parametrizedType,- renderName,- renderType,- renderWrapped,- )-import Prettyprinter- ( Doc,- comma,- enclose,- indent,- line,- nest,- pretty,- punctuate,- tupled,- vsep,- (<+>),- )-import Relude hiding (show)-import Prelude (show)--type Result = Either Text--renderTypes :: [ServerTypeDefinition] -> Either Text (Doc ann)-renderTypes = fmap vsep . traverse render--class RenderType a where- render :: a -> Result (Doc ann)--instance RenderType DerivingClass where- render SHOW = pure "Show"- render GENERIC = pure "Generic"--instance RenderType ServerTypeDefinition where- render ServerInterfaceDefinition {} = fail "not supported"- -- TODO: on scalar we should render user provided type- render ServerTypeDefinition {tKind = KindScalar, tName} =- pure $ label tName <> "type" <+> pretty tName <+> "= Int"- render typeDef@ServerTypeDefinition {tName, tCons, typeParameters, derives} = do- typeRendering <- renderTypeDef- pure $ label tName <> vsep [typeRendering, renderGQLType typeDef]- where- renderTypeDef = do- cons <- renderConstructors tCons- derivations <- renderDeriving derives- pure $- "data"- <+> parametrizedType tName typeParameters- <> cons- <> line- <> indent 2 derivations- <> line- renderConstructors [cons] = (" =" <+>) <$> render cons- renderConstructors conses = nest 2 . (line <>) . vsep . prefixVariants <$> traverse render conses- prefixVariants (x : xs) = "=" <+> x : map ("|" <+>) xs- prefixVariants [] = []- render typeDef@DirectiveTypeDefinition {directiveConstructor, directiveDerives} = do- typeRendering <- renderTypeDef- pure $ label name <> vsep [typeRendering, renderGQLType typeDef]- where- renderTypeDef = do- cons <- (" =" <+>) <$> render directiveConstructor- derivations <- renderDeriving directiveDerives- pure $- "data"- <+> parametrizedType name []- <> cons- <> line- <> indent 2 derivations- <> line- name = unpackName (constructorName directiveConstructor)--renderDeriving :: [DerivingClass] -> Result (Doc n)-renderDeriving list = ("deriving" <+>) . tupled <$> traverse render list--instance RenderType ServerConstructorDefinition where- render ServerConstructorDefinition {constructorName, constructorFields = []} =- pure $ renderName constructorName- render ServerConstructorDefinition {constructorName, constructorFields} = do- fields <- traverse render constructorFields- pure $ renderName constructorName <> renderSet fields- where- renderSet = nest 2 . enclose "\n{ " "\n}" . nest 2 . vsep . punctuate comma--instance RenderType ServerFieldDefinition where- render- ServerFieldDefinition- { fieldName,- wrappers,- fieldType- } =- pure $- pretty (unpackName fieldName :: Text)- <+> "::"- <+> unDoc (foldr renderWrapper (TypeDoc False $ pretty fieldType) wrappers)--renderWrapper :: FIELD_TYPE_WRAPPER -> TypeDoc n -> TypeDoc n-renderWrapper PARAMETRIZED = \x -> TypeDoc True (unDoc x <+> "m")-renderWrapper MONAD = appendType "m"-renderWrapper SUBSCRIPTION = id-renderWrapper (GQL_WRAPPER typeWrappers) = renderWrapped typeWrappers-renderWrapper (ARG name) = TypeDoc True . ((renderName name <+> "->") <+>) . unDoc-renderWrapper (TAGGED_ARG name typeRef) =- TypeDoc True- . ( ( "Arg"- <+> pretty (show name)- <+> renderType (renderTypeRef typeRef)- <+> "->"- )- <+>- )- . unDoc--renderTypeRef :: TypeRef -> TypeDoc n-renderTypeRef- TypeRef- { typeConName,- typeWrappers- } =- renderWrapped- typeWrappers- (TypeDoc False (renderName typeConName))
src/Data/Morpheus/CodeGen/Server.hs view
@@ -2,23 +2,46 @@ {-# LANGUAGE NoImplicitPrelude #-} module Data.Morpheus.CodeGen.Server- ( printServerTypeDefinitions,+ ( CodeGenConfig (..), PrinterConfig (..),+ gqlDocument,+ importServerTypeDefinitions,+ printServerTypeDefinitions, ) where -import Data.ByteString.Lazy.Char8 (ByteString)-import Data.Morpheus.CodeGen.Internal.AST- ( ServerTypeDefinition,+import Data.ByteString.Lazy.Char8+ ( ByteString,+ readFile, )-import Data.Morpheus.CodeGen.Printing.Render+import Data.FileEmbed (makeRelativeToProject)+import Data.Morpheus.CodeGen.Server.Internal.AST+ ( CodeGenConfig (..),+ ServerDeclaration,+ )+import Data.Morpheus.CodeGen.Server.Printing.Document ( renderDocument, )-import Relude hiding (ByteString)+import Data.Morpheus.CodeGen.Server.Printing.TH+ ( compileDocument,+ gqlDocument,+ )+import Language.Haskell.TH (Dec, Q, runIO)+import Language.Haskell.TH.Syntax+ ( qAddDependentFile,+ )+import Relude hiding (ByteString, readFile) newtype PrinterConfig = PrinterConfig { moduleName :: String } -printServerTypeDefinitions :: PrinterConfig -> [ServerTypeDefinition] -> ByteString+printServerTypeDefinitions :: PrinterConfig -> [ServerDeclaration] -> ByteString printServerTypeDefinitions PrinterConfig {moduleName} = renderDocument moduleName++importServerTypeDefinitions :: CodeGenConfig -> FilePath -> Q [Dec]+importServerTypeDefinitions ctx rawSrc = do+ src <- makeRelativeToProject rawSrc+ qAddDependentFile src+ runIO (readFile src)+ >>= compileDocument ctx
+ src/Data/Morpheus/CodeGen/Server/Internal/AST.hs view
@@ -0,0 +1,127 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskellQuotes #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Data.Morpheus.CodeGen.Server.Internal.AST+ ( ModuleDefinition (..),+ CodeGenConfig (..),+ ServerDeclaration (..),+ GQLTypeDefinition (..),+ CONST,+ TypeKind (..),+ TypeName,+ TypeRef (..),+ TypeWrapper (..),+ unpackName,+ DerivingClass (..),+ FIELD_TYPE_WRAPPER (..),+ Kind (..),+ ServerDirectiveUsage (..),+ TypeValue (..),+ InterfaceDefinition (..),+ GQLDirectiveTypeClass (..),+ )+where++import Data.Morpheus.CodeGen.Internal.AST+ ( CodeGenType,+ CodeGenTypeName,+ DerivingClass (..),+ FIELD_TYPE_WRAPPER (..),+ TypeValue (..),+ )+import Data.Morpheus.CodeGen.TH+ ( PrintExp (..),+ PrintType (..),+ )+import Data.Morpheus.Server.Types+ ( SCALAR,+ TYPE,+ enumDirective,+ fieldDirective,+ typeDirective,+ )+import Data.Morpheus.Types.Internal.AST+ ( CONST,+ DirectiveLocation (..),+ FieldName,+ TypeKind (..),+ TypeName,+ TypeRef (..),+ TypeWrapper (..),+ Value,+ unpackName,+ )+import Language.Haskell.TH.Lib (appE, conT, varE)+import Prettyprinter (Pretty (..), (<+>))+import Relude++data ModuleDefinition = ModuleDefinition+ { moduleName :: Text,+ imports :: [(Text, [Text])],+ extensions :: [Text],+ types :: [ServerDeclaration]+ }++data Kind+ = Scalar+ | Type+ deriving (Show, Eq)++instance Pretty Kind where+ pretty Type = "TYPE"+ pretty Scalar = "SCALAR"++instance PrintType Kind where+ printType Scalar = conT ''SCALAR+ printType Type = conT ''TYPE++data ServerDirectiveUsage+ = TypeDirectiveUsage TypeValue+ | FieldDirectiveUsage FieldName TypeValue+ | EnumDirectiveUsage TypeName TypeValue+ deriving (Show)++instance PrintExp ServerDirectiveUsage where+ printExp (TypeDirectiveUsage x) = appE (varE 'typeDirective) (printExp x)+ printExp (FieldDirectiveUsage field x) = appE (appE (varE 'fieldDirective) [|field|]) (printExp x)+ printExp (EnumDirectiveUsage enum x) = appE (appE (varE 'enumDirective) [|enum|]) (printExp x)++instance Pretty ServerDirectiveUsage where+ pretty (TypeDirectiveUsage value) = "typeDirective" <+> pretty value+ pretty (FieldDirectiveUsage place value) = "fieldDirective" <+> pretty (show place :: String) <+> pretty value+ pretty (EnumDirectiveUsage place value) = "enumDirective" <+> pretty (show place :: String) <+> pretty value++data GQLTypeDefinition = GQLTypeDefinition+ { gqlTarget :: CodeGenTypeName,+ gqlKind :: Kind,+ gqlTypeDirectiveUses :: [ServerDirectiveUsage],+ gqlTypeDefaultValues :: Map Text (Value CONST),+ dropNamespace :: Maybe (TypeKind, Text)+ }+ deriving (Show)++data InterfaceDefinition = InterfaceDefinition+ { aliasName :: TypeName,+ interfaceName :: TypeName,+ unionName :: TypeName+ }+ deriving (Show)++data GQLDirectiveTypeClass = GQLDirectiveTypeClass+ { directiveTypeName :: CodeGenTypeName,+ directiveLocations :: [DirectiveLocation]+ }+ deriving (Show)++data ServerDeclaration+ = GQLTypeInstance GQLTypeDefinition+ | GQLDirectiveInstance GQLDirectiveTypeClass+ | DataType CodeGenType+ | ScalarType {scalarTypeName :: Text}+ | InterfaceType InterfaceDefinition+ deriving (Show)++newtype CodeGenConfig = CodeGenConfig+ { namespace :: Bool+ }
+ src/Data/Morpheus/CodeGen/Server/Interpreting/Transform.hs view
@@ -0,0 +1,613 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskellQuotes #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Data.Morpheus.CodeGen.Server.Interpreting.Transform+ ( parseServerTypeDefinitions,+ )+where++import Data.ByteString.Lazy.Char8 (ByteString)+import Data.Morpheus.CodeGen.Internal.AST+ ( CodeGenConstructor (..),+ CodeGenField (..),+ CodeGenType (..),+ CodeGenTypeName (CodeGenTypeName),+ fromTypeName,+ getFullName,+ )+import Data.Morpheus.CodeGen.Server.Internal.AST+ ( CodeGenConfig (..),+ DerivingClass (..),+ FIELD_TYPE_WRAPPER (..),+ GQLDirectiveTypeClass (..),+ GQLTypeDefinition (..),+ InterfaceDefinition (..),+ Kind (..),+ ServerDeclaration (..),+ ServerDirectiveUsage (..),+ TypeValue (..),+ )+import Data.Morpheus.CodeGen.TH+ ( ToName (toName),+ )+import Data.Morpheus.CodeGen.Utils+ ( camelCaseFieldName,+ camelCaseTypeName,+ toHaskellTypeName,+ )+import Data.Morpheus.Core (internalSchema, parseDefinitions, render)+import Data.Morpheus.Error (gqlWarnings, renderGQLErrors)+import Data.Morpheus.Internal.Ext (GQLResult, Result (..))+import Data.Morpheus.Internal.Utils (IsMap, selectOr)+import Data.Morpheus.Server.Types (Arg, SubscriptionField)+import Data.Morpheus.Types.Internal.AST+ ( ANY,+ Argument (..),+ ArgumentDefinition (..),+ CONST,+ DataEnumValue (..),+ Description,+ Directive (Directive, directiveArgs, directiveName),+ DirectiveDefinition (..),+ FieldContent (..),+ FieldDefinition (..),+ FieldName,+ FieldsDefinition,+ GQLError,+ IN,+ OUT,+ ObjectEntry (..),+ OperationType (Subscription),+ RawTypeDefinition (..),+ TRUE,+ TypeContent (..),+ TypeDefinition (..),+ TypeKind (..),+ TypeName,+ TypeRef (..),+ UnionMember (..),+ isNullable,+ isPossibleInterfaceType,+ isResolverType,+ kindOf,+ lookupWith,+ packName,+ unpackName,+ )+import qualified Data.Morpheus.Types.Internal.AST as AST+import qualified Data.Morpheus.Types.Internal.AST as V+import Language.Haskell.TH+ ( Dec (..),+ Info (..),+ Q,+ TyVarBndr,+ reify,+ )+import Relude hiding (ByteString, get)++type ServerQ m = ReaderT (TypeContext CONST) m++class (Monad m, MonadFail m) => CodeGenMonad m where+ isParametrizedType :: TypeName -> m Bool+ printWarnings :: [GQLError] -> m ()++isParametrizedHaskellType :: Info -> Bool+isParametrizedHaskellType (TyConI x) = not $ null $ getTypeVariables x+isParametrizedHaskellType _ = False++#if MIN_VERSION_template_haskell(2,17,0)+getTypeVariables :: Dec -> [TyVarBndr ()]+#else+getTypeVariables :: Dec -> [TyVarBndr]+#endif+getTypeVariables (DataD _ _ args _ _ _) = args+getTypeVariables (NewtypeD _ _ args _ _ _) = args+getTypeVariables (TySynD _ args _) = args+getTypeVariables _ = []++instance CodeGenMonad Q where+ isParametrizedType name = isParametrizedHaskellType <$> reify (toName name)+ printWarnings = gqlWarnings++instance CodeGenMonad GQLResult where+ isParametrizedType _ = pure False+ printWarnings _ = pure ()++data TypeContext s = TypeContext+ { toArgsTypeName :: FieldName -> TypeName,+ typeDefinitions :: [TypeDefinition ANY s],+ directiveDefinitions :: [DirectiveDefinition s],+ currentTypeName :: Maybe TypeName,+ currentKind :: Maybe TypeKind,+ hasNamespace :: Bool+ }++parseServerTypeDefinitions :: CodeGenMonad m => CodeGenConfig -> ByteString -> m [ServerDeclaration]+parseServerTypeDefinitions ctx txt =+ case parseDefinitions txt of+ Failure errors -> fail (renderGQLErrors errors)+ Success {result, warnings} -> printWarnings warnings >> toTHDefinitions (namespace ctx) result++toTHDefinitions ::+ CodeGenMonad m =>+ Bool ->+ [RawTypeDefinition] ->+ m [ServerDeclaration]+toTHDefinitions namespace defs = concat <$> traverse generateTypes defs+ where+ typeDefinitions = [td | RawTypeDefinition td <- defs]+ directiveDefinitions = [td | RawDirectiveDefinition td <- defs]+ generateTypes :: CodeGenMonad m => RawTypeDefinition -> m [ServerDeclaration]+ generateTypes (RawTypeDefinition typeDef) =+ runReaderT+ (genTypeDefinition typeDef)+ TypeContext+ { toArgsTypeName = mkArgsTypeName namespace (typeName typeDef),+ typeDefinitions,+ directiveDefinitions,+ currentTypeName = Just (typeName typeDef),+ currentKind = Just (kindOf typeDef),+ hasNamespace = namespace+ }+ generateTypes (RawDirectiveDefinition DirectiveDefinition {..}) =+ runReaderT+ ( do+ fields <- traverse renderDataField (argument <$> toList directiveDefinitionArgs)+ let typename = coerce directiveDefinitionName+ dropNamespace <- defineTypeOptions KindInputObject (unpackName typename)+ let cgTypeName = fromTypeName typename+ pure+ [ DataType+ CodeGenType+ { cgTypeName,+ cgConstructors = [CodeGenConstructor (fromTypeName typename) fields],+ cgDerivations = [SHOW, GENERIC]+ },+ GQLDirectiveInstance+ GQLDirectiveTypeClass+ { directiveTypeName = cgTypeName,+ directiveLocations = directiveDefinitionLocations+ },+ GQLTypeInstance+ GQLTypeDefinition+ { gqlTarget = cgTypeName,+ gqlKind = Type,+ gqlTypeDefaultValues = mempty,+ gqlTypeDirectiveUses = [],+ dropNamespace+ }+ ]+ )+ TypeContext+ { toArgsTypeName = coerce,+ typeDefinitions,+ currentTypeName = Just (coerce directiveDefinitionName),+ directiveDefinitions,+ currentKind = Nothing,+ hasNamespace = namespace+ }+ generateTypes _ = pure []++defineTypeOptions :: MonadReader (TypeContext s) m => TypeKind -> Text -> m (Maybe (TypeKind, Text))+defineTypeOptions kind tName = do+ namespaces <- asks hasNamespace+ pure $ if namespaces then Just (kind, tName) else Nothing++inType :: MonadReader (TypeContext s) m => Maybe TypeName -> m a -> m a+inType name = local (\x -> x {currentTypeName = name, currentKind = Nothing})++mkInterfaceName :: TypeName -> TypeName+mkInterfaceName = ("Interface" <>)++mkPossibleTypesName :: TypeName -> TypeName+mkPossibleTypesName = ("PossibleTypes" <>)++genTypeDefinition ::+ CodeGenMonad m =>+ TypeDefinition ANY CONST ->+ ServerQ m [ServerDeclaration]+genTypeDefinition+ typeDef@TypeDefinition {typeName = originalTypeName, typeContent} =+ case tKind of+ KindScalar -> do+ scalarGQLType <- deriveGQL+ pure+ [ ScalarType (toHaskellTypeName typeName),+ scalarGQLType+ ]+ _ -> genTypeContent originalTypeName typeContent >>= withType+ where+ typeName = case typeContent of+ DataInterface {} -> mkInterfaceName originalTypeName+ _ -> originalTypeName+ tKind = kindOf typeDef+ cgTypeName = CodeGenTypeName [] ["m" | isResolverType tKind] (packName $ toHaskellTypeName typeName)+ deriveGQL = do+ gqlTypeDirectiveUses <- getDirs typeDef+ dropNamespace <- defineTypeOptions tKind (unpackName typeName)+ pure $+ GQLTypeInstance $+ GQLTypeDefinition+ { gqlTarget = cgTypeName,+ gqlTypeDirectiveUses,+ gqlKind = derivingKind tKind,+ gqlTypeDefaultValues =+ fromList $+ mapMaybe getDefaultValue $+ getInputFields typeDef,+ dropNamespace+ }+ cgDerivations = derivesClasses (isResolverType tKind)+ -------------------------+ withType (ConsIN cgConstructors) = do+ gqlType <- deriveGQL+ pure [DataType CodeGenType {..}, gqlType]+ withType (ConsOUT others cgConstructors) = do+ gqlType <- deriveGQL+ pure (DataType CodeGenType {..} : gqlType : others)++derivingKind :: TypeKind -> Kind+derivingKind KindScalar = Scalar+derivingKind _ = Type++derivesClasses :: Bool -> [DerivingClass]+derivesClasses isResolver = GENERIC : [SHOW | not isResolver]++mkObjectCons :: TypeName -> [CodeGenField] -> [CodeGenConstructor]+mkObjectCons name = pure . CodeGenConstructor (fromTypeName name)++mkArgsTypeName :: Bool -> TypeName -> FieldName -> TypeName+mkArgsTypeName namespace typeName fieldName+ | namespace = typeName <> argTName+ | otherwise = argTName+ where+ argTName = camelCaseTypeName [fieldName] "Args"++isParametrizedResolverType :: CodeGenMonad m => TypeName -> [TypeDefinition ANY s] -> m Bool+isParametrizedResolverType "__TypeKind" _ = pure False+isParametrizedResolverType "Boolean" _ = pure False+isParametrizedResolverType "String" _ = pure False+isParametrizedResolverType "Int" _ = pure False+isParametrizedResolverType "Float" _ = pure False+isParametrizedResolverType name lib = case lookupWith typeName name lib of+ Just x -> pure (isResolverType x)+ Nothing -> isParametrizedType name++isSubscription :: TypeKind -> Bool+isSubscription (KindObject (Just Subscription)) = True+isSubscription _ = False++mkObjectField ::+ CodeGenMonad m =>+ FieldDefinition OUT CONST ->+ ServerQ m CodeGenField+mkObjectField+ FieldDefinition+ { fieldName = fName,+ fieldContent,+ fieldType = TypeRef {typeConName, typeWrappers}+ } = do+ isParametrized <- lift . isParametrizedResolverType typeConName =<< asks typeDefinitions+ genName <- asks toArgsTypeName+ kind <- asks currentKind+ fieldName <- renderFieldName fName+ pure+ CodeGenField+ { fieldType = packName (toHaskellTypeName typeConName),+ fieldIsNullable = isNullable typeWrappers,+ wrappers =+ mkFieldArguments fName genName (toArgList fieldContent)+ <> [SUBSCRIPTION ''SubscriptionField | fmap isSubscription kind == Just True]+ <> [MONAD]+ <> [GQL_WRAPPER typeWrappers]+ <> [PARAMETRIZED | isParametrized],+ ..+ }++mkFieldArguments :: FieldName -> (FieldName -> TypeName) -> [ArgumentDefinition s] -> [FIELD_TYPE_WRAPPER]+mkFieldArguments _ _ [] = []+mkFieldArguments+ _+ _+ [ ArgumentDefinition FieldDefinition {fieldName, fieldType}+ ] = [TAGGED_ARG ''Arg fieldName fieldType]+mkFieldArguments fName genName _ = [ARG (genName fName)]++toArgList :: Maybe (FieldContent bool cat s) -> [ArgumentDefinition s]+toArgList (Just (FieldArgs args)) = toList args+toArgList _ = []++data BuildPlan+ = ConsIN [CodeGenConstructor]+ | ConsOUT [ServerDeclaration] [CodeGenConstructor]++genInterfaceUnion :: Monad m => TypeName -> ServerQ m [ServerDeclaration]+genInterfaceUnion interfaceName =+ mkInterface . map typeName . mapMaybe (isPossibleInterfaceType interfaceName)+ <$> asks typeDefinitions+ where+ mkInterface [] = []+ mkInterface [possibleTypeName] = [mkGuardWithPossibleType possibleTypeName]+ mkInterface members =+ [ mkGuardWithPossibleType tName,+ DataType+ CodeGenType+ { cgTypeName = possTypeName,+ cgConstructors = map (mkUnionFieldDefinition tName) members,+ cgDerivations = derivesClasses True+ },+ GQLTypeInstance+ GQLTypeDefinition+ { gqlTarget = possTypeName,+ gqlKind = Type,+ gqlTypeDirectiveUses = empty,+ gqlTypeDefaultValues = mempty,+ dropNamespace = Nothing+ }+ ]+ where+ possTypeName = CodeGenTypeName [] ["m"] (packName $ toHaskellTypeName tName)+ mkGuardWithPossibleType = InterfaceType . InterfaceDefinition interfaceName (mkInterfaceName interfaceName)+ tName = mkPossibleTypesName interfaceName++renderFieldName :: Monad m => FieldName -> ServerQ m FieldName+renderFieldName fieldName = do+ TypeContext {hasNamespace, currentTypeName} <- ask+ pure $+ if hasNamespace+ then maybe fieldName (`camelCaseFieldName` fieldName) currentTypeName+ else fieldName++mkConsEnum :: Monad m => TypeName -> DataEnumValue CONST -> ServerQ m CodeGenConstructor+mkConsEnum name DataEnumValue {enumName} = do+ namespace <- asks hasNamespace+ pure+ CodeGenConstructor+ { constructorName =+ if namespace+ then CodeGenTypeName [coerce name] [] enumName+ else fromTypeName enumName,+ constructorFields = []+ }++renderDataField :: Monad m => FieldDefinition c CONST -> ServerQ m CodeGenField+renderDataField FieldDefinition {fieldType = TypeRef {typeConName, typeWrappers}, fieldName = fName} = do+ fieldName <- renderFieldName fName+ let wrappers = [GQL_WRAPPER typeWrappers]+ let fieldType = packName (toHaskellTypeName typeConName)+ let fieldIsNullable = isNullable typeWrappers+ pure CodeGenField {..}++genTypeContent ::+ CodeGenMonad m =>+ TypeName ->+ TypeContent TRUE ANY CONST ->+ ServerQ m BuildPlan+genTypeContent _ DataScalar {} = pure (ConsIN [])+genTypeContent typeName (DataEnum tags) = ConsIN <$> traverse (mkConsEnum typeName) tags+genTypeContent typeName (DataInputObject fields) =+ ConsIN . mkObjectCons typeName <$> traverse renderDataField (toList fields)+genTypeContent _ DataInputUnion {} = fail "Input Unions not Supported"+genTypeContent typeName DataInterface {interfaceFields} =+ ConsOUT+ <$> ((<>) <$> genArgumentTypes interfaceFields <*> genInterfaceUnion typeName)+ <*> ( do+ let interfaceName = mkInterfaceName typeName+ inType+ (Just interfaceName)+ ( mkObjectCons interfaceName+ <$> traverse mkObjectField (toList interfaceFields)+ )+ )+genTypeContent typeName DataObject {objectFields} =+ ConsOUT+ <$> genArgumentTypes objectFields+ <*> ( mkObjectCons typeName+ <$> traverse mkObjectField (toList objectFields)+ )+genTypeContent typeName (DataUnion members) =+ pure $ ConsOUT [] (unionCon <$> toList members)+ where+ unionCon UnionMember {memberName} = mkUnionFieldDefinition typeName memberName++mkUnionFieldDefinition :: TypeName -> TypeName -> CodeGenConstructor+mkUnionFieldDefinition typeName memberName =+ CodeGenConstructor+ { constructorName,+ constructorFields =+ [ CodeGenField+ { fieldName = coerce ("un" <> getFullName constructorName),+ fieldType = packName (toHaskellTypeName memberName),+ wrappers = [PARAMETRIZED],+ fieldIsNullable = False+ }+ ]+ }+ where+ constructorName = CodeGenTypeName [coerce typeName] [] memberName++genArgumentTypes :: MonadFail m => FieldsDefinition OUT CONST -> ServerQ m [ServerDeclaration]+genArgumentTypes = fmap concat . traverse genArgumentType . toList++genArgumentType :: MonadFail m => FieldDefinition OUT CONST -> ServerQ m [ServerDeclaration]+genArgumentType+ FieldDefinition+ { fieldName,+ fieldContent = Just (FieldArgs arguments)+ }+ | length arguments > 1 = do+ tName <- (fieldName &) <$> asks toArgsTypeName+ inType (Just tName) $ do+ let argumentFields = argument <$> toList arguments+ fields <- traverse renderDataField argumentFields+ let typename = toHaskellTypeName tName+ gqlTypeDirectiveUses <- concat <$> traverse getDirs argumentFields+ dropNamespace <- defineTypeOptions KindInputObject typename+ let cgTypeName = fromTypeName (packName typename)+ pure+ [ DataType+ CodeGenType+ { cgTypeName,+ cgConstructors = mkObjectCons tName fields,+ cgDerivations = derivesClasses False+ },+ GQLTypeInstance+ GQLTypeDefinition+ { gqlTarget = cgTypeName,+ gqlKind = Type,+ gqlTypeDefaultValues = fromList (mapMaybe getDefaultValue argumentFields),+ gqlTypeDirectiveUses,+ dropNamespace+ }+ ]+genArgumentType _ = pure []++-- mkFieldDescription :: FieldDefinition cat s -> Maybe (Text, Description)+-- mkFieldDescription FieldDefinition {..} = (unpackName fieldName,) <$> fieldDescription++---++class Meta a where+ getDirs :: MonadFail m => a -> ServerQ m [ServerDirectiveUsage]++instance (Meta a) => Meta (Maybe a) where+ getDirs (Just x) = getDirs x+ getDirs _ = pure []++descDirective :: Maybe Description -> [TypeValue]+descDirective desc = map describe (maybeToList desc)+ where+ describe x = TypeValueObject "Describe" [("text", TypeValueString x)]++instance Meta (TypeDefinition c CONST) where+ getDirs TypeDefinition {typeContent, typeDirectives, typeDescription} = do+ contentD <- getDirs typeContent+ typeD <- traverse transform (toList typeDirectives)+ pure (contentD <> typeD <> map TypeDirectiveUsage (descDirective typeDescription))+ where+ transform v = TypeDirectiveUsage <$> directiveTypeValue v++instance Meta (TypeContent a c CONST) where+ getDirs DataObject {objectFields} = getDirs objectFields+ getDirs DataInputObject {inputObjectFields} = getDirs inputObjectFields+ getDirs DataInterface {interfaceFields} = getDirs interfaceFields+ getDirs DataEnum {enumMembers} = concat <$> traverse getDirs enumMembers+ getDirs _ = pure []++instance Meta (DataEnumValue CONST) where+ getDirs DataEnumValue {enumName, enumDirectives, enumDescription} = do+ dirs <- traverse directiveTypeValue (toList enumDirectives)+ pure $ map (EnumDirectiveUsage enumName) (dirs <> descDirective enumDescription)++instance Meta (FieldsDefinition c CONST) where+ getDirs = fmap concat . traverse getDirs . toList++instance Meta (FieldDefinition c CONST) where+ getDirs FieldDefinition {fieldName, fieldDirectives, fieldDescription} = do+ dirs <- traverse directiveTypeValue (toList fieldDirectives)+ pure $ map (FieldDirectiveUsage fieldName) (dirs <> descDirective fieldDescription)++getInputFields :: TypeDefinition c s -> [FieldDefinition IN s]+getInputFields TypeDefinition {typeContent = DataInputObject {inputObjectFields}} = toList inputObjectFields+getInputFields _ = []++getDefaultValue :: FieldDefinition c s -> Maybe (Text, V.Value s)+getDefaultValue+ FieldDefinition+ { fieldName,+ fieldContent = Just DefaultInputValue {defaultInputValue}+ } = Just (unpackName fieldName, defaultInputValue)+getDefaultValue _ = Nothing++nativeDirectives :: V.DirectivesDefinition CONST+nativeDirectives = AST.directiveDefinitions internalSchema++getDirective :: (MonadReader (TypeContext CONST) m, MonadFail m) => FieldName -> m (DirectiveDefinition CONST)+getDirective directiveName = do+ dirs <- asks directiveDefinitions+ case find (\DirectiveDefinition {directiveDefinitionName} -> directiveDefinitionName == directiveName) dirs of+ Just dir -> pure dir+ _ -> selectOr (fail $ "unknown directive" <> show directiveName) pure directiveName nativeDirectives++directiveTypeValue :: MonadFail m => Directive CONST -> ServerQ m TypeValue+directiveTypeValue Directive {..} = inType typeContext $ do+ dirs <- getDirective directiveName+ TypeValueObject typename <$> traverse (renderArgumentValue directiveArgs) (toList $ directiveDefinitionArgs dirs)+ where+ (typeContext, typename) = renderDirectiveTypeName directiveName++renderDirectiveTypeName :: FieldName -> (Maybe TypeName, TypeName)+renderDirectiveTypeName "deprecated" = (Nothing, "Deprecated")+renderDirectiveTypeName name = (Just (coerce name), coerce name)++renderArgumentValue ::+ (IsMap FieldName c, MonadFail m) =>+ c (Argument CONST) ->+ ArgumentDefinition s ->+ ReaderT (TypeContext CONST) m (FieldName, TypeValue)+renderArgumentValue args ArgumentDefinition {..} = do+ let dirName = AST.fieldName argument+ gqlValue <- selectOr (pure AST.Null) (pure . argumentValue) dirName args+ typeValue <- mapWrappedValue (AST.fieldType argument) gqlValue+ fName <- renderFieldName dirName+ pure (fName, typeValue)++notFound :: MonadFail m => String -> String -> m a+notFound name at = fail $ "can't found " <> name <> "at " <> at <> "!"++lookupType :: MonadFail m => TypeName -> ServerQ m (TypeDefinition ANY CONST)+lookupType name = do+ types <- asks typeDefinitions+ case find (\t -> typeName t == name) types of+ Just x -> pure x+ Nothing -> notFound (show name) "type definitions"++lookupValueFieldType :: MonadFail m => TypeName -> FieldName -> ServerQ m TypeRef+lookupValueFieldType name fieldName = do+ TypeDefinition {typeContent} <- lookupType name+ case typeContent of+ DataInputObject fields -> do+ FieldDefinition {fieldType} <- selectOr (notFound (show fieldName) (show name)) pure fieldName fields+ pure fieldType+ _ -> notFound "input object" (show name)++mapField :: MonadFail m => TypeName -> ObjectEntry CONST -> ServerQ m (FieldName, TypeValue)+mapField tName ObjectEntry {..} = do+ t <- lookupValueFieldType tName entryName+ value <- mapWrappedValue t entryValue+ pure (entryName, value)++expected :: MonadFail m => String -> V.Value CONST -> ServerQ m TypeValue+expected typ value = fail ("expected " <> typ <> ", found " <> show (render value) <> "!")++mapWrappedValue :: MonadFail m => TypeRef -> V.Value CONST -> ServerQ m TypeValue+mapWrappedValue (TypeRef name (AST.BaseType isRequired)) value+ | isRequired = mapValue name value+ | value == V.Null = pure (TypedValueMaybe Nothing)+ | otherwise = TypedValueMaybe . Just <$> mapValue name value+mapWrappedValue (TypeRef name (AST.TypeList elems isRequired)) d = case d of+ V.Null | not isRequired -> pure (TypedValueMaybe Nothing)+ (V.List xs) -> TypedValueMaybe . Just . TypeValueList <$> traverse (mapWrappedValue (TypeRef name elems)) xs+ value -> expected "list" value++mapValue :: MonadFail m => TypeName -> V.Value CONST -> ServerQ m TypeValue+mapValue name (V.List xs) = TypeValueList <$> traverse (mapValue name) xs+mapValue _ (V.Enum name) = pure $ TypeValueObject name []+mapValue name (V.Object fields) = TypeValueObject name <$> traverse (mapField name) (toList fields)+mapValue _ (V.Scalar x) = mapScalarValue x+mapValue t v = expected (show t) v++mapScalarValue :: MonadFail m => V.ScalarValue -> ServerQ m TypeValue+mapScalarValue (V.Int x) = pure $ TypeValueNumber (fromIntegral x)+mapScalarValue (V.Float x) = pure $ TypeValueNumber x+mapScalarValue (V.String x) = pure $ TypeValueString x+mapScalarValue (V.Boolean x) = pure $ TypeValueBool x+mapScalarValue (V.Value _) = fail "JSON objects are not supported!"
+ src/Data/Morpheus/CodeGen/Server/Printing/Document.hs view
@@ -0,0 +1,142 @@+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Data.Morpheus.CodeGen.Server.Printing.Document+ ( renderDocument,+ )+where++import Data.ByteString.Lazy.Char8 (ByteString)+import Data.Morpheus.CodeGen.Internal.AST (CodeGenTypeName (..))+import Data.Morpheus.CodeGen.Printer+ ( Printer (..),+ ignore,+ optional,+ renderExtension,+ renderImport,+ unpack,+ )+import Data.Morpheus.CodeGen.Server.Internal.AST+ ( GQLTypeDefinition (..),+ Kind (..),+ ModuleDefinition (..),+ ServerDeclaration (..),+ ServerDirectiveUsage (..),+ TypeKind,+ )+import Data.Text+ ( pack,+ )+import qualified Data.Text.Lazy as LT+ ( fromStrict,+ )+import Data.Text.Lazy.Encoding (encodeUtf8)+import Prettyprinter+ ( Doc,+ align,+ indent,+ line,+ pretty,+ punctuate,+ tupled,+ vsep,+ (<+>),+ )+import Relude hiding (ByteString, encodeUtf8, optional, print)++renderDocument :: String -> [ServerDeclaration] -> ByteString+renderDocument moduleName types =+ encodeUtf8 $+ LT.fromStrict $+ pack $+ show $+ renderModuleDefinition+ ModuleDefinition+ { moduleName = pack moduleName,+ imports =+ [ ("Data.Data", ["Typeable"]),+ ("Data.Morpheus.Kind", ["TYPE"]),+ ("Data.Morpheus.Types", ["*"]),+ ("Data.Morpheus", []),+ ("Data.Text", ["Text"]),+ ("GHC.Generics", ["Generic"])+ ],+ extensions =+ [ "DeriveGeneric",+ "TypeFamilies",+ "OverloadedStrings",+ "DataKinds",+ "DuplicateRecordFields"+ ],+ types+ }++renderModuleDefinition :: ModuleDefinition -> Doc n+renderModuleDefinition+ ModuleDefinition+ { extensions,+ moduleName,+ imports,+ types+ } =+ vsep (map renderExtension extensions)+ <> line+ <> line+ <> "module"+ <+> pretty moduleName+ <+> "where"+ <> line+ <> line+ <> vsep (map renderImport imports)+ <> line+ <> line+ <> either (error . show) id (renderTypes types)++type Result = Either Text++renderTypes :: [ServerDeclaration] -> Either Text (Doc ann)+renderTypes = fmap vsep . traverse render++class RenderType a where+ render :: a -> Result (Doc ann)++instance RenderType ServerDeclaration where+ render InterfaceType {} = fail "not supported"+ -- TODO: on scalar we should render user provided type+ render ScalarType {scalarTypeName} =+ pure $ "type" <+> ignore (print scalarTypeName) <+> "= Int"+ render (DataType cgType) = pure (pretty cgType)+ render (GQLTypeInstance gqlType) = pure $ renderGQLType gqlType+ render (GQLDirectiveInstance _) = fail "not supported"++renderTypeableConstraints :: [Text] -> Doc n+renderTypeableConstraints xs = tupled (map (("Typeable" <+>) . pretty) xs) <+> "=>"++defineTypeOptions :: Maybe (TypeKind, Text) -> [Doc n]+defineTypeOptions (Just (kind, tName)) = ["typeOptions _ = dropNamespaceOptions" <+> "(" <> pretty (show kind :: String) <> ")" <+> pretty (show tName :: String)]+defineTypeOptions _ = []++renderGQLType :: GQLTypeDefinition -> Doc ann+renderGQLType gql@GQLTypeDefinition {..}+ | gqlKind == Scalar = ""+ | otherwise =+ "instance"+ <> optional renderTypeableConstraints (typeParameters gqlTarget)+ <+> "GQLType"+ <+> typeHead+ <+> "where"+ <> line+ <> indent 2 (vsep (renderMethods typeHead gql <> defineTypeOptions dropNamespace))+ where+ typeHead = unpack (print gqlTarget)++renderMethods :: Doc n -> GQLTypeDefinition -> [Doc n]+renderMethods typeHead GQLTypeDefinition {..} =+ ["type KIND" <+> typeHead <+> "=" <+> pretty gqlKind]+ <> ["directives _=" <+> renderDirectiveUsages gqlTypeDirectiveUses | not (null gqlTypeDirectiveUses)]++renderDirectiveUsages :: [ServerDirectiveUsage] -> Doc n+renderDirectiveUsages = align . vsep . punctuate " <>" . map pretty
+ src/Data/Morpheus/CodeGen/Server/Printing/TH.hs view
@@ -0,0 +1,118 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE TemplateHaskellQuotes #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE NoImplicitPrelude #-}++module Data.Morpheus.CodeGen.Server.Printing.TH+ ( compileDocument,+ gqlDocument,+ )+where++import qualified Data.ByteString.Lazy.Char8 as LB+import Data.Morpheus.CodeGen.Internal.AST (CodeGenTypeName (..))+import Data.Morpheus.CodeGen.Server.Internal.AST+ ( CodeGenConfig (..),+ GQLDirectiveTypeClass (..),+ GQLTypeDefinition (..),+ InterfaceDefinition (..),+ ServerDeclaration (..),+ ServerDirectiveUsage,+ TypeKind,+ )+import Data.Morpheus.CodeGen.Server.Interpreting.Transform+ ( parseServerTypeDefinitions,+ )+import Data.Morpheus.CodeGen.TH+ ( PrintExp (..),+ PrintType (..),+ ToName (..),+ apply,+ m',+ m_,+ printDec,+ printTypeClass,+ printTypeSynonym,+ toCon,+ _',+ )+import Data.Morpheus.Server.Types+ ( GQLDirective (..),+ GQLType (..),+ TypeGuard (..),+ dropNamespaceOptions,+ )+import Data.Morpheus.Types.Internal.AST (DirectiveLocation)+import Language.Haskell.TH+import Language.Haskell.TH.Quote (QuasiQuoter (..))+import Relude hiding (ByteString, Type)++gqlDocument :: QuasiQuoter+gqlDocument = mkQuasiQuoter CodeGenConfig {namespace = False}++mkQuasiQuoter :: CodeGenConfig -> QuasiQuoter+mkQuasiQuoter ctx =+ QuasiQuoter+ { quoteExp = notHandled "Expressions",+ quotePat = notHandled "Patterns",+ quoteType = notHandled "Types",+ quoteDec = compileDocument ctx . LB.pack+ }+ where+ notHandled things =+ error $ things <> " are not supported by the GraphQL QuasiQuoter"++compileDocument :: CodeGenConfig -> LB.ByteString -> Q [Dec]+compileDocument ctx = parseServerTypeDefinitions ctx >=> printDecQ++class PrintDecQ a where+ printDecQ :: a -> Q [Dec]++instance PrintDecQ a => PrintDecQ [a] where+ printDecQ = fmap concat . traverse printDecQ++instance PrintDecQ InterfaceDefinition where+ printDecQ InterfaceDefinition {..} =+ pure [printTypeSynonym aliasName [m_] (apply ''TypeGuard [apply interfaceName [m'], apply unionName [m']])]++instance PrintDecQ GQLTypeDefinition where+ printDecQ GQLTypeDefinition {..} = do+ let params = map toName (typeParameters gqlTarget)+ associatedTypes <- fmap (pure . (''KIND,)) (printType gqlKind)+ pure <$> printTypeClass (map (''Typeable,) params) ''GQLType (printType gqlTarget) associatedTypes methods+ where+ methods =+ [ ('defaultValues, [_'], [|gqlTypeDefaultValues|]),+ ('directives, [_'], printDirectiveUsages gqlTypeDirectiveUses)+ ]+ <> map printTypeOptions (maybeToList dropNamespace)++instance PrintDecQ ServerDeclaration where+ printDecQ (InterfaceType interface) = printDecQ interface+ printDecQ ScalarType {} = pure []+ printDecQ (DataType dataType) = pure [printDec dataType]+ printDecQ (GQLTypeInstance gql) = printDecQ gql+ printDecQ (GQLDirectiveInstance dir) = printDecQ dir++instance PrintDecQ GQLDirectiveTypeClass where+ printDecQ GQLDirectiveTypeClass {..} =+ pure+ <$> printTypeClass+ []+ ''GQLDirective+ (toCon directiveTypeName)+ [(''DIRECTIVE_LOCATIONS, promotedList directiveLocations)]+ []++promotedList :: [DirectiveLocation] -> Type+promotedList = foldr (AppT . AppT PromotedConsT . PromotedT . toName) PromotedNilT++printTypeOptions :: (TypeKind, Text) -> (Name, [PatQ], ExpQ)+printTypeOptions (kind, tName) = ('typeOptions, [_'], [|dropNamespaceOptions kind tName|])++printDirectiveUsages :: [ServerDirectiveUsage] -> ExpQ+printDirectiveUsages = foldr (appE . appE [|(<>)|] . printExp) [|mempty|]