packages feed

flags-applicative 0.0.4.5 → 0.0.5.0

raw patch · 4 files changed

+58/−72 lines, 4 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- Flags.Applicative: MissingFlag :: Name -> FlagError
- Flags.Applicative: data FlagParser a
- Flags.Applicative: instance GHC.Base.Alternative Flags.Applicative.FlagParser
- Flags.Applicative: instance GHC.Base.Applicative Flags.Applicative.FlagParser
- Flags.Applicative: instance GHC.Base.Functor Flags.Applicative.FlagParser
+ Flags.Applicative: MissingFlags :: NonEmpty Name -> FlagError
+ Flags.Applicative: data FlagsParser a
+ Flags.Applicative: instance GHC.Base.Alternative Flags.Applicative.FlagsParser
+ Flags.Applicative: instance GHC.Base.Applicative Flags.Applicative.FlagsParser
+ Flags.Applicative: instance GHC.Base.Functor Flags.Applicative.FlagsParser
- Flags.Applicative: autoFlag :: Read a => Name -> Description -> FlagParser a
+ Flags.Applicative: autoFlag :: Read a => Name -> Description -> FlagsParser a
- Flags.Applicative: autoListFlag :: Read a => Text -> Name -> Description -> FlagParser [a]
+ Flags.Applicative: autoListFlag :: Read a => Text -> Name -> Description -> FlagsParser [a]
- Flags.Applicative: boolFlag :: Name -> Description -> FlagParser Bool
+ Flags.Applicative: boolFlag :: Name -> Description -> FlagsParser Bool
- Flags.Applicative: flag :: (Text -> Either String a) -> Name -> Description -> FlagParser a
+ Flags.Applicative: flag :: (Text -> Either String a) -> Name -> Description -> FlagsParser a
- Flags.Applicative: hostFlag :: Name -> Description -> FlagParser (HostName, Maybe PortNumber)
+ Flags.Applicative: hostFlag :: Name -> Description -> FlagsParser (HostName, Maybe PortNumber)
- Flags.Applicative: parseFlags :: FlagParser a -> [String] -> Either FlagError (a, [String])
+ Flags.Applicative: parseFlags :: FlagsParser a -> [String] -> Either FlagError (a, [String])
- Flags.Applicative: parseSystemFlagsOrDie :: FlagParser a -> IO (a, [String])
+ Flags.Applicative: parseSystemFlagsOrDie :: FlagsParser a -> IO (a, [String])
- Flags.Applicative: switch :: Name -> Description -> FlagParser ()
+ Flags.Applicative: switch :: Name -> Description -> FlagsParser ()
- Flags.Applicative: textFlag :: Name -> Description -> FlagParser Text
+ Flags.Applicative: textFlag :: Name -> Description -> FlagsParser Text
- Flags.Applicative: textListFlag :: Text -> Name -> Description -> FlagParser [Text]
+ Flags.Applicative: textListFlag :: Text -> Name -> Description -> FlagsParser [Text]

Files

− app/SimpleExample.hs
@@ -1,27 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--module Main where--import Control.Applicative ((<|>), optional)-import Data.Text (Text, pack)-import Flags.Applicative-import System.Environment (getArgs)--data Mode = Lenient | Strict deriving (Read, Show)--data Options = Options-  { rootPath :: Text-  , logLevel :: Int-  , mode :: Mode-  , context :: Maybe (Text, [Double])-  } deriving Show--optionsParser :: FlagParser Options-optionsParser = Options-  <$> (textFlag "root" "path to the root" <|> ("HERE" <$ switch "default_root" ""))-  <*> autoFlag "log_level" "log verbosity"-  <*> (autoFlag "mode" "mode [Lenient|Strict]" <|> pure Strict)-  <*> (optional $ (,) <$> textFlag "context" "" <*> (autoListFlag "," "values" "" <|> pure []))--main :: IO ()-main = parseSystemFlagsOrDie optionsParser >>= print
flags-applicative.cabal view
@@ -1,5 +1,5 @@ name:                flags-applicative-version:             0.0.4.5+version:             0.0.5.0 synopsis:            Applicative flag parsing description:         https://github.com/mtth/flags-applicative homepage:            https://github.com/mtth/flags-applicative@@ -36,15 +36,6 @@     , hspec >=2.6 && <2.7     , flags-applicative   default-language: Haskell2010--executable simple-example-  hs-source-dirs:      app-  main-is:             SimpleExample.hs-  build-depends:-      base >=4.8 && <5-    , text >= 1.2 && < 1.3-    , flags-applicative-  default-language:    Haskell2010  source-repository head   type:     git
src/Flags/Applicative.hs view
@@ -20,7 +20,7 @@ --   , context :: Maybe Text --   } deriving Show ----- optionsParser :: FlagParser Options+-- optionsParser :: FlagsParser Options -- optionsParser = Options \<$\> textFlag "root" "path to the root" --                         \<*\> (autoFlag "log_level" "" \<|\> pure 0) --                         \<*\> (optional $ textFlag "context" "")@@ -31,13 +31,17 @@ --   print opts -- @ -module Flags.Applicative-  ( Name, Description, FlagParser, FlagError(..)+module Flags.Applicative (+  -- * Types+  Name, Description, FlagsParser, FlagError(..),   -- * Running parsers-  , parseFlags, parseSystemFlagsOrDie-  -- * Defining flags-  , switch, boolFlag, flag, textFlag, hostFlag, autoFlag, textListFlag, autoListFlag-  ) where+  parseFlags, parseSystemFlagsOrDie,+  -- * Declaring flags+  -- ** Nullary flags+  switch, boolFlag,+  -- ** Unary flags+  flag, textFlag, hostFlag, autoFlag, textListFlag, autoListFlag+) where  import Control.Applicative ((<|>), Alternative, empty) import Control.Monad (when)@@ -92,9 +96,12 @@  -- The errors which can happen during flag parsing. data ValueError-  = MissingValue Name-  | InvalidValue Name Text String+  = InvalidValue Name Text String+  | MissingValues (NonEmpty Name) +missingValue :: Name -> ValueError+missingValue name = MissingValues $ name :| []+ type Action a = RWST   (Map Name Text) -- Flag values (or empty for nullary flags).   () -- Unused.@@ -155,7 +162,7 @@ -- form is accepted. -- -- You can run a parser using 'parseFlags' or 'parseSystemFlagsOrDie'.-data FlagParser a+data FlagsParser a   = Actionable (Action a) (Map Name Flag) Usage   | Invalid ParserError   deriving Functor@@ -167,7 +174,7 @@   Just ((name, _), _) -> Left name   Nothing -> Right $ flags1 `Map.union` flags2 -instance Applicative FlagParser where+instance Applicative FlagsParser where   pure res = Actionable (pure res) Map.empty emptyUsage    Invalid err <*> _ = Invalid err@@ -177,7 +184,7 @@       Left name -> Invalid $ Duplicate name       Right flags -> Actionable (action1 <*> action2) flags (usage1 `andAlso` usage2) -instance Alternative FlagParser where+instance Alternative FlagsParser where   empty = Invalid Empty    Invalid Empty <|> parser = parser@@ -188,14 +195,18 @@     case mergeFlags flags1 flags2 of       Left name -> Invalid $ Duplicate name       Right flags -> Actionable action flags (usage1 `orElse` usage2) where-        wrap a = catchError (Just <$> a) $ \case-          (MissingValue _) -> pure Nothing+        wrap a = catchError (Right <$> a) $ \case+          (MissingValues names) -> pure $ Left names           err -> throwError err         action = do           used <- get           wrap action1 >>= \case-            Nothing -> put used >> action2-            Just res -> do+            Left names -> do+              put used+              wrap action2 >>= \case+                Left names' -> throwError $ MissingValues $ names <> names'+                Right res -> pure res+            Right res -> do               used' <- get               _ <- wrap action2               put used'@@ -213,8 +224,8 @@   | InconsistentFlagValues Name   -- | A unary flag's value failed to parse.   | InvalidFlagValue Name Text String-  -- | A required flag was missing.-  | MissingFlag Name+  -- | A required flag was missing; at least one of the returned flags should be set.+  | MissingFlags (NonEmpty Name)   -- | A unary flag was missing a value. This can happen either if a value-less unary flag was the   -- last token or was followed by a value which is also a flag name (in which case you should use   -- the single-token form: @--flag=--value@).@@ -230,6 +241,9 @@   | UnknownFlag Name   deriving (Eq, Show) +displayFlags :: Foldable f => f Name -> Text+displayFlags = T.intercalate " " . fmap qualify . toList+ -- Pretty-print a 'FlagError'. displayFlagError :: FlagError -> Text displayFlagError (DuplicateFlag name) = qualify name <> " was declared multiple times"@@ -238,12 +252,12 @@ displayFlagError (InconsistentFlagValues name) = "inconsistent values for " <> qualify name displayFlagError (InvalidFlagValue name val msg) =   "invalid value \"" <> val <> "\" for " <> qualify name <> " (" <> T.pack msg <> ")"-displayFlagError (MissingFlag name) = qualify name <> " is required but was not set"+displayFlagError (MissingFlags names) =+  "at least one of the following required flags must be set: " <> displayFlags names displayFlagError (MissingFlagValue name) = "missing value for " <> qualify name displayFlagError (ReservedFlag name) = qualify name <> " was declared but is reserved" displayFlagError (UnexpectedFlagValue name) = "unexpected value for " <> qualify name-displayFlagError (UnexpectedFlags names) =-  "unexpected " <> (T.intercalate " " $ fmap qualify $ toList $ names)+displayFlagError (UnexpectedFlags names) = "unexpected " <> displayFlags names displayFlagError (UnknownFlag name) = "undeclared " <> qualify name  -- Mark a flag as used. This is useful to check for unexpected flags after parsing is complete.@@ -253,27 +267,27 @@ -- | Returns a parser with the given name and description for a flag with no value, failing if the -- flag is not present. See also 'boolFlag' for a variant which doesn't fail when the flag is -- missing.-switch :: Name -> Description -> FlagParser ()+switch :: Name -> Description -> FlagsParser () switch name desc = Actionable action flags usage where   action = asks (Map.member name) >>= \case     True -> useFlag name-    False -> throwError $ MissingValue name+    False -> throwError $ missingValue name   flags = Map.singleton name (Flag Nullary desc)   usage = Exactly name  -- | Returns a parser with the given name and description for a flag with no value, returning -- whether the flag was present.-boolFlag :: Name -> Description -> FlagParser Bool+boolFlag :: Name -> Description -> FlagsParser Bool boolFlag name desc = (True <$ switch name desc) <|> pure False  -- | Returns a parser using the given parsing function, name, and description for a flag with an -- associated value.-flag :: (Text -> Either String a) -> Name -> Description -> FlagParser a+flag :: (Text -> Either String a) -> Name -> Description -> FlagsParser a flag convert name desc = Actionable action flags usage where   action = do     useFlag name     asks (Map.lookup name) >>= \case-      Nothing -> throwError $ MissingValue name+      Nothing -> throwError $ missingValue name       Just val -> case convert val of         Left err -> throwError $ InvalidValue name val err         Right res -> pure res@@ -281,10 +295,11 @@   usage = Exactly name  -- | Returns a parser for a single text value.-textFlag :: Name -> Description -> FlagParser Text+textFlag :: Name -> Description -> FlagsParser Text textFlag = flag Right -hostFlag :: Name -> Description -> FlagParser (HostName, Maybe PortNumber)+-- | Returns a parser for network hosts of the form @hostname:port@. The port part is optional.+hostFlag :: Name -> Description -> FlagsParser (HostName, Maybe PortNumber) hostFlag = flag $ \txt -> do   let (hostname, suffix) = T.breakOn ":" txt   mbPort <- case T.stripPrefix ":" suffix of@@ -294,17 +309,17 @@  -- | Returns a parser for any value with a 'Read' instance. Prefer 'textFlag' for textual values -- since 'flag'  will expect its values to be double-quoted and might not work as expected.-autoFlag :: Read a => Name -> Description -> FlagParser a+autoFlag :: Read a => Name -> Description -> FlagsParser a autoFlag = flag (readEither . T.unpack)  -- | Returns a parser for a single flag with multiple text values.-textListFlag :: Text -> Name -> Description -> FlagParser [Text]+textListFlag :: Text -> Name -> Description -> FlagsParser [Text] textListFlag sep =  flag $ Right . T.splitOn sep  -- | Returns a parser for a single flag with multiple values having a 'Read' instance, with a -- configurable separator. Empty values are always ignored, so it's possible to declare an empty -- list as @--list=@ and trailing commas are supported.-autoListFlag :: Read a => Text -> Name -> Description -> FlagParser [a]+autoListFlag :: Read a => Text -> Name -> Description -> FlagsParser [a] autoListFlag sep =   flag $ sequenceA . fmap (readEither . T.unpack) . filter (not . T.null) . T.splitOn sep @@ -354,11 +369,11 @@     Right (rv, usedNames, _) ->       let unused = Set.difference (Map.keysSet values) usedNames       in Right (rv, unused, args)-    Left (MissingValue name) -> Left $ MissingFlag name+    Left (MissingValues names) -> Left $ MissingFlags names     Left (InvalidValue name val msg) -> Left $ InvalidFlagValue name val msg  -- Preprocessing parser.-reservedParser :: FlagParser (Bool, Set Name, Set Name)+reservedParser :: FlagsParser (Bool, Set Name, Set Name) reservedParser =   let textSetFlag name = Set.fromList <$> (flag (Right . T.splitOn ",") name "" <|> pure [])   in (,,)@@ -369,7 +384,7 @@ -- | Runs a parser on a list of tokens, returning the parsed flags alongside other non-flag -- arguments (i.e. which don't start with @--@). If the special @--@ token is found, all following -- tokens will be considered arguments even if they look like flags.-parseFlags :: FlagParser a -> [String] -> Either FlagError (a, [String])+parseFlags :: FlagsParser a -> [String] -> Either FlagError (a, [String]) parseFlags parser tokens = case reservedParser of   Invalid _ -> error "unreachable"   Actionable action0 flags0 _ -> do@@ -393,7 +408,7 @@  -- | Runs a parser on the system's arguments, or exits with code 1 and prints the relevant error -- message in case of failure.-parseSystemFlagsOrDie :: FlagParser a -> IO (a, [String])+parseSystemFlagsOrDie :: FlagsParser a -> IO (a, [String]) parseSystemFlagsOrDie parser = parseFlags parser <$> getArgs >>= \case   Left err -> die $ T.unpack $ displayFlagError err   Right res -> pure res
test/Spec.hs view
@@ -98,3 +98,10 @@       case res of         Left (InvalidFlagValue "host" _ _) -> pure ()         _ -> expectationFailure $ show res+    it "should report all missing required flags" $ do+      let+        parser = switch "foo" "" <|> switch "bar" ""+        res = parseFlags parser []+      case res of+        Left (MissingFlags ("foo" :| ["bar"])) -> pure ()+        _ -> expectationFailure $ show res