cmdtheline-0.1.0.0: src/System/Console/CmdTheLine/Common.hs
{- Copyright © 2012, Vincent Elisha Lee Frey. All rights reserved.
- This is open source software distributed under a MIT license.
- See the file 'LICENSE' for further information.
-}
module System.Console.CmdTheLine.Common where
import Data.Default
import Data.Function ( on )
import Text.PrettyPrint ( Doc )
import qualified Data.Map as M
data Absence = Absent
| Present String
deriving ( Eq )
data OptKind = FlagKind
| OptKind
| OptVal String
deriving ( Eq )
data PosKind = PosAny
| PosN Bool Int
| PosL Bool Int
| PosR Bool Int
deriving ( Eq, Ord )
-- | Information about an argument. The following fields are exported for your
-- use.
--
-- #argName#
--
-- [@argName@] :: 'String' A name to be used in the documentation to
-- refer to the argument's value. Defaults to @\"\"@.
--
-- #argDoc#
--
-- [@argDoc@] :: 'String' A documentation string for the argument.
-- Defaults to @\"\"@.
--
-- #argSection#
--
-- [@argSection@] :: 'String' The section under which to place the argument's
-- documentation. Defaults to @\"OPTIONS\"@ for optional arguments and
-- @\"ARGUMENTS\"@ for positional arguments.
data ArgInfo = ArgInfo
{ absence :: Absence
, argDoc :: String
, argName :: String
, argSection :: String
, posKind :: PosKind
, optKind :: OptKind
, optNames :: [String]
, repeatable :: Bool
}
instance Eq ArgInfo where
ai == ai'
| isPos ai && isPos ai' = ((==) `on` posKind) ai ai'
| isOpt ai && isOpt ai' = ((==) `on` optNames) ai ai'
| otherwise = False
-- This Ord instance works for placing in 'Data.Map's, but not much else.
instance Ord ArgInfo where
compare ai ai'
| isPos ai && isPos ai' = (compare `on` posKind) ai ai'
| isOpt ai && isOpt ai' = (compare `on` optNames) ai ai'
| isOpt ai && isPos ai' = LT
| otherwise = GT
data Arg = Opt [( Int -- The position were the argument was found.
, String -- The name by which the argument was supplied.
, Maybe String -- If present, a value assigned to the argument.
)]
| Pos [String] -- A list of positional arguments
type CmdLine = M.Map ArgInfo Arg
isOpt, isPos :: ArgInfo -> Bool
isOpt ai = optNames ai /= []
isPos ai = optNames ai == []
{- |
Any 'String' argument to a 'ManBlock' constructor may contain the
following significant forms for a limited kind of meta-programing.
* $(i,text): italicizes @text@.
* $(b,text): bolds @text@.
* $(mname): evaluates to the name of the default term if there are choices
of commands, or the only term otherwise.
* $(tname): evaluates to the name of the currently evaluating term.
Additionally, text inside the content portion of an 'I' constructor may
contain one of the following significant forms.
* $(argName): evaluates to the name of the argument being documented.
-}
data ManBlock = S String -- ^ A section title.
| P String -- ^ A paragraph.
| I String String -- ^ A label-content pair. As in an argument
-- definition and its accompanying
-- documentation.
| NoBlank -- ^ Suppress the normal blank line following
-- a 'P' or an 'I'.
deriving ( Eq )
type Title = ( String, Int, String, String, String )
type Page = ( Title, [ManBlock] )
-- | Information about a 'Term'. It is recommended that 'TermInfo's be
-- created by customizing the 'Data.Default' instance, as in
--
-- > termInfo = def
-- > { termName = "caroline-no"
-- > , termDoc = "carry a line off"
-- > }
data TermInfo = TermInfo
{
-- | The name of the command or program represented by the term. Defaults to
-- @\"\"@.
termName :: String
-- | Documentation for the term. Defaults to @\"\"@.
, termDoc :: String
-- | The section under which to place the terms documentation.
-- Defaults to @\"COMMANDS\"@.
, termSection :: String
-- | The section under which to place a term's argument's
-- documentation by default. Defaults to @\"OPTIONS\"@.
, stdOptSection :: String
-- | A version string. Must be left blank for commands. Defaults to @\"\"@.
, version :: String
-- | A list of 'ManBlock's to append to the default @[ManBlock]@. Defaults
-- to @[]@.
, man :: [ManBlock]
} deriving ( Eq )
instance Default TermInfo where
def = TermInfo
{ termName = ""
, version = ""
, termDoc = ""
, termSection = "COMMANDS"
, stdOptSection = "OPTIONS"
, man = []
}
type Command = ( TermInfo, [ArgInfo] )
data EvalInfo = EvalInfo
{ term :: Command -- The chosen term for this run.
, main :: Command -- The default term.
, choices :: [Command] -- A list of command-terms.
}
data EvalKind = Simple -- The program has no commands.
| Main -- The default program is running.
| Choice -- A command has been chosen.
-- | The format to print help in.
data HelpFormat = Pager | Plain | Groff
data Fail =
-- | An arbitrary message to be printed on failure.
MsgFail Doc
-- | A message to be printed along with the usage on failure.
| UsageFail Doc
-- | A format to print the help in and an optional name of the term
-- to print help for. If 'Nothing' is supplied, help will be printed
-- for the currently evaluating term.
| HelpFail HelpFormat (Maybe String)
-- | A monad for values in the context of possibly failing with a helpful
-- message.
type Err a = Either Fail a
type Yield a = EvalInfo -> CmdLine -> Err a
-- | The underlying Applicative of the library. A @Term@ represents a value
-- in the context of being computed from the command line arguments.
data Term a = Term [ArgInfo] (Yield a)
evalKind :: EvalInfo -> EvalKind
evalKind ei
| choices ei == [] = Simple
| fst (term ei) == fst (main ei) = Main
| otherwise = Choice
descCompare :: Ord a => a -> a -> Ordering
descCompare = flip compare
splitOn sep xs = ( left, rest' )
where
rest' = if rest == [] then rest else tail rest -- Skip the 'sep'.
( left, rest ) = span (/= sep) xs