packages feed

spectacle-1.0.0: src/Language/Spectacle/Interaction/Options.hs

-- | Command-line interface options.
--
-- @since 1.0.0
module Language.Spectacle.Interaction.Options
  ( -- * CLI Options
    OptsCLI (OptsCLI),
    optsLogGraph,
    optsOnlyTrace,
    optsLogOutput,

    -- * CLI Parser
    execOptsCLI,
    parseOptsCLI,

    -- ** "only-trace" option
    pOnlyTrace,

    -- ** "log" option
    pLogGraph,

    -- ** "output" Option
    OutputOpt (OptStdout, OptPath),
    isStdout,
    handleFrom,
    pOutputOpt,
    pOutputPath,
  )
where

import Control.Applicative (Alternative ((<|>)), (<**>))
import Options.Applicative
  ( Parser,
    customExecParser,
    help,
    helper,
    idm,
    info,
    long,
    metavar,
    prefs,
    short,
    showHelpOnEmpty,
    strOption,
    switch,
  )
import System.IO (BufferMode (LineBuffering), Handle, IOMode (ReadWriteMode), hSetBuffering, openFile, stdout)

-- ---------------------------------------------------------------------------------------------------------------------

-- | 'OptsCLI' is a record command-line options for configuring the model checker.
--
-- @since 1.0.0
data OptsCLI = OptsCLI
  { -- | Should the state diagram be drawn?
    optsLogGraph :: Bool
  , -- | Should the model checker only trace the states of a specification, without checking temporal properties?
    optsOnlyTrace :: Bool
  , -- | The output path for logs produced by CLI.
    optsLogOutput :: OutputOpt
  }
  deriving (Eq, Show)

-- | 'execOptsCLI' runs the command-line options parser.
--
-- @since 1.0.0
execOptsCLI :: IO OptsCLI
execOptsCLI =
  let option = info (parseOptsCLI <**> helper) idm
      config = prefs showHelpOnEmpty
   in customExecParser config option

-- | 'parseOptsCLI' is the parses command-line options into an 'OptsCLI'.
--
-- @since 1.0.0
parseOptsCLI :: Parser OptsCLI
parseOptsCLI =
  OptsCLI
    <$> pLogGraph
    <*> pOnlyTrace
    <*> pOutputOpt

-- ---------------------------------------------------------------------------------------------------------------------

-- | CLI parser that consumes the "only-trace" flag.
--
-- @since 1.0.0
pOnlyTrace :: Parser Bool
pOnlyTrace =
  switch
    ( long "only-trace"
        <> short 't'
        <> help "Disable property checking and only trace a specification"
    )

-- ---------------------------------------------------------------------------------------------------------------------

-- | CLI parser that consumes the "log" flag.
--
-- @since 1.0.0
pLogGraph :: Parser Bool
pLogGraph =
  switch
    ( long "log"
        <> short 'l'
        <> help "Graph the model checker trace"
    )

-- ---------------------------------------------------------------------------------------------------------------------

-- | CLI option datatype holding either a filepath to emit model checker logs to.
--
-- * @'OutputPath' str@ is a filepath to write logs to.
-- * 'OutputStdout' represents stdout as the chosen output location.
--
-- @since 1.0.0
data OutputOpt
  = OptStdout
  | OptPath FilePath
  deriving (Eq, Show)

-- | Is the output buffer stdout?
--
-- @since 1.0.0
isStdout :: OutputOpt -> Bool
isStdout opt = opt == OptStdout

-- | @'handleFrom' opt@ will extract the file hand from the given 'OutputOpt' @opt@.
--
-- @since 1.0.0
handleFrom :: OutputOpt -> IO Handle
handleFrom opt = do
  handle <- case opt of
    OptStdout -> pure stdout
    OptPath fp -> openFile fp ReadWriteMode

  hSetBuffering handle LineBuffering
  pure handle

-- | CLI parser that consumes the result of 'pOutputPath' if an output path is provided, otherwise 'OutputStd' is
-- returned by default and logs will be written to stdout.
--
-- @since 1.0.0
pOutputOpt :: Parser OutputOpt
pOutputOpt = pOutputPath <|> pure OptStdout

-- | CLI parser that consumes a filepath to emit logs to.
--
-- @since 1.0.0
pOutputPath :: Parser OutputOpt
pOutputPath = OptPath <$> parser
  where
    parser =
      strOption
        ( long "output"
            <> short 'o'
            <> metavar "OUTPUT"
            <> help "The log output path"
        )