packages feed

optparse-applicative (empty) → 0.0.1

raw patch · 12 files changed

+1224/−0 lines, 12 filesdep +HUnitdep +basedep +data-defaultsetup-changed

Dependencies added: HUnit, base, data-default, data-lens, data-lens-template, optparse-applicative, test-framework, test-framework-hunit, test-framework-th-prime

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2012, Paolo Capriotti++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Paolo Capriotti nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Options/Applicative.hs view
@@ -0,0 +1,32 @@+module Options.Applicative (+  -- * Applicative option parsers+  --+  -- | This is an empty module which simply re-exports all the public definitions+  -- of this package.+  --+  -- See <https://github.com/pcapriotti/optparse-applicative> for a tutorial,+  -- and a general introduction to applicative option parsers.+  --+  -- See the documentation of individual modules for more details.++  -- * Exported modules+  --+  -- | The standard @Applicative@ module is re-exported here for convenience.+  module Control.Applicative,++  -- | Parser type and low-level parsing functionality.+  module Options.Applicative.Common,++  -- | Utilities to build parsers out of basic primitives.+  module Options.Applicative.Builder,++  -- | Utilities to run parsers and display a help text.+  module Options.Applicative.Extra,+  ) where++-- reexport Applicative here for convenience+import Control.Applicative++import Options.Applicative.Common+import Options.Applicative.Builder+import Options.Applicative.Extra
+ Options/Applicative/Builder.hs view
@@ -0,0 +1,305 @@+{-# LANGUAGE TemplateHaskell, ScopedTypeVariables #-}+module Options.Applicative.Builder (+  -- * Parser builders+  --+  -- | This module contains utility functions and combinators to create parsers+  -- for individual options.+  --+  -- Each parser builder takes an option modifier, which can be specified by+  -- composing basic modifiers using '&' and 'idm' (which are just convenient+  -- synonyms for the 'Category' operations 'Control.Category.>>>' and+  -- 'Control.Category.id').+  --+  -- For example:+  --+  --+  -- > out = strOption+  -- >     ( long "output"+  -- >     & short 'o'+  -- >     & metavar "FILENAME" )+  --+  --+  -- creates a parser for an option called \"output\".+  subparser,+  argument,+  arguments,+  flag,+  switch,+  nullOption,+  strOption,+  option,++  -- * Modifiers+  short,+  long,+  help,+  value,+  metavar,+  reader,+  hide,+  multi,+  transform,+  command,+  idm,+  (&),++  -- * Readers+  --+  -- | A collection of basic option readers.+  auto,+  str,+  disabled,++  -- * Internals+  Mod,+  HasName,+  OptionFields,+  FlagFields,+  CommandFields,++  -- * Builder for `ParserInfo`+  InfoMod,+  fullDesc,+  header,+  progDesc,+  footer,+  failureCode,+  info+  ) where++import Control.Applicative+import Control.Category+import Control.Monad+import Data.Lens.Common+import Data.Lens.Template++import Options.Applicative.Common+import Options.Applicative.Types++import Prelude hiding (id, (.))++data OptionFields a = OptionFields+  { _optNames :: [OptName]+  , _optReader :: String -> Maybe a }++data FlagFields a = FlagFields+  { _flagNames :: [OptName] }++data CommandFields a = CommandFields+  { _cmdCommands :: [(String, ParserInfo a)] }++$( makeLenses [ ''OptionFields+              , ''FlagFields+              , ''CommandFields ] )++class HasName f where+  name :: OptName -> f a -> f a++instance HasName OptionFields where+  name n = modL optNames (n:)++instance HasName FlagFields where+  name n = modL flagNames (n:)++-- mod --++data Mod f r a b = Mod (f r -> f r) (Option r a -> Option r b)++optionMod :: (Option r a -> Option r b) -> Mod f r a b+optionMod = Mod id++fieldMod :: (f r -> f r) -> Mod f r a a+fieldMod f = Mod f id++instance Category (Mod f r) where+  id = Mod id id+  Mod f1 g1 . Mod f2 g2 = Mod (f1 . f2) (g1 . g2)++-- readers --++-- | Option reader based on the 'Read' type class.+auto :: Read a => String -> Maybe a+auto arg = case reads arg of+  [(r, "")] -> Just r+  _         -> Nothing++-- | String option reader.+str :: String -> Maybe String+str = Just++-- | Null option reader. All arguments will fail validation.+disabled :: String -> Maybe a+disabled = const Nothing++-- modifiers --++-- | Specify a short name for an option.+short :: HasName f => Char -> Mod f r a a+short = fieldMod . name . OptShort++-- | Specify a long name for an option.+long :: HasName f => String -> Mod f r a a+long = fieldMod . name . OptLong++-- | Specify a default value for an option.+value :: a -> Mod f r a a+value = optionMod . setL optDefault . Just++-- | Specify the help text for an option.+help :: String -> Mod f r a a+help = optionMod . setL optHelp++-- | Specify the option reader.+reader :: (String -> Maybe r) -> Mod OptionFields r a a+reader = fieldMod . setL optReader++-- | Specify the metavariable.+metavar :: String -> Mod f r a a+metavar = optionMod . setL optMetaVar++-- | Hide this option.+hide :: Mod f r a a+hide = optionMod $ optShow^=False++-- | Create a multi-valued option.+multi :: Mod f r a [a]+multi = optionMod f+  where+    f opt = mkOptGroup []+      where+        mkOptGroup xs = opt+          { _optDefault = Just xs+          , _optCont = mkCont xs }+        mkCont xs r = do+          p' <- getL optCont opt r+          x <- evalParser p'+          return $ liftOpt (mkOptGroup (x:xs))++-- | Apply a transformation to the return value of this option.+--+-- This can be used, for example, to provide a default value for+-- a required option, like:+--+-- >strOption+-- >( transform Just+-- >, value Nothing )+transform :: (a -> b) -> Mod f r a b+transform f = optionMod $ fmap f++-- | Add a command to a subparser option.+command :: String -> ParserInfo r -> Mod CommandFields r a a+command cmd pinfo = fieldMod $ cmdCommands^%=((cmd, pinfo):)++-- parsers --++-- | Base default option.+baseOpts :: OptReader a -> Option a a+baseOpts opt = Option+  { _optMain = opt+  , _optMetaVar = ""+  , _optShow = True+  , _optCont = Just . pure+  , _optHelp = ""+  , _optDefault = Nothing }++-- | Builder for a command parser. The 'command' modifier can be used to+-- specify individual commands.+subparser :: Mod CommandFields a a b -> Parser b+subparser m = liftOpt . g . baseOpts $ opt+  where+    Mod f g = m . metavar "COMMAND"+    CommandFields cmds = f (CommandFields [])+    opt = CmdReader (map fst cmds) (`lookup` cmds)++-- | Builder for an argument parser.+argument :: (String -> Maybe a) -> Mod f a a b -> Parser b+argument p (Mod _ g) = liftOpt . g . baseOpts $ ArgReader p++-- | Builder for an argument list parser. All arguments are collected and+-- returned as a list.+arguments :: (String -> Maybe a) -> Mod f a [a] b -> Parser b+arguments p m = argument p (m . multi)++-- | Builder for a flag parser.+--+-- A flag that switches from a \"default value\" to an \"active value\" when+-- encountered. For a simple boolean value, use `switch` instead.+flag :: a                         -- ^ default value+     -> a                         -- ^ active value+     -> Mod FlagFields a a b      -- ^ option modifier+     -> Parser b+flag defv actv (Mod f g) = liftOpt . g . set_default . baseOpts $ rdr+  where+    rdr = let fields = f (FlagFields [])+          in FlagReader (fields^.flagNames) actv+    set_default = optDefault ^= Just defv++-- | Builder for a boolean flag.+--+-- > switch = flag False True+switch :: Mod FlagFields Bool Bool a -> Parser a+switch = flag False True++-- | Builder for an option with a null reader. A non-trivial reader can be+-- added using the 'reader' modifier.+nullOption :: Mod OptionFields a a b -> Parser b+nullOption (Mod f g) = liftOpt . g . baseOpts $ rdr+  where+    rdr = let fields = f (OptionFields [] disabled)+          in OptReader (fields^.optNames) (fields^.optReader)++-- | Builder for an option taking a 'String' argument.+strOption :: Mod OptionFields String String a -> Parser a+strOption m = nullOption $ m . reader str++-- | Builder for an option using the 'auto' reader.+option :: Read a => Mod OptionFields a a b -> Parser b+option m = nullOption $ m . reader auto++-- | Modifier for 'ParserInfo'.+newtype InfoMod a b = InfoMod+  { applyInfoMod :: ParserInfo a -> ParserInfo b }++instance Category InfoMod where+  id = InfoMod id+  m1 . m2 = InfoMod $ applyInfoMod m1 . applyInfoMod m2++-- | Specify a full description for this parser.+fullDesc :: InfoMod a a+fullDesc = InfoMod $ infoFullDesc^=True++-- | Specify a header for this parser.+header :: String -> InfoMod a a+header s = InfoMod $ infoHeader^=s++-- | Specify a footer for this parser.+footer :: String -> InfoMod a a+footer s = InfoMod $ infoFooter^=s++-- | Specify a short program description.+progDesc :: String -> InfoMod a a+progDesc s = InfoMod $ infoProgDesc^=s++-- | Specify an exit code if a parse error occurs.+failureCode :: Int -> InfoMod a a+failureCode n = InfoMod $ infoFailureCode^=n++-- | Create a 'ParserInfo' given a 'Parser' and a modifier.+info :: Parser a -> InfoMod a a -> ParserInfo a+info parser m = applyInfoMod m base+  where+    base = ParserInfo+      { _infoParser = parser+      , _infoFullDesc = True+      , _infoHeader = ""+      , _infoProgDesc = ""+      , _infoFooter = ""+      , _infoFailureCode = 1 }++-- | Trivial option modifier.+idm :: Category hom => hom a a+idm = id++-- | Compose modifiers.+(&) :: Category hom => hom a b -> hom b c -> hom a c+(&) = flip (.)
+ Options/Applicative/Common.hs view
@@ -0,0 +1,147 @@+{-# LANGUAGE Rank2Types, PatternGuards #-}+module Options.Applicative.Common (+  -- * Option parsers+  --+  -- | A 'Parser' is composed of a list of options. Several kinds of options+  -- are supported:+  --+  --  * Flags: simple no-argument options. When a flag is encountered on the+  --  command line, its value is returned.+  --+  --  * Options: options with an argument. An option can define a /reader/,+  --  which converts its argument from String to the desired value, or throws a+  --  parse error if the argument does not validate correctly.+  --+  --  * Arguments: positional arguments, validated in the same way as option+  --  arguments.+  --+  --  * Commands. A command defines a completely independent sub-parser. When a+  --  command is encountered, the whole command line is passed to the+  --  corresponding parser.+  --+  Parser,+  liftOpt,++  -- * Program descriptions+  --+  -- A 'ParserInfo' describes a command line program, used to generate a help+  -- screen. Two help modes are supported: brief and full. In brief mode, only+  -- an option and argument summary is displayed, while in full mode each+  -- available option and command, including hidden ones, is described.+  --+  -- A basic 'ParserInfo' with default values for fields can be created using+  -- the 'info' function.+  ParserInfo(..),++  -- * Running parsers+  evalParser,+  runParser,++  -- * Low-level utilities+  mapParser,+  optionNames+  ) where++import Control.Applicative+import Data.Lens.Common+import Data.Maybe+import Data.Monoid+import Options.Applicative.Types++optionNames :: OptReader a -> [OptName]+optionNames (OptReader names _) = names+optionNames (FlagReader names _) = names+optionNames _ = []++-- | Create a parser composed of a single option.+liftOpt :: Option r a -> Parser a+liftOpt opt = ConsP (fmap const opt) (pure ())++uncons :: [a] -> Maybe (a, [a])+uncons [] = Nothing+uncons (x : xs) = Just (x, xs)++data MatchResult+  = NoMatch+  | Match (Maybe String)++instance Monoid MatchResult where+  mempty = NoMatch+  mappend m@(Match _) _ = m+  mappend _ m = m++type Matcher a = [String] -> P (a, [String])++optMatches :: OptReader a -> String -> Maybe (Matcher a)+optMatches rdr arg = case rdr of+  OptReader names f+    | Just (arg1, val) <- parsed+    , arg1 `elem` names+    -> Just $ \args -> do+         (arg', args') <- tryP . uncons $ maybeToList val ++ args+         r <- tryP $ f arg'+         return (r, args')+    | otherwise -> Nothing+  FlagReader names x+    | Just (arg1, Nothing) <- parsed+    , arg1 `elem` names+    -> Just $ \args -> return (x, args)+  ArgReader f+    | Just result <- f arg+    -> Just $ \args -> return (result, args)+  CmdReader _ f+    | Just cmdInfo <- f arg+    -> Just $ \args -> tryP $ runParser (cmdInfo^.infoParser) args+  _ -> Nothing+  where+    parsed+      | '-' : '-' : arg1 <- arg+      = case span (/= '=') arg1 of+          (_, "") -> Just (OptLong arg1, Nothing)+          (arg1', _ : rest) -> Just (OptLong arg1', Just rest)+      | '-' : arg1 <- arg+      = case arg1 of+          [] -> Nothing+          [a] -> Just (OptShort a, Nothing)+          (a : rest) -> Just (OptShort a, Just rest)+      | otherwise = Nothing++tryP :: Maybe a -> P a+tryP = maybe ParseError return++stepParser :: Parser a -> String -> [String] -> P (Parser a, [String])+stepParser (NilP _) _ _ = ParseError+stepParser (ConsP opt p) arg args+  | Just matcher <- optMatches (opt^.optMain) arg+  = do (r, args') <- matcher args+       liftOpt' <- tryP $ getL optCont opt r+       return (liftOpt' <*> p, args')+  | otherwise+  = do (p', args') <- stepParser p arg args+       return (ConsP opt p', args')++-- | Apply a 'Parser' to a command line, and return a result and leftover+-- arguments.  This function returns 'Nothing' if any parsing error occurs, or+-- if any options are missing and don't have a default value.+runParser :: Parser a -> [String] -> Maybe (a, [String])+runParser p args = case args of+  [] -> result+  (arg : argt) -> case stepParser p arg argt of+    ParseError -> result+    ParseResult (p', args') -> runParser p' args'+  where+    result = (,) <$> evalParser p <*> pure args++-- | The default value of a 'Parser'.  This function returns 'Nothing' if any+-- of the options don't have a default value.+evalParser :: Parser a -> Maybe a+evalParser (NilP r) = pure r+evalParser (ConsP opt p) = opt^.optDefault <*> evalParser p++-- | Map a polymorphic function over all the options of a parser, and collect+-- the results.+mapParser :: (forall r x . Option r x -> b)+          -> Parser a+          -> [b]+mapParser _ (NilP _) = []+mapParser f (ConsP opt p) = f opt : mapParser f p
+ Options/Applicative/Extra.hs view
@@ -0,0 +1,72 @@+module Options.Applicative.Extra (+  -- * Extra parser utilities+  --+  -- | This module contains high-level functions to run parsers.+  helper,+  execParser,+  execParserPure,+  usage,+  ParserFailure(..),+  ) where++import Data.Lens.Common+import Options.Applicative.Common+import Options.Applicative.Builder+import Options.Applicative.Help+import Options.Applicative.Utils+import Options.Applicative.Types+import System.Environment+import System.Exit+import System.IO++-- | A hidden \"helper\" option which always fails.+helper :: Parser (a -> a)+helper = nullOption+       ( long "help"+       & short 'h'+       & help "Show this help text"+       & value id+       & hide )++-- | Result after a parse error.+data ParserFailure = ParserFailure+  { errMessage :: String -> String -- ^ Function which takes the program name+                                   -- as input and returns an error message+  , errExitCode :: ExitCode        -- ^ Exit code to use for this error+  }++-- | Run a program description.+--+-- Parse command line arguments. Display help text and exit if any parse error+-- occurs.+execParser :: ParserInfo a -> IO a+execParser pinfo = do+  args <- getArgs+  case execParserPure pinfo args of+    Right a -> return a+    Left failure -> do+      progn <- getProgName+      hPutStr stderr (errMessage failure progn)+      exitWith (errExitCode failure)++-- | A pure version 'execParser'.+execParserPure :: ParserInfo a      -- ^ Description of the program to run+               -> [String]          -- ^ Program arguments+               -> Either ParserFailure a+execParserPure pinfo args =+  case runParser parser args of+    Just (a, []) -> Right a+    _ -> Left ParserFailure+      { errMessage = \progn -> parserHelpText (add_usage progn pinfo)+      , errExitCode = ExitFailure (pinfo^.infoFailureCode) }+  where+    parser = pinfo^.infoParser+    add_usage progn = modL infoHeader $ \h -> vcat [h, usage parser progn]+++-- | Generate option summary.+usage :: Parser a -> String -> String+usage p progn = foldr (<+>) ""+  [ "Usage:"+  , progn+  , briefDesc p ]
+ Options/Applicative/Help.hs view
@@ -0,0 +1,99 @@+{-# LANGUAGE PatternGuards #-}+module Options.Applicative.Help (+  cmdDesc,+  briefDesc,+  fullDesc,+  parserHelpText,+  ) where++import Data.Lens.Common+import Data.List+import Data.Maybe+import Options.Applicative.Common+import Options.Applicative.Types+import Options.Applicative.Utils++showOption :: OptName -> String+showOption (OptLong n) = "--" ++ n+showOption (OptShort n) = '-' : [n]++-- | Style for rendering an option.+data OptDescStyle = OptDescStyle+  { descSep :: String+  , descHidden :: Bool+  , descSurround :: Bool }++-- | Generate description for a single option.+optDesc :: OptDescStyle -> Option r a -> String+optDesc style opt =+  let ns = optionNames $ opt^.optMain+      mv = opt^.optMetaVar+      descs = map showOption (sort ns)+      desc' = intercalate (descSep style) descs <+> mv+      render text+        | not (opt^.optShow) && not (descHidden style)+        = ""+        | null text || not (descSurround style)+        = text+        | isJust (opt^.optDefault)+        = "[" ++ text ++ "]"+        | null (drop 1 descs)+        = text+        | otherwise+        = "(" ++ text ++ ")"+  in render desc'++-- | Generate descriptions for commands.+cmdDesc :: Parser a -> String+cmdDesc = intercalate "\n"+        . filter (not . null)+        . mapParser desc+  where+    desc opt+      | CmdReader cmds p <- opt^.optMain+      = tabulate [(cmd, d)+                 | cmd <- cmds+                 , d <- maybeToList . fmap (getL infoProgDesc) $ p cmd ]+      | otherwise+      = ""++-- | Generate a brief help text for a parser.+briefDesc :: Parser a -> String+briefDesc = foldr (<+>) "" . mapParser (optDesc style)+  where+    style = OptDescStyle+      { descSep = "|"+      , descHidden = False+      , descSurround = True }++-- | Generate a full help text for a parser.+fullDesc :: Parser a -> String+fullDesc = tabulate . catMaybes . mapParser doc+  where+    doc opt+      | null n = Nothing+      | null h = Nothing+      | otherwise = Just (n, h)+      where n = optDesc style opt+            h = opt^.optHelp+    style = OptDescStyle+      { descSep = ","+      , descHidden = True+      , descSurround = False }++-- | Generate the help text for a program.+parserHelpText :: ParserInfo a -> String+parserHelpText pinfo = unlines+   $ nn [pinfo^.infoHeader]+  ++ [ "  " ++ line | line <- nn [pinfo^.infoProgDesc] ]+  ++ [ line | desc <- nn [fullDesc p]+            , line <- ["", "Common options:", desc]+            , pinfo^.infoFullDesc ]+  ++ [ line | desc <- nn [cmdDesc p]+            , line <- ["", "Available commands:", desc]+            , pinfo^.infoFullDesc ]+  ++ [ line | footer <- nn [pinfo^.infoFooter]+            , line <- ["", footer] ]+  where+    nn = filter (not . null)+    p = pinfo^.infoParser
+ Options/Applicative/Types.hs view
@@ -0,0 +1,95 @@+{-# LANGUAGE GADTs, DeriveFunctor, TemplateHaskell #-}+module Options.Applicative.Types (+  ParserInfo(..),++  infoParser,+  infoFullDesc,+  infoProgDesc,+  infoHeader,+  infoFooter,+  infoFailureCode,++  Option(..),+  OptName(..),+  OptReader(..),+  Parser(..),+  P(..),++  optMain,+  optDefault,+  optShow,+  optHelp,+  optMetaVar,+  optCont+  ) where++import Control.Applicative+import Control.Monad+import Data.Lens.Template++-- | A full description for a runnable 'Parser' for a program.+data ParserInfo a = ParserInfo+  { _infoParser :: Parser a            -- ^ the option parser for the program+  , _infoFullDesc :: Bool              -- ^ whether the help text should contain full documentation+  , _infoProgDesc :: String            -- ^ brief parser description+  , _infoHeader :: String              -- ^ header of the full parser description+  , _infoFooter :: String              -- ^ footer of the full parser description+  , _infoFailureCode :: Int            -- ^ exit code for a parser failure+  } deriving Functor+++data OptName = OptShort !Char+             | OptLong !String+  deriving (Eq, Ord)++-- | Specification for an individual parser option.+data Option r a = Option+  { _optMain :: OptReader r               -- ^ reader for this option+  , _optDefault :: Maybe a                -- ^ default value+  , _optShow :: Bool                      -- ^ whether this flag is shown is the brief description+  , _optHelp :: String                    -- ^ help text for this option+  , _optMetaVar :: String                 -- ^ metavariable for this option+  , _optCont :: r -> Maybe (Parser a) }   -- ^ option continuation+  deriving Functor++-- | An 'OptReader' defines whether an option matches an command line argument.+data OptReader a+  = OptReader [OptName] (String -> Maybe a)             -- ^ option reader+  | FlagReader [OptName] !a                             -- ^ flag reader+  | ArgReader (String -> Maybe a)                       -- ^ argument reader+  | CmdReader [String] (String -> Maybe (ParserInfo a)) -- ^ command reader+  deriving Functor++-- | A @Parser a@ is an option parser returning a value of type 'a'.+data Parser a where+  NilP :: a -> Parser a+  ConsP :: Option r (a -> b)+        -> Parser a+        -> Parser b++instance Functor Parser where+  fmap f (NilP x) = NilP (f x)+  fmap f (ConsP opt p) = ConsP (fmap (f.) opt) p++instance Applicative Parser where+  pure = NilP+  NilP f <*> p = fmap f p+  ConsP opt p1 <*> p2 =+    ConsP (fmap uncurry opt) $ (,) <$> p1 <*> p2++data P a+  = ParseError+  | ParseResult a+  deriving Functor++instance Monad P where+  return = ParseResult+  ParseError >>= _ = ParseError+  ParseResult a >>= f = f a+  fail _ = ParseError++instance Applicative P where+  pure = return+  (<*>) = ap++$( makeLenses [''Option, ''ParserInfo] )
+ Options/Applicative/Utils.hs view
@@ -0,0 +1,32 @@+module Options.Applicative.Utils (+  (<+>),+  vcat,+  tabulate,+  pad+  ) where++import Data.List++-- | Concatenate two strings with a space in the middle.+(<+>) :: String -> String -> String+"" <+> s = s+s <+> "" = s+s1 <+> s2 = s1 ++ " " ++ s2++-- | Concatenate strings vertically with empty lines in between.+vcat :: [String] -> String+vcat = intercalate "\n\n" . filter (not . null)++tabulate' :: Int -> [(String, String)] -> String+tabulate' size table = unlines+  [ "  " ++ pad size key ++ " " ++ value+  | (key, value) <- table ]++-- | Display pairs of strings in a table.+tabulate :: [(String, String)] -> String+tabulate = tabulate' 24++-- | Pad a string to a fixed size with whitespace.+pad :: Int -> String -> String+pad size str = str ++ replicate (size - n `max` 0) ' '+  where n = length str
+ README.md view
@@ -0,0 +1,281 @@+# Applicative option parser++This package contains utilities and combinators to define command line option+parsers.++[![Continuous Integration status][status-png]][status]++## Getting started++Here is a simple example of an applicative option parser:++```haskell+data Sample = Sample+  { hello :: String+  , quiet :: Bool }++sample :: Parser Sample+sample = Sample+     <$> strOption+         ( long "hello"+         & metavar "TARGET"+         & help "Target for the greeting" )+     <*> switch+         ( long "quiet"+         & help "Whether to be quiet" )+```++The parser is built using [applicative style][applicative] starting from a set+of basic combinators. In this example, `hello` is defined as an option with a+`String` argument, while `quiet` is a boolean flag (called `switch`).++A parser can be used like this:++```haskell+greet :: Sample -> IO ()+greet (Sample h True) = putStrLn $ "Hello, " ++ h+greet _ = return ()++main :: IO ()+main = execParser opts >>= greet+  where+    opts = info (helper <*> sample)+      ( fullDesc+      & progDesc "Print a greeting for TARGET"+      & header "hello - a test for optparse-applicative" )+```++The `greet` function is the entry point of the program, while `opts` is a+complete description of the program, used when generating a help text. The+`helper` combinator takes any parser, and adds a `help` option to it (which+always fails).++The `hello` option in this example is mandatory (since it doesn't have a+default value), so running the program without any argument will display the+help text:++    hello - a test for optparse-applicative++    Usage: hello --hello TARGET [--quiet]+      Print a greeting for TARGET++    Common options:+      -h,--help                Show this help text+      --hello TARGET           Target for the greeting+      --quiet                  Whether to be quiet++containing a short usage summary, and a detailed list of options with+descriptions.++ [applicative]: http://www.soi.city.ac.uk/~ross/papers/Applicative.html++## Supported options++`optparse-applicative` supports four kinds of options: regular options, flags,+arguments and commands.++### Regular options++A **regular option** is an option which takes a single argument, parses it, and+returns a value.++A regular option can have a default value, which is used as the result if the+option is not found in the command line. An option without a default value is+considered mandatory, and produces an error when not found.++Regular options can have **long** names, or **short** (one-character) names,+which determine when the option matches and how the argument is extracted.++An option with a long name (say "output") is specified on the command line as++    --output filename.txt++or++    --output=filename.txt++while a short name option (say "o") can be specified with++    -o filename.txt++or++    -ofilename.txt++Options can have more than one name, usually one long and one short, although+you are free to create options with an arbitrary combination of long and short+names.++Regular options returning strings are the most common, and they can be created+using the `strOption` builder. For example,++```haskell+strOption+( long "output"+& short 'o'+& metavar "FILE"+& help "Write output to FILE" )+```++creates a regular option with a string argument (which can be referred to as+`FILE` in the help text and documentation), a long name "option" and a short+name "o". See below for more information on the builder syntax and modifiers.++A regular option can return an object of any type, provided you specify a+**reader** for it. A common reader is `auto`, used by the `option` builder,+which assumes a `Read` instance for the return type and uses it to parse its+argument. For example:++```haskell+lineCount :: Parser Int+lineCount = option+            ( long "lines"+            & short 'n'+            & metavar "K"+            & help "Output the last K lines" )+```++specifies a regular option with an `Int` argument. We added an explicit type+annotation here, since without it the parser would have been polymorphic in the+output type. There's usually no need to add type annotations, however, because+the type will be normally inferred from the context in which the parser is+used.++You can also create a custom reader without using the `Read` typeclass, and set+it as the reader for an option using the `reader` modifier and the `nullOption`+builder:++```haskell+data FluxCapacitor = ...++parseFluxCapacitor :: String -> Maybe FluxCapacitor++option+( long "flux-capacitor"+& reader parseFluxCapacitor )+```++### Flags++A **flag** is just like a regular option, but it doesn't take any arguments: it is+either present in the command line or not.++A flag has a default value and an **active value**. If the flag is found on the+command line, the active value is returned, otherwise the default value is+used. For example:++```haskell+data Verbosity = Normal | Verbose++flag Normal Verbose+( long "verbose"+& short 'v'+& help "Enable verbose mode"+```++is a flag parser returning a `Verbosity` value.++Simple boolean flags can be specified using the `switch` builder, like so:++```haskell+switch+( long "keep-tmp-files"+, help "Retain all intermediate temporary files" )+```++### Arguments++An **argument** parser specifies a positional command line argument.++The `argument` builder takes a reader parameter, and creates a parser which+will return the parsed value every time it is passed a command line argument+for which the reader succeeds. For example++```haskell+argument str ( metavar "FILE" )+```++creates an argument accepting any string.++Arguments are only displayed in the brief help text, so there's no need to+attach a description to them. They should manually documented in the program+description.++### Commands++A **command** can be used to specify a sub-parser to be used when a certain+string is encountered in the command line.++Commands are useful to implement command line programs with multiple functions,+each with its own set of options, and possibly some global options that apply+to all of them. Typical examples are version control systems like `git`, or+build tools like `cabal`.++A command can be created using the `subparser` builder, and commands can be+added with the `command` modifier. For example++```haskell+subparser+( command "add" (info addOptions)+    ( progDesc "Add a file to the repository" )+& command "commit") (info commitOptions)+    ( progDesc "Record changes to the repository" )+)+```++Each command takes a full `ParserInfo` structure, which will be used to extract+a description for this command when generating a help text.++Note that all the parsers appearing in a command need to have the same type.+For this reason, it is often best to use a sum type which has the same+structure as the command itself. For example, for the parser above, you would+define a type like:++```haskell+data Options = Options+  { globalOpt :: String+  , globalFlag :: Bool+  ...+  , commandOpts :: CommandOptions }++data CommandOptions+  = AddOptions { ... }+  | CommitOptions { ... }+  ...+```++# Option builders++Builders allow you to define parsers using a convenient combinator-based+syntax. Each builder takes a **modifier** as parameter, and returns a parser.++A modifier is a composition of functions which act on the option, setting+values for properties or adding features, and is used to build the option from+scratch and finally lift it to a single-option parser, which can then be+combined with other parsers using normal `Applicative` combinators.++Modifiers are instances of the `Category` typeclass, so they can be combined+using the composition operator `(.)` from `Control.Category`, but the+`Options.Applicative.Builders` module provides a convenience operator `(&)`,+which is just a specialized version of flipped composition, so that you don't+need to import the `Category` module and hide the `(.)` operator from the+`Prelude`.++See the haddock documentation for `Options.Applicative.Builder` for a full list+of builders and modifiers.++## How it works++A `Parser a` is essentially a heterogeneous list of `Option`s, implemented with+existential types.++All options are therefore known statically (i.e. before parsing, not+necessarily before runtime), and can, for example, be traversed to generate a+help text.++See [this blog post][blog] for a more detailed explanation based on a+simplified implementation.++ [status-png]: https://secure.travis-ci.org/pcapriotti/optparse-applicative.png?branch=master+ [status]: http://travis-ci.org/pcapriotti/optparse-applicative?branch=master+ [blog]: http://paolocapriotti.com/blog/2012/04/27/applicative-option-parser/
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ optparse-applicative.cabal view
@@ -0,0 +1,95 @@+name:                optparse-applicative+version:             0.0.1+synopsis:            Utilities and combinators for parsing command line options+description:+    Here is a simple example of an applicative option parser:+    .+    @+    data Sample = Sample+    &#x20; &#x7b; hello :: String+    &#x20; , quiet :: Bool &#x7d;+    .+    sample :: Parser Sample+    sample = Sample+    &#x20; \<$\> strOption+    &#x20;     ( long \"hello\"+    &#x20;     & metavar \"TARGET\"+    &#x20;     & help \"Target for the greeting\" )+    &#x20; \<*\> switch+    &#x20;     ( long \"quiet\"+    &#x20;     & help \"Whether to be quiet\" )+    @+    .+    The parser is built using applicative style starting from a set of basic+    combinators. In this example, @hello@ is defined as an 'option' with a+    @String@ argument, while @quiet@ is a boolean 'flag' (called 'switch').+    .+    A parser can be used like this:+    .+    @+    greet :: Sample -> IO ()+    greet (Sample h True) = putStrLn $ \"Hello, \" ++ h+    greet _ = return ()+    .+    main :: IO ()+    main = execParser opts \>\>= greet+    &#x20; where+    &#x20;   opts = info (helper \<*\> sample)+    &#x20;     ( fullDesc+    &#x20;     & progDesc \"Print a greeting for TARGET\"+    &#x20;     & header \"hello - a test for optparse-applicative\" )+    @+    .+    The @greet@ function is the entry point of the program, while @opts@ is a+    complete description of the program, used when generating a help text. The+    'helper' combinator takes any parser, and adds a @help@ option to it (which+    always fails).+    .+    The @hello@ option in this example is mandatory (since it doesn't have a+    default value), so running the program without any argument will display a+    help text:+    .+ >hello - a test for optparse-applicative+ >+ >Usage: hello --hello TARGET [--quiet]+ >  Print a greeting for TARGET+ >+ >Common options:+ >  -h,--help                Show this help text+ >  --hello TARGET           Target for the greeting+ >  --quiet                  Whether to be quiet+    .+    containing a short usage summary, and a detailed list of options with+    descriptions.+license:             BSD3+license-file:        LICENSE+author:              Paolo Capriotti+maintainer:          p.capriotti@gmail.com+copyright:           (c) 2012  Paolo Capriotti <p.capriotti@gmail.com>+category:            System+build-type:          Simple+cabal-version:       >= 1.8+extra-source-files:  README.md++library+  exposed-modules:     Options.Applicative,+                       Options.Applicative.Common,+                       Options.Applicative.Types,+                       Options.Applicative.Builder,+                       Options.Applicative.Utils,+                       Options.Applicative.Extra,+                       Options.Applicative.Help+  build-depends:       base == 4.*,+                       data-lens == 2.10.*,+                       data-lens-template == 2.1.*,+                       data-default == 0.4.*+test-suite tests+  type:                exitcode-stdio-1.0+  hs-source-dirs:      tests+  main-is:             Tests.hs+  build-depends:       base == 4.*,+                       HUnit == 1.2.*,+                       optparse-applicative == 0.0.*,+                       test-framework == 0.6.*,+                       test-framework-hunit == 0.2.*,+                       test-framework-th-prime == 0.0.*
+ tests/Tests.hs view
@@ -0,0 +1,34 @@+{-# LANGUAGE TemplateHaskell #-}+module Main where++import qualified Examples.Hello as Hello+import qualified Examples.Commands as Commands++import Options.Applicative.Extra+import Options.Applicative.Types+import System.Exit+import Test.HUnit+import Test.Framework.Providers.HUnit+import Test.Framework.TH.Prime++assertLeft :: Show b => Either a b -> (a -> Assertion) -> Assertion+assertLeft x f = either f err x+  where+    err b = assertFailure $ "expected Left, got " ++ show b++checkHelpText :: Show a => String -> ParserInfo a -> Assertion+checkHelpText name p = do+  let result = execParserPure p ["--help"]+  assertLeft result $ \(ParserFailure err code) -> do+    expected <- readFile $ "tests/" ++ name ++ ".err.txt"+    expected @=? err name+    ExitFailure 1 @=? code++case_hello :: Assertion+case_hello = checkHelpText "hello" Hello.opts++case_modes :: Assertion+case_modes = checkHelpText "commands" Commands.opts++main :: IO ()+main = $(defaultMainGenerator)