content: {- | 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 (KleenePlusConcat, KleeneStarConcat, Named, 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.
   ```
  -}
  