repl-toolkit-1.1.0.0: 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 failed 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 predicate

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

Constructors

Asker 

Fields

type Asker' m a = Asker m a a Source #

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

Exceptions

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

Exception e => SomeAskerError e 

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

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 around 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 PathExistenceType Source #

Indicates whether the target of a path exists and what form it has.

Instances

Bounded PathExistenceType Source # 
Enum PathExistenceType Source # 
Eq PathExistenceType Source # 
Ord PathExistenceType Source # 
Read PathExistenceType Source # 
Show PathExistenceType Source # 

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: