packages feed

valiant-cli-0.1.0.0: src/Valiant/CLI/App.hs

module Valiant.CLI.App
  ( run
  ) where

import Valiant.CLI.Command.Check (runCheck)
import Valiant.CLI.Command.Generate (GenerateOpts (..), runGenerate)
import Valiant.CLI.Command.Prepare (runPrepare)
import Valiant.CLI.Command.Types (runTypes)
import Valiant.CLI.Command.Watch (runWatch)
import Valiant.CLI.Config (AppEnv (..), resolveEnv)
import Options.Applicative
import System.Exit (ExitCode, exitWith)

-- | Entry point for the @valiant@ CLI.
run :: IO ()
run = do
  opts <- execParser parserInfo
  env <- resolveEnv (optSqlDir opts) (optCacheDir opts) (optDatabaseUrl opts) (optVerbose opts)
  code <- dispatch env (optCommand opts)
  exitWith code

-- CLI option types --------------------------------------------------------

data Opts = Opts
  { optSqlDir :: FilePath
  , optCacheDir :: FilePath
  , optDatabaseUrl :: Maybe String
  , optVerbose :: Bool
  , optCommand :: Command
  }

data PrepareOpts = PrepareOpts
  { prepInlineDirs :: [FilePath]
  -- ^ Directories to scan for inline @query "..."@ calls in @.hs@ files.
  }

data Command
  = CmdPrepare PrepareOpts
  | CmdCheck
  | CmdTypes (Maybe FilePath)
  | CmdGenerate GenerateOpts
  | CmdWatch

-- Dispatch ----------------------------------------------------------------

dispatch :: AppEnv -> Command -> IO ExitCode
dispatch env = \case
  CmdPrepare opts -> runPrepare env (prepInlineDirs opts)
  CmdCheck -> runCheck env
  CmdTypes mFile -> runTypes env mFile
  CmdGenerate opts -> runGenerate env opts
  CmdWatch -> runWatch env

-- Parsers -----------------------------------------------------------------

parserInfo :: ParserInfo Opts
parserInfo =
  info
    (optsParser <**> helper)
    ( fullDesc
        <> header "valiant — compile-time checked SQL for Haskell"
        <> progDesc "Validate .sql files against a live Postgres database and cache type metadata."
    )

optsParser :: Parser Opts
optsParser =
  Opts
    <$> strOption
      ( long "sql-dir"
          <> metavar "DIR"
          <> value "sql"
          <> showDefault
          <> help "Directory containing .sql files"
      )
    <*> strOption
      ( long "cache-dir"
          <> metavar "DIR"
          <> value ".valiant"
          <> showDefault
          <> help "Directory for cached query metadata"
      )
    <*> optional
      ( strOption
          ( long "database-url"
              <> metavar "URL"
              <> help "PostgreSQL connection URL (overrides DATABASE_URL env var)"
          )
      )
    <*> switch
      ( long "verbose"
          <> short 'v'
          <> help "Enable verbose output"
      )
    <*> commandParser

commandParser :: Parser Command
commandParser =
  subparser
    ( command "prepare"
        ( info
            ( CmdPrepare . PrepareOpts
                <$> many
                  ( strOption
                      ( long "inline"
                          <> metavar "DIR"
                          <> help "Scan .hs files in DIR for inline query \"...\" calls"
                      )
                  )
            )
            (progDesc "Prepare all .sql files (and inline queries) against the database")
        )
        <> command "check" (info (pure CmdCheck) (progDesc "Check that all cache files are current"))
        <> command
          "types"
          ( info
              ( CmdTypes
                  <$> optional (argument str (metavar "FILE" <> help "Show types for a specific .sql file"))
              )
              (progDesc "Print inferred Haskell types for queries")
          )
        <> command
          "generate"
          ( info
              ( CmdGenerate
                  <$> ( GenerateOpts
                          <$> strOption
                            ( long "module-prefix"
                                <> metavar "PREFIX"
                                <> value "Queries"
                                <> showDefault
                                <> help "Module name prefix for generated modules"
                            )
                          <*> strOption
                            ( long "output-dir"
                                <> metavar "DIR"
                                <> value "src"
                                <> showDefault
                                <> help "Output directory for generated Haskell files"
                            )
                      )
              )
              (progDesc "Generate Haskell binding modules from .sql files")
          )
        <> command "watch" (info (pure CmdWatch) (progDesc "Watch .sql files for changes and re-prepare"))
    )