repl-toolkit-0.5.0.0: Toolkit for quickly whipping up config files and command-line interfaces.

Safe HaskellNone
LanguageHaskell2010

System.REPL.Command

Contents

Description

Provides Commands for REPLs. Commands are there to provide high-level handling of user input and to offer functionality in a standard, composable way.

Use cases:

  1. Asking the user for input that has to be of the right format or fulfil specific constraints, such as asking for an Int between 0 and 10, or asking for a username that has to exist in some database.
  2. Creating command hierarchies, e.g.
>>> myVersionControl commit "my first commit" only-binaries

In this example, we could have commit as a root command that consumes the commit name "my first commit" and hands over to the sub-command only-binaries.

  1. Creating a REPL out of individual commands. Let us say that we have a version control system with commit, revert, diff commands, plus an exit command like "exit", and one that prints some "command not recognized" message if the user's input doesn't match any commands. We can compose these commands with makeREPL, providing robust command selection and obviating the need for manually writing the input handling loop.

Commands can take their input in one line, e.g.

>>> :cmd param1 "param2 with spaces" param3

or they can ask for missing parameters if so configured:

>>> :cmd param1 "param2 with spaces"
Please enter param3:

Synopsis

Command class

data Command m i a Source

A REPL command, possibly with parameters.

Constructors

Command 

Fields

commandName :: Text

The short name of the command. Purely informative.

commandTest :: i -> Bool

Returns whether the first part of an input (the command name) matches a the command. The simplest form is ((==) . getPart) s for some string s, but more liberal matchings are possible.

commandDesc :: Text

A description of the command.

runPartialCommand :: [i] -> m (a, [i])

Runs the command with the input text as parameter, returning the unconsumed input.

Instances

Functor m => Functor (Command m i) 
(Functor m, Monad m) => Apply (Command m i) 
(Functor m, Monad m) => Bind (Command m i) 

runCommand :: (Functor m, Monad m, MonadThrow m) => Command m Text a -> Text -> m a Source

Runs the command with the input text as parameter, discarding any left-over input.

runSingleCommand :: (MonadThrow m, Functor m) => Command m Text a -> Text -> m a Source

Runs the command with the input text as parameter. If any input is left unconsumed, an error is thrown.

oneOf Source

Arguments

:: Monoid i 
=> Text

Command name.

-> Text

Command description.

-> [Command m i a] 
-> Command m i a 

Takes a list xs and executes the first command in a list whose commandTest matches the input.

Note that the resultant command cs runPartialCommand should only be executed with an input t if 'commandTest c t' == True', where t' is either head (readArgs t) or mempty if t is empty. Otherwise, the result is undefined.

subcommand Source

Arguments

:: (Functor m, Monad m, Monoid i) 
=> Command m i a

The root command.

-> [a -> Command m i b]

The subcommands that may follow it. This list must be finite.

-> Command m i b 

Adds a list of possible subcommands after a command (that should leave some input unconsumed). Ignoring all the required parameters for a moment,

subcommand x xs = x >>- oneOf xs

makeREPL Source

Arguments

:: (Functor m, MonadIO m, MonadCatch m, Functor f, Foldable f) 
=> [Command m Text a]

The regular commands.

-> Command m Text b

The "exit" command which terminates the loop.

-> Command m Text c

The command that is called when none of the others match. This one's commandTest is replaced with const True.

-> m Text

The asker to execute before each command (i.e. the prompt).

-> f (Handler m ())

Handlers for any exceptions that may arise. Generally, you will want to handle at least the exceptions of this module (SomeCommandError, MalformedParamsError, TooManyParamsError, TooFewParamsError), and whatever the Asker can throw.

-> m ()

Asks the user repeatedly for input, until the input matches the command test of the "exit" command.

Runs a REPL based on a set of commands. For a line of input, the commands are tried in following order:

  • the "exit" command,
  • all regular commands, and then
  • the "unknown" command.

Exceptions

These are the exceptions that can be thrown during the course of command invocation (in addition to those that you throw yourself, of course).

SomeCommandError is an abstract exception and all others are its concrete subclasses. See the example in Control.Exception for details.

data SomeCommandError Source

Generic error related to command execution.

Constructors

forall e . Exception e => SomeCommandError e 

data MalformedParamsError Source

The input of a command was malformed and could not interpreted. I.e. the input contained inadmissible characters, or quotes were mismatched. The Text argument contains the parser error.

data TooFewParamsError Source

Too few parameters were given to a command. The first value is the minium, the second the actual number.

Constructors

TooFewParamsError Int Int 

data TooManyParamsError Source

Too many parameters were given to a command. The first value is the maximum, the second the actual number.

Constructors

TooManyParamsError Int Int 

Dealing with arguments

readArgs :: MonadThrow m => Text -> m [Text] Source

Splits and trims the input of a command. If the input cannot be parsed, a MalformedCommand exception is thrown.

  • - * Format

Any non-whitespace sequence of characters is interpreted as one argument, unless double quotes (") are used, in which case they demarcate an argument. Each argument is parsed as a haskell string literal (quote-less arguments have quotes inserted around them).

Arguments are parsed using parsec's stringLiteral (haskell-style), meaning that escape sequences and unicode characters are handled automatically.

getName :: (Functor m, MonadThrow m) => Text -> m Text Source

Gets the first part of a command string, or the empty string, if the comand string is empty.

quoteArg :: Text -> Text Source

Surrounds an argument in quote marks, if necessary. This is useful when arguments were extracted via readArgs, which deletes quote marks. Quotes are placed around the input iff it is empty or contains whitespace.

Helpers

summarizeCommands :: MonadIO m => [Command m2 i z] -> m () Source

Prints out a list of command names, with their descriptions.

Making commands

makeCommand Source

Arguments

:: (MonadIO m, MonadCatch m, Functor m, Monoid i) 
=> Text

Command name.

-> (i -> Bool)

Command test.

-> Text

Command description.

-> (i -> m z)

Command function. It will receive the first part of the input (customarily the command name), or the empty string if the input only contained whitespace.

-> Command m i z 

Creates a command without parameters.

makeCommand1 Source

Arguments

:: (MonadIO m, MonadCatch m, Functor m) 
=> Text

Command name.

-> (Text -> Bool)

Command test.

-> Text

Command description

-> Bool

Whether the command can ask for input. ^If True, running the command will run the Asker's IO action if not enough input is provided. If False a ParamNumError will be thrown.

-> Asker m a

Asker for the first parameter.

-> (Text -> a -> m z)

Command function.

-> Command m Text z 

Creates a command with one parameter.

makeCommand2 Source

Arguments

:: (MonadIO m, MonadCatch m, Functor m) 
=> Text

Command name.

-> (Text -> Bool)

Command test.

-> Text

Command description

-> Bool

Whether the command can ask for input.

-> Asker m a

Asker for the first parameter.

-> Asker m b

Asker for the second parameter.

-> (Text -> a -> b -> m z)

Command function.

-> Command m Text z 

Creates a command with two parameters.

makeCommand3 Source

Arguments

:: (MonadIO m, MonadCatch m, Functor m) 
=> Text

Command name.

-> (Text -> Bool)

Command test.

-> Text

Command description

-> Bool

Whether the command can ask for input.

-> Asker m a

Asker for the first parameter.

-> Asker m b

Asker for the second parameter.

-> Asker m c

Asker for the third parameter.

-> (Text -> a -> b -> c -> m z)

Command function.

-> Command m Text z 

Creates a command with three parameters.

makeCommand4 Source

Arguments

:: (MonadIO m, MonadCatch m, Functor m) 
=> Text

Command name.

-> (Text -> Bool)

Command test.

-> Text

Command description

-> Bool

Whether the command can ask for input.

-> Asker m a

Asker for the first parameter.

-> Asker m b

Asker for the second parameter.

-> Asker m c

Asker for the third parameter.

-> Asker m d

Asker for the fourth parameter.

-> (Text -> a -> b -> c -> d -> m z)

Command function.

-> Command m Text z 

Creates a command with four parameters.

makeCommand5 Source

Arguments

:: (MonadIO m, MonadCatch m, Functor m) 
=> Text

Command name.

-> (Text -> Bool)

Command test.

-> Text

Command description

-> Bool

Whether the command can ask for input.

-> Asker m a

Asker for the first parameter.

-> Asker m b

Asker for the second parameter.

-> Asker m c

Asker for the third parameter.

-> Asker m d

Asker for the fourth parameter.

-> Asker m e

Asker for the fifth parameter.

-> (Text -> a -> b -> c -> d -> e -> m z)

Command function.

-> Command m Text z 

Creates a command with five parameters.

makeCommand6 Source

Arguments

:: (MonadIO m, MonadCatch m, Functor m) 
=> Text

Command name.

-> (Text -> Bool)

Command test.

-> Text

Command description

-> Bool

Whether the command can ask for input.

-> Asker m a

Asker for the first parameter.

-> Asker m b

Asker for the second parameter.

-> Asker m c

Asker for the third parameter.

-> Asker m d

Asker for the fourth parameter.

-> Asker m e

Asker for the fifth parameter.

-> Asker m f

Asker for the sixth parameter.

-> (Text -> a -> b -> c -> d -> e -> f -> m z)

Command function.

-> Command m Text z 

Creates a command with six parameters.

makeCommand7 Source

Arguments

:: (MonadIO m, MonadCatch m, Functor m) 
=> Text

Command name.

-> (Text -> Bool)

Command test.

-> Text

Command description

-> Bool

Whether the command can ask for input.

-> Asker m a

Asker for the first parameter.

-> Asker m b

Asker for the second parameter.

-> Asker m c

Asker for the third parameter.

-> Asker m d

Asker for the fourth parameter.

-> Asker m e

Asker for the fifth parameter.

-> Asker m f

Asker for the sixth parameter.

-> Asker m g

Asker for the seventh parameter.

-> (Text -> a -> b -> c -> d -> e -> f -> g -> m z)

Command function.

-> Command m Text z 

Creates a command with seven parameters.

makeCommand8 Source

Arguments

:: (MonadIO m, MonadCatch m, Functor m) 
=> Text

Command name.

-> (Text -> Bool)

Command test.

-> Text

Command description

-> Bool

Whether the command can ask for input.

-> Asker m a

Asker for the first parameter.

-> Asker m b

Asker for the second parameter.

-> Asker m c

Asker for the third parameter.

-> Asker m d

Asker for the fourth parameter.

-> Asker m e

Asker for the fifth parameter.

-> Asker m f

Asker for the sixth parameter.

-> Asker m g

Asker for the seventh parameter.

-> Asker m h

Asker for the eighth parameter.

-> (Text -> a -> b -> c -> d -> e -> f -> g -> h -> m z)

Command function.

-> Command m Text z 

Creates a command with eight parameters.

makeCommandN Source

Arguments

:: (MonadIO m, MonadCatch m, Functor m) 
=> Text

Command name.

-> (Text -> Bool)

Command test.

-> Text

Command description

-> Bool

Whether the command can ask for input. This only affects the necessary parameters.

-> [Asker m a]

Askers for the necessary parameters.

-> [Asker m a]

Askers for the optional parameters.

-> (Text -> [a] -> m z) 
-> Command m Text z 

Creates a command with a list of parameters. The first list necc of Askers indicates the necessary parameters; the user must at least provide this many. The second list opt contains Askers for additional, optional parameters, and may be infinite. If the number of passed parameters exceeds length necc + length opt, or if any Asker fails, the command returns an AskFailure.