scrod-0.2026.2.21: source/library/Scrod/Executable/Config.hs
{-# LANGUAGE TemplateHaskellQuotes #-}
-- | CLI configuration record, built from parsed 'Flag.Flag' values.
module Scrod.Executable.Config where
import qualified Control.Monad as Monad
import qualified Control.Monad.Catch as Exception
import Data.Sequence ((|>))
import qualified Data.Sequence as Seq
import qualified GHC.Stack as Stack
import qualified Scrod.Executable.Flag as Flag
import qualified Scrod.Executable.Format as Format
import qualified Scrod.Extra.Read as Read
import qualified Scrod.Spec as Spec
data Config = MkConfig
{ format :: Format.Format,
ghcOptions :: Seq.Seq String,
help :: Bool,
literate :: Bool,
schema :: Bool,
signature :: Bool,
version :: Bool
}
deriving (Eq, Ord, Show)
fromFlags :: (Stack.HasCallStack, Exception.MonadThrow m) => [Flag.Flag] -> m Config
fromFlags = Monad.foldM applyFlag initial
applyFlag :: (Stack.HasCallStack, Exception.MonadThrow m) => Config -> Flag.Flag -> m Config
applyFlag config flag = case flag of
Flag.Format string -> do
fmt <- Format.fromString string
pure config {format = fmt}
Flag.GhcOption string -> pure config {ghcOptions = ghcOptions config |> string}
Flag.Help maybeString -> case maybeString of
Nothing -> pure config {help = True}
Just string -> do
bool <- Read.readM string
pure config {help = bool}
Flag.Literate maybeString -> case maybeString of
Nothing -> pure config {literate = True}
Just string -> do
bool <- Read.readM string
pure config {literate = bool}
Flag.Schema maybeString -> case maybeString of
Nothing -> pure config {schema = True}
Just string -> do
bool <- Read.readM string
pure config {schema = bool}
Flag.Signature maybeString -> case maybeString of
Nothing -> pure config {signature = True}
Just string -> do
bool <- Read.readM string
pure config {signature = bool}
Flag.Version maybeString -> case maybeString of
Nothing -> pure config {version = True}
Just string -> do
bool <- Read.readM string
pure config {version = bool}
initial :: Config
initial =
MkConfig
{ format = Format.Json,
ghcOptions = Seq.empty,
help = False,
literate = False,
schema = False,
signature = False,
version = False
}
spec :: (Applicative m, Monad n) => Spec.Spec m n -> n ()
spec s = do
Spec.named s 'fromFlags $ do
Spec.it s "works with no flags" $ do
Spec.assertEq s (fromFlags []) $ Just initial
Spec.describe s "format" $ do
Spec.it s "defaults to json" $ do
Spec.assertEq s (fromFlags []) $ Just initial
Spec.it s "works with json" $ do
Spec.assertEq s (fromFlags [Flag.Format "json"]) $ Just initial
Spec.it s "works with html" $ do
Spec.assertEq s (fromFlags [Flag.Format "html"]) $ Just initial {format = Format.Html}
Spec.it s "fails with invalid format" $ do
Spec.assertEq s (fromFlags [Flag.Format "invalid"]) Nothing
Spec.describe s "literate" $ do
Spec.it s "works with nothing" $ do
Spec.assertEq s (fromFlags [Flag.Literate Nothing]) $ Just initial {literate = True}
Spec.it s "works with just false" $ do
Spec.assertEq s (fromFlags [Flag.Literate $ Just "False"]) $ Just initial
Spec.it s "works with just true" $ do
Spec.assertEq s (fromFlags [Flag.Literate $ Just "True"]) $ Just initial {literate = True}
Spec.it s "fails with just invalid" $ do
Spec.assertEq s (fromFlags [Flag.Literate $ Just "invalid"]) Nothing
Spec.describe s "schema" $ do
Spec.it s "works with nothing" $ do
Spec.assertEq s (fromFlags [Flag.Schema Nothing]) $ Just initial {schema = True}
Spec.it s "works with just false" $ do
Spec.assertEq s (fromFlags [Flag.Schema $ Just "False"]) $ Just initial
Spec.it s "works with just true" $ do
Spec.assertEq s (fromFlags [Flag.Schema $ Just "True"]) $ Just initial {schema = True}
Spec.it s "fails with just invalid" $ do
Spec.assertEq s (fromFlags [Flag.Schema $ Just "invalid"]) Nothing
Spec.describe s "signature" $ do
Spec.it s "works with nothing" $ do
Spec.assertEq s (fromFlags [Flag.Signature Nothing]) $ Just initial {signature = True}
Spec.it s "works with just false" $ do
Spec.assertEq s (fromFlags [Flag.Signature $ Just "False"]) $ Just initial
Spec.it s "works with just true" $ do
Spec.assertEq s (fromFlags [Flag.Signature $ Just "True"]) $ Just initial {signature = True}
Spec.it s "fails with just invalid" $ do
Spec.assertEq s (fromFlags [Flag.Signature $ Just "invalid"]) Nothing
Spec.describe s "help" $ do
Spec.it s "works with nothing" $ do
Spec.assertEq s (fromFlags [Flag.Help Nothing]) $ Just initial {help = True}
Spec.it s "works with just false" $ do
Spec.assertEq s (fromFlags [Flag.Help $ Just "False"]) $ Just initial
Spec.it s "works with just true" $ do
Spec.assertEq s (fromFlags [Flag.Help $ Just "True"]) $ Just initial {help = True}
Spec.it s "picks the last flag" $ do
Spec.assertEq s (fromFlags [Flag.Help $ Just "False", Flag.Help Nothing]) $ Just initial {help = True}
Spec.it s "fails with just invalid" $ do
Spec.assertEq s (fromFlags [Flag.Help $ Just "invalid"]) Nothing
Spec.describe s "ghcOptions" $ do
Spec.it s "defaults to empty" $ do
Spec.assertEq s (ghcOptions <$> fromFlags []) $ Just Seq.empty
Spec.it s "collects one option" $ do
Spec.assertEq s (ghcOptions <$> fromFlags [Flag.GhcOption "-XCPP"]) $ Just (Seq.fromList ["-XCPP"])
Spec.it s "collects multiple options in order" $ do
Spec.assertEq s (ghcOptions <$> fromFlags [Flag.GhcOption "-XCPP", Flag.GhcOption "-XGADTs"]) $ Just (Seq.fromList ["-XCPP", "-XGADTs"])