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

Safe HaskellNone
LanguageHaskell2010

System.REPL.Ask

Contents

Description

Asking the user for input on the console.

The main type is Asker, which takes care of parsing and verifying user input.

Synopsis

Types

type PromptMsg = Text Source

A prompt.

type TypeError = SomeException Source

An error message indicating that a value wasn't able to be parsed.

type PredicateError = SomeException Source

An error message indicating that a value failied a predicate.

type Predicate m a b = a -> m (Either PredicateError b) Source

A predicate which a value has to fulfil.

type Predicate' m a = Predicate m a a Source

A predicate which does not change the type of its input.

type Parser a = Text -> Either TypeError a Source

A parser which either returns a parsed value or an error message.

data Asker m a b Source

The description of an 'ask for user input'-action. The type parameters are the used monad (typically IO or ExceptT), the type of the read value and the type of the error that is thrown in case of failures.

The components are a prompt, a parser, and a predicate that the parsed value must fulfil. The the predicate

  • is monadic and
  • can change the returned type (useful for adjoining additional information)

Constructors

Asker 

Fields

askerPrompt :: Text

The prompt to be displayed to the user.

askerParser :: Parser a

The parser for the input value.

askerPredicate :: Predicate m a b

The predicate which the input, once read, must fulfill. The Left side is an error message.

type Asker' m a = Asker m a a Source

An Asker which does not convert its argument into different type after parsing.

Exceptions

data SomeREPLError Source

Root of the exception hierarchy.

Constructors

forall e . Exception e => SomeREPLError e 

data SomeAskerError Source

Generic error related to Askers. Either the input was incorrect in some way, or the process was aborted by the user.

Constructors

forall e . Exception e => SomeAskerError e 

data AskerTypeError Source

The input wasn't able to be parsed.

data GenericTypeError Source

A generic type failure for use with Askers.

Constructors

GenericTypeError Text 

genericTypeError :: Text -> SomeException Source

Constructor for GenericTypeError which wraps the value into a SomeException.

genericPredicateError :: Text -> SomeException Source

Constructor for GenericTypeError which wraps the value into a SomeException.

Creating askers

These are all just convenience functions. You can also create Askers directly via the constructor.

For errors, you can supply a custom exception or use GenericTypeError, GenericPredicateError.

typeAskerP :: Applicative m => PromptMsg -> Parser a -> Asker' m a Source

Creates an Asker which only cares about the type of the input.

maybeAskerP :: Applicative m => PromptMsg -> Parser a -> Predicate m a b -> Asker m (Maybe a) (Maybe b) Source

An asker which asks for an optional value. If only whitespace is entered (according to isSpace), it returns Nothing without further parsing or checking; otherwise, it behaves identically to asker.

Creating askers via Read

These askers use readMaybe as their parser.

It is possible to ask for Strings, but then quotes will be required around them (per their Read-instance). To get the user's input as-is, use the Verbatim type or predAsker.

newtype Verbatim Source

A verbatim Text whose Read instance simply returns the read string, as-is. This is useful for askers which ask for strings without quotes.

Constructors

Verbatim 

Fields

fromVerbatim :: Text
 

Instances

Read Verbatim Source

Read-instance for Verbatim. Wraps the given value into quotes and reads it a a Text.

readParser :: Read a => (Text -> TypeError) -> Parser a Source

A parser based on readMaybe. This suffices for the parsing of most data types.

asker :: (Functor m, Read a) => PromptMsg -> (Text -> TypeError) -> Predicate' m a -> Asker' m a Source

Creates a general Asker with readMaybe as its parser. Using readMaybe is perfectly fine for most values, keep in mind that the input Text has to be unpacked into a string. This can be costly on very large inputs.

NOTE: Instances of String/Text have to be surrounded with quotes ("). You practically never want this when asking for input. If you want to get the user input as-is, restrict the return type to Asker m Verbatim or use 'predAsker'/'lineAsker'.

lineAsker :: Applicative m => Asker' m Text Source

A wrapper aroung getLine. Prints no prompt and returns the user input as-is.

typeAsker :: (Applicative m, Read a) => PromptMsg -> (Text -> TypeError) -> Asker' m a Source

Creates an Asker based on Read which just cares about the type of the input.

predAsker :: Functor m => PromptMsg -> Predicate m Text b -> Asker m Text b Source

Creates an Asker which takes its input verbatim as Text. Quotes around the input are not required. The input thus only has to pass a predicate, not any parsing.

maybeAsker :: (Applicative m, Read a) => PromptMsg -> (Text -> TypeError) -> Predicate' m a -> Asker' m (Maybe a) Source

An asker based on Read which asks for an optional value.

Running askers

Created askers can be run via these functions. Since the parsing depends on the Read-instance, the expected result type must be explicitly given. E.g.:

  intAsker :: Asker IO Int
  intAsker = typeAsker "> " "Expected Int!"

or, for polymorphic askers,

  genericAsk :: Read a => Asker IO a
  genericAsk = typeAsker "> " "Couldn't parse value!"
  ...
  do (x :: Int) <- genericAsk
     (y :: Int) <- genericAsk
     putStrLn $ "The sum is: " ++ show (x+y)

ask :: (MonadIO m, MonadCatch m) => Asker m a b -> Maybe Text -> m b Source

Executes an Asker. A SomeAskerError is thrown if the inpout can't be parsing into a value of the correct type, if the input fails the Asker's predicate, or if the escape key is pressed.

ask' :: (MonadIO m, MonadCatch m) => Asker m a b -> m b Source

See ask. Always reads the input from stdin.

ask' a = ask a Nothing

askEither :: (MonadIO m, MonadCatch m) => Asker m a b -> Maybe Text -> m (Either SomeAskerError b) Source

Executes an Asker. If the Text argument is Nothing, the user is asked to enter a line on stdin. If it is Just x, x is taken to be input.

Pressing the escape key returns a AskerInputAborterError (if supported).

untilValid :: forall m a. (MonadIO m, MonadCatch m, Read a) => m a -> m a Source

Repeatedly executes an ask action until the user enters a valid value. Error messages are printed each time.

Creating predicates

boolPredicate :: Functor m => (a -> m Bool) -> (a -> PredicateError) -> Predicate' m a Source

Creates a predicate from a boolean function and an error message.

Example askers

A few askers for convenience.

data PathIsNotWritable Source

Indicatres that the last existing portion of a path is not writable.

filepathAsker :: MonadIO m => PromptMsg -> (FilePath -> TypeError) -> Predicate m (PathExistenceType, FilePath) b -> Asker m FilePath b Source

Asks the user for a file or a directory.

Parsing checks for basic validity via isValid. Invalid paths are rejected.

After that, the asker determines whether the target exists and what type it has. You can run a predicate on that information.

writablefilepathAsker :: MonadIO m => PromptMsg -> (FilePath -> TypeError) -> Predicate m (PathExistenceType, FilePath) b -> Asker m FilePath b Source

See filepathAsker. This Asker also ensures that the given path is writeable in the following sense:

  • at least some initial part of the path exists and
  • the last existing part of the path is writeable.

PathRootDoesNotExist and PathIsNotWritable exceptions are thrown if the first or second of these conditions is violated.

For relative paths, we only check that the current directory is writable.

Handled exceptions: