{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE NoImplicitPrelude #-} module Main ( main, ) where import qualified Data.ByteString.Lazy as L ( readFile, writeFile, ) import Data.ByteString.Lazy (ByteString) import Data.Char import Data.Morpheus.CodeGen ( CodeGenConfig (..), PrinterConfig (..), parseServerTypeDefinitions, printServerTypeDefinitions, ) import Data.Morpheus.Internal.Ext ( GQLResult, Result (..), ) import Data.Version (showVersion) import Options.Applicative ( Parser, ReadM, command, customExecParser, eitherReader, fullDesc, help, helper, info, long, metavar, option, prefs, progDesc, short, showHelpOnError, strArgument, subparser, switch, ) import qualified Options.Applicative as OA import qualified Paths_morpheus_graphql_code_gen as CLI import Relude hiding (ByteString) import System.FilePath.Posix ( (), dropExtensions, makeRelative, normalise, replaceExtensions, splitDirectories, splitFileName, ) currentVersion :: String currentVersion = showVersion CLI.version main :: IO () 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 processFile :: FilePath -> FilePath -> IO () processFile root path = print (path, hsPath) >> L.readFile path >>= saveDocument hsPath . fmap (printServerTypeDefinitions PrinterConfig {moduleName}) . parseServerTypeDefinitions CodeGenConfig {namespace = False} where hsPath = processFileName path moduleName = intercalate "." $ splitDirectories $ dropExtensions $ makeRelative root hsPath processFileName :: FilePath -> FilePath processFileName = (\(x, y) -> x replaceExtensions (capitalize y) "hs") . splitFileName . normalise capitalize :: String -> String capitalize [] = [] capitalize (x : xs) = toUpper x : xs saveDocument :: FilePath -> GQLResult ByteString -> IO () saveDocument _ (Failure errors) = print errors saveDocument output Success {result} = L.writeFile output result data Operation = Build {source :: [FilePath]} | About deriving (Show) data App = App { operations :: Operation, options :: Options } deriving (Show) data Options = Options { version :: Bool, root :: String } deriving (Show) defaultParser :: IO App defaultParser = customExecParser (prefs showHelpOnError) (info (helper <*> parseApp) description) parseApp :: OA.Parser App parseApp = App <$> commandParser <*> parseOptions parseOptions :: Parser Options parseOptions = Options <$> switch (long "version" <> short 'v' <> help "show Version number") <*> option readOutput (long "root" <> short 'r' <> help "Root directory of the Haskell project") readOutput :: ReadM String readOutput = eitherReader Right description :: OA.InfoMod a description = fullDesc <> progDesc "Morpheus GraphQL CLI - haskell Api Generator" commandParser :: Parser Operation commandParser = buildOperation [ ( "build", "builds Haskell API from GraphQL schema", Build <$> readFiles ), ( "about", "api information", pure About ) ] buildOperation :: [(String, String, Parser Operation)] -> Parser Operation buildOperation xs = joinParsers $ map parseOperation xs joinParsers :: [OA.Mod OA.CommandFields a] -> Parser a joinParsers xs = subparser $ mconcat xs parseOperation :: (String, String, Parser Operation) -> OA.Mod OA.CommandFields Operation parseOperation (bName, bDesc, bValue) = command bName (info (helper <*> bValue) (fullDesc <> progDesc bDesc)) readFiles :: Parser [String] readFiles = (many . strArgument . mconcat) [ metavar "file", help "source files for generating api" ]