packages feed

optparse-generic 1.1.5 → 1.2.0

raw patch · 2 files changed

+184/−46 lines, 2 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Options.Generic: [shortNameModifier] :: Modifiers -> String -> Maybe Char
+ Options.Generic: firstLetter :: String -> Maybe Char
+ Options.Generic: getWithHelp :: (MonadIO io, ParseRecord a) => Text -> io (a, io ())
+ Options.Generic: unwrapWithHelp :: (MonadIO io, ParseRecord (f Wrapped), Unwrappable f) => Text -> io (f Unwrapped, io ())
- Options.Generic: Modifiers :: (String -> String) -> (String -> String) -> Modifiers
+ Options.Generic: Modifiers :: (String -> String) -> (String -> String) -> (String -> Maybe Char) -> Modifiers
- Options.Generic: class ParseField a where parseField h m = do { let metavar = map toUpper (show (typeOf (undefined :: a))); case m of { Nothing -> do { let fs = metavar metavar <> maybe mempty (help . unpack) h; argument auto fs } Just name -> do { let fs = metavar metavar <> long (unpack name) <> maybe mempty (help . unpack) h; option auto fs } } } parseListOfField h m = many (parseField h m)
+ Options.Generic: class ParseField a where parseField h m c = do { let metavar = map toUpper (show (typeOf (undefined :: a))); case m of { Nothing -> do { let fs = metavar metavar <> foldMap (help . unpack) h; argument auto fs } Just name -> do { let fs = metavar metavar <> long (unpack name) <> foldMap (help . unpack) h <> foldMap short c; option auto fs } } } parseListOfField h m c = many (parseField h m c)
- Options.Generic: parseField :: (ParseField a, Typeable a, Read a) => Maybe Text -> Maybe Text -> Parser a
+ Options.Generic: parseField :: (ParseField a, Typeable a, Read a) => Maybe Text -> Maybe Text -> Maybe Char -> Parser a
- Options.Generic: parseFields :: (ParseFields a, ParseField a) => Maybe Text -> Maybe Text -> Parser a
+ Options.Generic: parseFields :: (ParseFields a, ParseField a) => Maybe Text -> Maybe Text -> Maybe Char -> Parser a
- Options.Generic: parseListOfField :: ParseField a => Maybe Text -> Maybe Text -> Parser [a]
+ Options.Generic: parseListOfField :: ParseField a => Maybe Text -> Maybe Text -> Maybe Char -> Parser [a]

Files

optparse-generic.cabal view
@@ -1,5 +1,5 @@ Name: optparse-generic-Version: 1.1.5+Version: 1.2.0 Cabal-Version: >=1.8.0.2 Build-Type: Simple License: BSD3
src/Options/Generic.hs view
@@ -237,12 +237,37 @@ -- >     In an equation for ‘parseRecord’: -- >         parseRecord = Options.Generic.$gdmparseRecord -- >     In the instance declaration for ‘ParseRecord TheTypeOfYourRecord’+--+-- You can customize the library's default behavior using the+-- `parseRecordWithModifiers` utility, like this:+--+-- > {-# LANGUAGE DeriveGeneric     #-}+-- > {-# LANGUAGE OverloadedStrings #-}+-- > +-- > import Options.Generic+-- > +-- > data Example = Example { foo :: Int, bar :: Double }+-- >     deriving (Generic, Show)+-- > +-- > modifiers :: Modifiers+-- > modifiers = defaultModifiers+-- >     { shortNameModifier = firstLetter+-- >     }+-- >+-- > instance ParseRecord Example where+-- >     parseRecord = parseRecordWithModifiers modifiers+-- > +-- > main = do+-- >     x <- getRecord "Test program"+-- >     print (x :: Example)  module Options.Generic (     -- * Parsers       getRecord+    , getWithHelp     , getRecordPure     , unwrapRecord+    , unwrapWithHelp     , unwrapRecordPure     , ParseRecord(..)     , ParseFields(..)@@ -253,6 +278,7 @@     , parseRecordWithModifiers     , defaultModifiers     , lispCaseModifiers+    , firstLetter      -- * Help     , type (<?>)(..)@@ -275,6 +301,7 @@ import Control.Applicative import Control.Monad.IO.Class (MonadIO(..)) import Data.Char (isUpper, toLower, toUpper)+import Data.Foldable (foldMap) import Data.Monoid import Data.List.NonEmpty (NonEmpty((:|))) import Data.Proxy@@ -327,6 +354,8 @@         -- ^ Help message         -> Maybe Text         -- ^ Field label+        -> Maybe Char+        -- ^ Short name         -> Parser a     default parseField         :: (Typeable a, Read a)@@ -334,18 +363,21 @@         -- ^ Help message         -> Maybe Text         -- ^ Field label+        -> Maybe Char+        -- ^ Short name         -> Parser a-    parseField h m = do+    parseField h m c = do         let metavar = map toUpper (show (Data.Typeable.typeOf (undefined :: a)))         case m of             Nothing   -> do                 let fs =  Options.metavar metavar-                       <> maybe mempty (Options.help . Data.Text.unpack) h+                       <> foldMap (Options.help . Data.Text.unpack) h                 Options.argument auto fs             Just name -> do                 let fs =  Options.metavar metavar                        <> Options.long (Data.Text.unpack name)-                       <> maybe mempty (Options.help . Data.Text.unpack) h+                       <> foldMap (Options.help . Data.Text.unpack) h+                       <> foldMap Options.short c                 Options.option   auto fs      {-| The only reason for this method is to provide a special case for@@ -357,8 +389,10 @@         -- ^ Help message         -> Maybe Text         -- ^ Field label+        -> Maybe Char+        -- ^ Short name         -> Parser [a]-    parseListOfField h m = many (parseField h m)+    parseListOfField h m c = many (parseField h m c)  instance ParseField Bool instance ParseField Double@@ -373,71 +407,75 @@     parseField = parseHelpfulString "STRING"  instance ParseField Char where-    parseField h m = do+    parseField h m c = do         let metavar = "CHAR"         let readM = do                 s <- Options.readerAsk                 case s of-                    [c] -> return c-                    _   -> Options.readerAbort Options.ShowHelpText+                    [ch] -> return ch+                    _    -> Options.readerAbort Options.ShowHelpText         case m of             Nothing   -> do                 let fs =  Options.metavar metavar-                       <> maybe mempty (Options.help . Data.Text.unpack) h+                       <> foldMap (Options.help . Data.Text.unpack) h                 Options.argument readM fs             Just name -> do                 let fs =  Options.metavar metavar                        <> Options.long (Data.Text.unpack name)-                       <> maybe mempty (Options.help . Data.Text.unpack) h+                       <> foldMap (Options.help . Data.Text.unpack) h+                       <> foldMap Options.short c                 Options.option   readM fs      parseListOfField = parseHelpfulString "STRING"  instance ParseField Any where-    parseField h m = Any <$> parseField h m+    parseField h m c = Any <$> parseField h m c instance ParseField All where-    parseField h m = All <$> parseField h m+    parseField h m c = All <$> parseField h m c -parseHelpfulString :: String -> Maybe Text -> Maybe Text -> Parser String-parseHelpfulString metavar h m =+parseHelpfulString+    :: String -> Maybe Text -> Maybe Text -> Maybe Char -> Parser String+parseHelpfulString metavar h m c =     case m of         Nothing   -> do             let fs =  Options.metavar metavar-                   <> maybe mempty (Options.help . Data.Text.unpack) h+                   <> foldMap (Options.help . Data.Text.unpack) h             Options.argument Options.str fs         Just name -> do             let fs =  Options.metavar metavar                    <> Options.long (Data.Text.unpack name)-                   <> maybe mempty (Options.help . Data.Text.unpack) h+                   <> foldMap (Options.help . Data.Text.unpack) h+                   <> foldMap Options.short c             Options.option Options.str fs  instance ParseField Data.Text.Text where-    parseField h m = Data.Text.pack <$> parseHelpfulString "TEXT" h m+    parseField h m c = Data.Text.pack <$> parseHelpfulString "TEXT" h m c  instance ParseField Data.ByteString.ByteString where-    parseField h m = fmap Data.Text.Encoding.encodeUtf8 (parseField h m)+    parseField h m c = fmap Data.Text.Encoding.encodeUtf8 (parseField h m c)  instance ParseField Data.Text.Lazy.Text where-    parseField h m = Data.Text.Lazy.pack <$> parseHelpfulString "TEXT" h m+    parseField h m c = Data.Text.Lazy.pack <$> parseHelpfulString "TEXT" h m c  instance ParseField Data.ByteString.Lazy.ByteString where-    parseField h m = fmap Data.Text.Lazy.Encoding.encodeUtf8 (parseField h m)+    parseField h m c = fmap Data.Text.Lazy.Encoding.encodeUtf8 (parseField h m c)  instance ParseField FilePath where-    parseField h m = Filesystem.decodeString <$> parseHelpfulString "FILEPATH" h m+    parseField h m c = Filesystem.decodeString <$> parseHelpfulString "FILEPATH" h m c  instance ParseField Data.Time.Calendar.Day where-    parseField h m = do+    parseField h m c = do         let metavar = "YYYY-MM-DD"         case m of             Nothing   -> do                 let fs =  Options.metavar metavar-                       <> maybe mempty (Options.help . Data.Text.unpack) h+                       <> foldMap (Options.help . Data.Text.unpack) h                 Options.argument iso8601Day fs             Just name -> do                 let fs =  Options.metavar metavar                        <> Options.long (Data.Text.unpack name)-                       <> maybe mempty (Options.help . Data.Text.unpack) h+                       <> foldMap (Options.help . Data.Text.unpack) h+                       <> foldMap Options.short c                 Options.option   iso8601Day fs         where         iso8601Day = Options.eitherReader@@ -461,8 +499,11 @@         -- ^ Help message         -> Maybe Text         -- ^ Field label+        -> Maybe Char+        -- ^ Short name         -> Parser a-    default parseFields :: ParseField a => Maybe Text -> Maybe Text -> Parser a+    default parseFields+        :: ParseField a => Maybe Text -> Maybe Text -> Maybe Char -> Parser a     parseFields = parseField  instance ParseFields Char@@ -480,46 +521,47 @@ instance ParseFields Data.Time.Calendar.Day  instance ParseFields Bool where-    parseFields h m =+    parseFields h m c =         case m of             Nothing   -> do                 let fs =  Options.metavar "BOOL"-                       <> maybe mempty (Options.help . Data.Text.unpack) h+                       <> foldMap (Options.help . Data.Text.unpack) h                 Options.argument auto fs             Just name -> do                 Options.switch $                   Options.long (Data.Text.unpack name)-                  <> maybe mempty (Options.help . Data.Text.unpack) h+                  <> foldMap (Options.help . Data.Text.unpack) h+                  <> foldMap Options.short c  instance ParseFields () where-    parseFields _ _ = pure ()+    parseFields _ _ _ = pure ()  instance ParseFields Any where-    parseFields h m = (fmap mconcat . many . fmap Any) (parseField h m)+    parseFields h m c = (fmap mconcat . many . fmap Any) (parseField h m c)  instance ParseFields All where-    parseFields h m = (fmap mconcat . many . fmap All) (parseField h m)+    parseFields h m c = (fmap mconcat . many . fmap All) (parseField h m c)  instance ParseField a => ParseFields (Maybe a) where-    parseFields h m = optional (parseField h m)+    parseFields h m c = optional (parseField h m c)  instance ParseField a => ParseFields (First a) where-    parseFields h m = (fmap mconcat . many . fmap (First . Just)) (parseField h m)+    parseFields h m c = (fmap mconcat . many . fmap (First . Just)) (parseField h m c)  instance ParseField a => ParseFields (Last a) where-    parseFields h m = (fmap mconcat . many . fmap (Last . Just)) (parseField h m)+    parseFields h m c = (fmap mconcat . many . fmap (Last . Just)) (parseField h m c)  instance (Num a, ParseField a) => ParseFields (Sum a) where-    parseFields h m = (fmap mconcat . many . fmap Sum) (parseField h m)+    parseFields h m c = (fmap mconcat . many . fmap Sum) (parseField h m c)  instance (Num a, ParseField a) => ParseFields (Product a) where-    parseFields h m = (fmap mconcat . many . fmap Product) (parseField h m)+    parseFields h m c = (fmap mconcat . many . fmap Product) (parseField h m c)  instance ParseField a => ParseFields [a] where     parseFields = parseListOfField  instance ParseField a => ParseFields (NonEmpty a) where-    parseFields h m = (:|) <$> parseField h m <*> parseListOfField h m+    parseFields h m c = (:|) <$> parseField h m c <*> parseListOfField h m c  {-| Use this to annotate a field with a type-level string (i.e. a `Symbol`)     representing the help description for that field:@@ -532,12 +574,12 @@ newtype (<?>) (field :: *) (help :: Symbol) = Helpful { unHelpful :: field } deriving (Generic, Show)  instance (ParseField a, KnownSymbol h) => ParseField (a <?> h) where-    parseField _ m = Helpful <$>-      parseField ((Just . Data.Text.pack .symbolVal) (Proxy :: Proxy h)) m+    parseField _ m c = Helpful <$>+      parseField ((Just . Data.Text.pack .symbolVal) (Proxy :: Proxy h)) m c  instance (ParseFields a, KnownSymbol h) => ParseFields (a <?> h) where-    parseFields _ m = Helpful <$>-      parseFields ((Just . Data.Text.pack .symbolVal) (Proxy :: Proxy h)) m+    parseFields _ m c = Helpful <$>+      parseFields ((Just . Data.Text.pack .symbolVal) (Proxy :: Proxy h)) m c instance (ParseFields a, KnownSymbol h) => ParseRecord (a <?> h)  {-| A 1-tuple, used solely to translate `ParseFields` instances into@@ -646,13 +688,54 @@  instance (ParseFields a, ParseFields b) => ParseRecord (Either a b) +{-| Options for customizing derived `ParseRecord` implementations for `Generic`+    types++    You can either create the `Modifiers` record directly:++    > modifiers :: Modifiers+    > modifiers = Modifiers+    >     { fieldNameModifier       = ...+    >     , constructorNameModifier = ...+    >     , shortNameModifier       = ...+    >     }++    ... or you can tweak the `defaultModifiers`:++    > modifiers :: Modifiers+    > modifiers = defaultModifiers { fieldNameModifier = ... }++    ... or you can use/tweak a predefined `Modifier`, like `lispCaseModifiers`++    The `parseRecordWithModifiers` function uses this `Modifiers` record when+    generating a `Generic` implementation of `ParseRecord`+-} data Modifiers = Modifiers   { fieldNameModifier :: String -> String+  -- ^ Transform the name of derived fields (Default: @id@)   , constructorNameModifier :: String -> String+  -- ^ Transform the name of derived constructors (Default: @map toLower@)+  , shortNameModifier :: String -> Maybe Char+  -- ^ Derives an optional short name from the field name (Default: @\\_ -> Nothing@)   } +{-| These are the default modifiers used if you derive a `Generic`+    implementation.  You can customize this and pass the result to+    `parseRecordWithModifiers` if you would like to modify the derived+    implementation:++    > myModifiers :: Modifiers+    > myModifiers = defaultModifiers { constructorNameModifier = id }+    >+    > instance ParseRecord MyType where+    >     parseRecord = parseRecordWithModifiers myModifiers+-} defaultModifiers :: Modifiers-defaultModifiers = Modifiers id (map toLower)+defaultModifiers = Modifiers+    { fieldNameModifier       = id+    , constructorNameModifier = map toLower+    , shortNameModifier       = \_ -> Nothing+    }  -- | Convert field and constructor names from @CamelCase@ to @lisp-case@. --@@ -664,12 +747,19 @@ -- > _type -> --type -- > _splitAt -> --split-at lispCaseModifiers :: Modifiers-lispCaseModifiers = Modifiers lispCase lispCase+lispCaseModifiers = Modifiers lispCase lispCase (\_ -> Nothing)   where     lispCase = dropWhile (== '-') . (>>= lower) . dropWhile (== '_')     lower c | isUpper c = ['-', toLower c]             | otherwise = [c] +{-| Use this for the `shortNameModifier` field of the `Modifiers` record if+    you want to use the first letter of each option as the short name+-}+firstLetter :: String -> Maybe Char+firstLetter (c:_) = Just c+firstLetter  _    = Nothing+ class GenericParseRecord f where     genericParseRecord :: Modifiers -> Parser (f p) @@ -758,10 +848,11 @@         let m :: M1 i s f a             m = undefined -        let label = case (selName m) of+        let label = case selName m of                 ""   -> Nothing                 name -> Just (Data.Text.pack (fieldNameModifier name))-        fmap (M1 . K1) (parseFields Nothing label)+        let shortName = shortNameModifier (selName m)+        fmap (M1 . K1) (parseFields Nothing label shortName)  {- [NOTE - Sums] @@ -831,7 +922,21 @@ instance GenericParseRecord f => GenericParseRecord (M1 D c f) where     genericParseRecord mods = fmap M1 (Options.helper <*> genericParseRecord mods) -parseRecordWithModifiers :: (Generic a, GenericParseRecord (Rep a)) => Modifiers -> Parser a+{-| Use `parseRecordWithModifiers` when you want to tweak the behavior of a+    derived `ParseRecord` implementation, like this:++    > myModifiers :: Modifiers+    > myModifiers = defaultModifiers { constructorNameModifier = id }+    >+    > instance ParseRecord MyType where+    >     parseRecord = parseRecordWithModifiers myModifiers++    This will still require that you derive `Generic` for your type to automate+    most of the implementation, but the `Modifiers` that you pass will change+    how the implementation generates the command line interface+-}+parseRecordWithModifiers+    :: (Generic a, GenericParseRecord (Rep a)) => Modifiers -> Parser a parseRecordWithModifiers mods = fmap GHC.Generics.to (genericParseRecord mods)  -- | Marshal any value that implements `ParseRecord` from the command line@@ -845,6 +950,22 @@     header = Options.header (Data.Text.unpack desc)     info = Options.info parseRecord header +-- | Marshal any value that implements `ParseRecord` from the commmand line+-- alongside an io action that prints the help message.+getWithHelp+    :: (MonadIO io, ParseRecord a)+    => Text+    -- ^ Program description+    -> io (a, io ())+    -- ^ (options, io action to print help message)+getWithHelp desc = do+  a <- liftIO (Options.customExecParser defaultParserPrefs info)+  return (a, help)+  where+    header = Options.header (Data.Text.unpack desc)+    info = Options.info parseRecord header+    help = liftIO (showHelpText defaultParserPrefs info)+ {-| Pure version of `getRecord`  >>> :set -XOverloadedStrings@@ -929,3 +1050,20 @@     -- ^ Command-line arguments     -> Maybe (f Unwrapped) unwrapRecordPure = fmap unwrap . getRecordPure++showHelpText :: Options.ParserPrefs -> Options.ParserInfo a -> IO ()+showHelpText pprefs pinfo =+  Options.handleParseResult . Options.Failure $+  Options.parserFailure pprefs pinfo Options.ShowHelpText mempty++-- | Marshal any value that implements 'ParseRecord' from the command line+-- and unwrap its fields alongside an io action to print the help message+unwrapWithHelp+    :: (MonadIO io, ParseRecord (f Wrapped), Unwrappable f)+    => Text+    -- ^ Program description+    -> io (f Unwrapped, io ())+    -- ^ (options, io action to print help message)+unwrapWithHelp desc = do+  (opts, help) <- getWithHelp desc+  return (unwrap opts, help)