{- | CalamityCommands commands
This module exports the DSL and core types for using commands
-}
module CalamityCommands (
module CalamityCommands.Context,
module CalamityCommands.Dsl,
module CalamityCommands.Error,
module CalamityCommands.Handler,
module CalamityCommands.Utils,
module CalamityCommands.ParsePrefix,
module CalamityCommands.Help,
-- * Parameter parsers
module CalamityCommands.Parser,
-- * Commands
-- $commandDocs
) where
import CalamityCommands.Context
import CalamityCommands.Dsl
import CalamityCommands.Error
import CalamityCommands.Handler
import CalamityCommands.Help
import CalamityCommands.ParsePrefix
import CalamityCommands.Parser (Named, KleeneStarConcat, KleenePlusConcat, ParameterParser (..))
import CalamityCommands.Utils
{- $commandDocs
This module provides abstractions for writing declarative commands, that
support grouping, pre-invokation checks, and automatic argument parsing by
using a type level list of parameter types.
A DSL is provided in "CalamityCommands.Dsl" for constructing commands
declaratively.
You can implement 'ParameterParser' for your own types to allow them to be used in the
parameter list of commands.
A default help command is provided in "CalamityCommands.Help" which can be
added just by using 'helpCommand' inside the command declaration DSL.
Commands are parameterised over three types:
- @m@, the base monad of the command processor. This is used because all
commands and checks run in their base monad in the current implementation,
as a result, all commands will run with the monadic state remaining the
same as when they were created. In the future this design decision may be
revised to remove this constraint. For pure commands this may be
'Data.Functor.Identity.Identity' and for IO commands this may be 'IO'.
- @c@, the context that is provided to each command invokation. The default
context: 'BasicContext' stores the prefix, command, and unparsed parameters
to the command. The context in use is decided by the interpreter for
'ConstructContext'.
- @a@, the result of a command invokation, for commands performing IO actions
this is usually just @()@.
==== Examples
Make a command handler, we don't actually use the context and therefore this
handler is generic over the context used:
@
h' :: 'CommandContext' 'Data.Functor.Identity.Identity' c ('Either' 'Data.Text.Text' 'Int') => 'CommandHandler' 'Data.Functor.Identity.Identity' c ('Either' 'Data.Text.Text' 'Int')
h' = 'Data.Functor.Identity.runIdentity' . 'Polysemy.runFinal' $ do
(h, _) <- 'buildCommands' $ do
'command' \@\'[Int, Int] "add" $ \ctx a b -> 'pure' $ 'Right' (a '+' b)
'command' \@\'[Int, Int] "mul" $ \ctx a b -> 'pure' $ 'Right' (a '*' b)
'helpCommand' ('pure' . 'Left')
pure h
@
To use the commands we need to provide the interpreters for
'ConstructContext' and 'ParsePrefix', the default provided ones are being
used here: 'useBasicContext' which makes @ctx ~ 'BasicContext'@, and
@'useConstantPrefix' "!"@ which treats any input starting with @!@ as a
command.
The 'processCommands' function can then be used to parse and invoke commands,
since commands are generic over the monad they run in we use
@'Data.Functor.Identity.runIdentity' . 'Polysemy.runFinal' .
'Polysemy.embedToFinal'@ to have the commands interpreted purely.
This function 'r' takes an input string such as "!add 1 2", and then looks up
the invoked command and runs it, returning the result.
@
r :: 'Data.Text.Text' -> 'Maybe' ('Either'
('CmdInvokeFailReason' ('BasicContext' 'Data.Functor.Identity.Identity' ('Either' 'Data.Text.Text' 'Int')))
('BasicContext' 'Data.Functor.Identity.Identity' ('Either' 'Data.Text.Text' 'Int'), 'Either' 'Data.Text.Text' 'Int'))
r = 'Data.Functor.Identity.runIdentity' . 'Polysemy.runFinal' . 'Polysemy.embedToFinal' . 'useBasicContext' . 'useConstantPrefix' "!" . 'processCommands' h'
@
Then to display the result of processing the command nicely, we can use a
something like this function, which prints the result of a command if one was
invoked successfully, and prints the error nicely if not.
@
rm :: 'Data.Text.Text' -> IO ()
rm s = case r s of
Just (Right (_, Right r)) ->
print r
Just (Right (_, Left h)) ->
'Data.Text.IO.putStrLn' h
Just (Left ('CommandInvokeError' _ ('ParseError' t r))) ->
'Data.Text.IO.putStrLn' ("Parsing parameter " '<>' t '<>' " failed with reason: " '<>' r)
_ -> pure ()
@
>>> rm "!add 1 1"
2
>>> rm "!add blah 1"
Parsing parameter :Int failed with reason: 1:2:
|
1 | blah 1
| ^
unexpected 'b'
expecting '+', '-', integer, or white space
>>> rm "!help"
```
The following commands exist:
- mul :Int, :Int
- help :[Text]
- add :Int, :Int
```
>>> rm "!help add"
Help for command `add`:
```
Usage: !add :Int, :Int
This command or group has no help.
```
-}