commander-cli 0.5.0.0 → 0.6.0.0
raw patch · 5 files changed
+116/−33 lines, 5 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Options.Commander: Optional :: Optionality
+ Options.Commander: Required :: Optionality
+ Options.Commander: data Env :: Optionality -> Symbol -> * -> *
+ Options.Commander: data Optionality
+ Options.Commander: env :: KnownSymbol name => (x -> ProgramT p m a) -> ProgramT (Env 'Required name x & p) m a
+ Options.Commander: envOpt :: KnownSymbol name => (Maybe x -> ProgramT p m a) -> ProgramT (Env 'Optional name x & p) m a
+ Options.Commander: envOptDef :: KnownSymbol name => x -> (x -> ProgramT p m a) -> ProgramT (Env 'Optional name x & p) m a
+ Options.Commander: instance (Options.Commander.Unrender t, GHC.TypeLits.KnownSymbol name, Options.Commander.HasProgram p) => Options.Commander.HasProgram (Options.Commander.Env 'Options.Commander.Optional name t Options.Commander.& p)
+ Options.Commander: instance (Options.Commander.Unrender t, GHC.TypeLits.KnownSymbol name, Options.Commander.HasProgram p) => Options.Commander.HasProgram (Options.Commander.Env 'Options.Commander.Required name t Options.Commander.& p)
Files
- README.md +7/−7
- Test.hs +20/−4
- app/Main.hs +3/−3
- commander-cli.cabal +1/−1
- src/Options/Commander.hs +85/−18
README.md view
@@ -10,13 +10,13 @@ ```bash usage:-task-manager help -task-manager -d <task-directory :: [Char]> edit <task-name :: [Char]> -task-manager -d <task-directory :: [Char]> open <task-name :: [Char]> -task-manager -d <task-directory :: [Char]> close <task-name :: [Char]> -task-manager -d <task-directory :: [Char]> tasks -task-manager -d <task-directory :: [Char]> priorities -task-manager -d <task-directory :: [Char]> +task-manager help+task-manager (required env: TASK_DIRECTORY :: [Char]> edit <task-name :: [Char]>+task-manager (required env: TASK_DIRECTORY :: [Char]> open <task-name :: [Char]>+task-manager (required env: TASK_DIRECTORY :: [Char]> close <task-name :: [Char]>+task-manager (required env: TASK_DIRECTORY :: [Char]> tasks+task-manager (required env: TASK_DIRECTORY :: [Char]> priorities+task-manager (required env: TASK_DIRECTORY :: [Char]> ```
Test.hs view
@@ -15,10 +15,11 @@ import Control.Monad import System.Exit import Control.Exception+import System.Environment (setEnv) import Data.Maybe main :: IO ()-main = rawTest >> argTest >> optTest >> flagTest >> bigProgTests+main = rawTest >> argTest >> optTest >> flagTest >> bigProgTests >> optDefTest >> envTest rawProg :: ProgramT Raw IO Bool rawProg = raw (pure True)@@ -74,6 +75,21 @@ optDefProg prop = optDef "Default" \o -> raw (pure (prop o)) optDefTest :: IO ()-optDefTest = parlay (== "Default") (State mempty mempty mempty)- >> parlay (== "hello") (State mempty (HashMap.fromList [("o", "hello")]) mempty) where- parlay prop state = maybe exitFailure (cond (pure ()) exitFailure) =<< runCommanderT (run (optDefProg prop)) state+optDefTest = + parlay (pure ()) exitFailure (== "Default") (State mempty mempty mempty)+ >> parlay (pure ()) exitFailure (== "hello") (State mempty (HashMap.fromList [("o", "hello")]) mempty)+ >> parlay exitFailure (pure ()) (== "Default0") (State mempty mempty mempty)+ >> parlay exitFailure (pure ()) (== "hello0") (State mempty (HashMap.fromList [("o", "hello")]) mempty)+ where+ parlay y n prop state = maybe exitFailure (cond y n) =<< runCommanderT (run (optDefProg prop)) state++envProg :: (x -> Bool) -> ProgramT (Env 'Required "ONOGOTTAFINDABIGNAME" x & Raw) IO Bool+envProg prop = env \e -> raw (pure (prop e))++envTest :: IO ()+envTest = do+ testBool =<< isNothing <$> test (envProg (== ("POOP" :: String))) (State mempty mempty mempty)+ setEnv "ONOGOTTAFINDABIGNAME" "POOP"+ testMaybeBool =<< test (envProg (== ("POOP" :: String))) (State mempty mempty mempty)+ setEnv "ONOGOTTAFINDABIGNAME" "POP"+ testMaybeBool =<< fmap not <$> test (envProg (== ("POOP" :: String))) (State mempty mempty mempty)
app/Main.hs view
@@ -13,7 +13,7 @@ import Options.Commander import Prelude import System.Directory-import System.Process+import System.Process hiding (env) import Data.List import Text.Read import System.Exit@@ -22,7 +22,7 @@ type TaskManager = Named "task-manager" & ("help" & Raw- + Opt "d" "task-directory" FilePath+ + Env 'Required "TASK_DIRECTORY" FilePath & ("edit" & TaskProgram + "open" & TaskProgram + "close" & TaskProgram@@ -35,7 +35,7 @@ type TaskProgram = Arg "task-name" String & Raw taskManager :: ProgramT TaskManager IO ()-taskManager = toplevel @"task-manager" . optDef @"d" @"task-directory" "tasks" $ \tasksFilePath -> +taskManager = toplevel @"task-manager" . env @"TASK_DIRECTORY" $ \tasksFilePath -> sub @"edit" (editTask tasksFilePath) <+> sub @"open" (newTask tasksFilePath) <+> sub @"close" (closeTask tasksFilePath)
commander-cli.cabal view
@@ -1,7 +1,7 @@ cabal-version: 2.4 name: commander-cli-version: 0.5.0.0+version: 0.6.0.0 synopsis: A command line argument/option parser library built around a monadic metaphor description: A command line argument/option parser library built around a monadic metaphor. homepage: https://github.com/SamuelSchlesinger/commander-cli
src/Options/Commander.hs view
@@ -54,6 +54,7 @@ @ usage:+file help file maybe-read \<filename :: String\> ~read file maybe-write -file \<file-to-write :: String\> @@@ -74,13 +75,13 @@ {- | To construct a 'ProgramT' (a specification of a CLI program), you can have 'arg'uments, 'opt'ions, 'raw' actions in a monad (typically IO),- 'sub'programs, 'named' programs, you can combine programs together using - '<+>', and you can generate primitive 'usage' information with 'usage'.- We also have a convenience combinator, 'toplevel', which lets you add- a name and a help command to your program using the 'usage' combinator.+ 'sub'programs, 'named' programs, 'env'ironment variables, you can combine + programs together using '<+>', and you can generate primitive 'usage'+ information with 'usage'. There are combinators for retrieving environment+ variables as well. We also have a convenience combinator, 'toplevel',+ which lets you add a name and a help command to your program using the 'usage' combinator. -}- arg, opt, optDef, raw, sub, named, flag, toplevel, (<+>), usage,- -- ** Describing CLI Programs+ arg, opt, optDef, raw, sub, named, flag, toplevel, (<+>), usage, env, envOpt, envOptDef, -- ** Run CLI Programs {- | To run a 'ProgramT' (a specification of a CLI program), you will @@ -91,7 +92,7 @@ Each 'ProgramT' has a type level description, build from these type level combinators. -}- type (&), type (+), Arg, Opt, Named, Raw, Flag,+ type (&), type (+), Arg, Opt, Named, Raw, Flag, Env, Optionality(Required, Optional), -- ** Interpreting CLI Programs {- | The 'HasProgram' class forms the backbone of this library, defining the@@ -105,6 +106,8 @@ SubProgramT, unSubProgramT, NamedProgramT, unNamedProgramT, FlagProgramT, unFlagProgramT,+ EnvProgramT'Optional, unEnvProgramT'Optional, unEnvDefault,+ EnvProgramT'Required, unEnvProgramT'Required, (:+:) ), -- ** The CommanderT Monad@@ -138,7 +141,7 @@ import GHC.TypeLits (Symbol, KnownSymbol, symbolVal) import GHC.Generics (Generic) import Numeric.Natural-import System.Environment (getArgs)+import System.Environment (getArgs, lookupEnv) import Data.Typeable (Typeable, typeRep) import qualified Data.ByteString as SBS import qualified Data.ByteString.Char8 as BS8@@ -215,6 +218,10 @@ instance Unrender Char where unrender = find (const True) +-- | The type level naming combinator, giving your program a name for the+-- sake of documentation.+data Named :: Symbol -> *+ -- | The type level argument combinator, with a 'Symbol' designating the -- name of that argument. data Arg :: Symbol -> * -> *@@ -224,23 +231,27 @@ -- documentation purposes. data Opt :: Symbol -> Symbol -> * -> * --- | The type level naming combinator, giving your program a name for the--- sake of documentation.-data Named :: Symbol -> *+-- | The type level flag combinator, taking a name as input, allowing your+-- program to take flags with the syntax @~flag@.+data Flag :: Symbol -> * --- | The type level program sequencing combinator, taking two program types--- and sequencing them one after another.-data (&) :: k -> * -> *-infixr 4 &+-- | The type level environment variable combinator, taking a name as+-- input, allowing your program to take environment variables as input+-- automatically.+data Env :: Optionality -> Symbol -> * -> * -- | The type level raw monadic program combinator, allowing a command line -- program to just do some computation. data Raw :: * --- | The type level flag combinator, taking a name as input, allowing your--- program to take flags with the syntax @~flag@.-data Flag :: Symbol -> *+-- | The type level tag for whether or not a variable is required or not.+data Optionality = Required | Optional +-- | The type level program sequencing combinator, taking two program types+-- and sequencing them one after another.+data (&) :: k -> * -> *+infixr 4 &+ -- | The type level combining combinator, taking two program types as -- input, and being interpreted as a program which attempts to run the -- first command line program and, if parsing its flags, subprograms,@@ -394,6 +405,41 @@ hoist :: (forall x. m x -> n x) -> ProgramT p m a -> ProgramT p n a invocations :: [Text] +instance (Unrender t, KnownSymbol name, HasProgram p) => HasProgram (Env 'Required name t & p) where+ newtype ProgramT (Env 'Required name t & p) m a = EnvProgramT'Required { unEnvProgramT'Required :: t -> ProgramT p m a }+ run f = Action $ \state -> do+ val <- lookupEnv (symbolVal (Proxy @name))+ case val of+ Just v ->+ case unrender (pack v) of+ Just t -> return (run (unEnvProgramT'Required f t), state) + Nothing -> return (Defeat, state)+ Nothing -> return (Defeat, state)+ hoist n (EnvProgramT'Required f) = EnvProgramT'Required (hoist n . f)+ invocations =+ [(("(required env: " <> pack (symbolVal (Proxy @name))+ <> " :: " <> pack (show (typeRep (Proxy @t)))+ <> "> ") <>)] <*> invocations @p++instance (Unrender t, KnownSymbol name, HasProgram p) => HasProgram (Env 'Optional name t & p) where+ data ProgramT (Env 'Optional name t & p) m a = EnvProgramT'Optional+ { unEnvProgramT'Optional :: Maybe t -> ProgramT p m a+ , unEnvDefault :: Maybe t }+ run f = Action $ \state -> do+ val <- lookupEnv (symbolVal (Proxy @name))+ case val of+ Just v ->+ case unrender (pack v) of+ Just t -> return (run (unEnvProgramT'Optional f t), state) + Nothing -> return (Defeat, state)+ Nothing -> return (run (unEnvProgramT'Optional f (unEnvDefault f)), state)++ hoist n (EnvProgramT'Optional f d) = EnvProgramT'Optional (hoist n . f) d+ invocations =+ [(("(optional env: " <> pack (symbolVal (Proxy @name))+ <> " :: " <> pack (show (typeRep (Proxy @t)))+ <> "> ") <>)] <*> invocations @p+ instance (Unrender t, KnownSymbol name, HasProgram p) => HasProgram (Arg name t & p) where newtype ProgramT (Arg name t & p) m a = ArgProgramT { unArgProgramT :: t -> ProgramT p m a } run f = Action $ \State{..} -> do@@ -501,6 +547,27 @@ => ProgramT p IO a -> IO (Maybe a) command prog = initialState >>= runCommanderT (run prog)++-- | Required environment variable combinator+env :: KnownSymbol name+ => (x -> ProgramT p m a)+ -> ProgramT (Env 'Required name x & p) m a+env = EnvProgramT'Required++-- | Optional environment variable combinator+envOpt :: KnownSymbol name+ => (Maybe x -> ProgramT p m a)+ -> ProgramT (Env 'Optional name x & p) m a+envOpt = flip EnvProgramT'Optional Nothing++-- | Optional environment variable combinator with default+envOptDef :: KnownSymbol name+ => x+ -> (x -> ProgramT p m a)+ -> ProgramT (Env 'Optional name x & p) m a+envOptDef x f = EnvProgramT'Optional { unEnvDefault = Just x, unEnvProgramT'Optional = \case { Just x -> f x; Nothing -> error "Violated invariant of optEnvDef" } }++-- | Environment -- | Argument combinator arg :: KnownSymbol name