packages feed

optparse-generic 1.4.9 → 1.5.3

raw patch · 3 files changed

Files

CHANGELOG.md view
@@ -1,3 +1,23 @@+1.5.3++- [Link to code in documentation examples](https://github.com/Gabriella439/optparse-generic/pull/118)+- [Allow newer `optparse-applicative`](https://github.com/Gabriella439/optparse-generic/pull/116)+- [Allow newer `time`](https://github.com/Gabriella439/optparse-generic/pull/115)+- [Allow newer `text`, `bytestring`, and `filepath`](https://github.com/Gabriella439/optparse-generic/pull/113)++1.5.2++* [Add support for `OsPath`](https://github.com/Gabriella439/optparse-generic/pull/111)++1.5.1++* [Add `Eq` instances for `(<?>)`, `(<!>)`, and `(<#>)`](https://github.com/Gabriella439/optparse-generic/pull/109)++1.5.0++* BREAKING CHANGE: [Drop support for `system-filepath`](https://github.com/Gabriella439/optparse-generic/pull/106)+* [Add support for more `Data.Time` types](https://github.com/Gabriella439/optparse-generic/pull/102)+ 1.4.9  * [Add `Data` instance for `Unwrapped`](https://github.com/Gabriella439/optparse-generic/pull/100)
optparse-generic.cabal view
@@ -1,5 +1,5 @@ Name: optparse-generic-Version: 1.4.9+Version: 1.5.3 Cabal-Version: >=1.10 Build-Type: Simple License: BSD3@@ -24,17 +24,17 @@ Library     Hs-Source-Dirs: src     Build-Depends:-        base                 >= 4.7      && < 5   ,-        system-filepath      >= 0.3.1    && < 0.5 ,-        text                                < 2.1 ,+        base                 >= 4.8      && < 5   ,+        text                                < 2.2 ,         transformers         >= 0.2.0.0  && < 0.7 ,         transformers-compat  >= 0.3      && < 0.8 ,         Only                                < 0.2 ,-        optparse-applicative >= 0.16.0.0 && < 0.18,-        time                 >= 1.5      && < 1.13,+        optparse-applicative >= 0.16.0.0 && < 0.20,+        time                 >= 1.5      && < 1.15,         void                                < 0.8 ,-        bytestring                          < 0.12-    +        bytestring                          < 0.13,+        filepath                            < 1.6+     if impl(ghc < 8.0)         Build-Depends:             semigroups           >= 0.5.0    && < 0.20
src/Options/Generic.hs view
@@ -21,22 +21,24 @@ -- For example, suppose that you want to parse a record with named fields like -- this: ----- > -- Example.hs--- >--- > {-# LANGUAGE DeriveGeneric     #-}--- > {-# LANGUAGE OverloadedStrings #-}--- > --- > import Options.Generic--- > --- > data Example = Example { foo :: Int, bar :: Double }--- >     deriving (Generic, Show)--- > --- > instance ParseRecord Example--- > --- > main = do--- >     x <- getRecord "Test program"--- >     print (x :: Example)+-- @+-- -- Example.hs --+-- {-# LANGUAGE DeriveGeneric     #-}+-- {-# LANGUAGE OverloadedStrings #-}+--+-- import Options.Generic+--+-- data Example = Example { foo :: Int, bar :: Double }+--     deriving (Generic, Show)+--+-- instance 'ParseRecord' Example+--+-- main = do+--     x <- 'getRecord' "Test program"+--     print (x :: Example)+-- @+-- -- Named fields translate to flags which you can provide in any order: -- -- > $ stack build optparse-generic@@ -47,39 +49,41 @@ -- -- > $ stack runghc Example.hs -- --help -- > Test program--- > +-- > -- > Usage: Example.hs --foo INT --bar DOUBLE--- > +-- > -- > Available options: -- >   -h,--help                Show this help text -- -- You can also add help descriptions to each field, like this: ----- > {-# LANGUAGE DataKinds         #-}--- > {-# LANGUAGE DeriveGeneric     #-}--- > {-# LANGUAGE OverloadedStrings #-}--- > {-# LANGUAGE TypeOperators     #-}--- > --- > import Options.Generic--- > --- > data Example = Example--- >     { foo :: Int    <?> "Documentation for the foo flag"--- >     , bar :: Double <?> "Documentation for the bar flag"--- >     } deriving (Generic, Show)--- > --- > instance ParseRecord Example--- > --- > main = do--- >     x <- getRecord "Test program"--- >     print (x :: Example)+-- @+-- {-# LANGUAGE DataKinds         #-}+-- {-# LANGUAGE DeriveGeneric     #-}+-- {-# LANGUAGE OverloadedStrings #-}+-- {-# LANGUAGE TypeOperators     #-} --+-- import Options.Generic+--+-- data Example = Example+--     { foo :: Int    '<?>' "Documentation for the foo flag"+--     , bar :: Double '<?>' "Documentation for the bar flag"+--     } deriving (Generic, Show)+--+-- instance 'ParseRecord' Example+--+-- main = do+--     x <- 'getRecord' "Test program"+--     print (x :: Example)+-- @+-- -- ... which produces the following @--help@ output: -- -- > $ stack runghc Example.hs -- --help -- > Test program--- > +-- > -- > Usage: Example.hs --foo INT --bar DOUBLE--- > +-- > -- > Available options: -- >   -h,--help                Show this help text -- >   --foo INT                Documentation for the foo flag@@ -95,52 +99,56 @@ -- generalize the definition of your record with a parameter 'w', and use -- 'unwrapRecord'. ----- > {-# LANGUAGE DataKinds          #-}--- > {-# LANGUAGE DeriveGeneric      #-}--- > {-# LANGUAGE FlexibleInstances  #-}  -- One more extension.--- > {-# LANGUAGE OverloadedStrings  #-}--- > {-# LANGUAGE StandaloneDeriving #-}  -- To derive Show--- > {-# LANGUAGE TypeOperators      #-}--- >--- > import Options.Generic--- >--- > data Example w = Example--- >     { foo :: w ::: Int    <?> "Documentation for the foo flag"--- >     , bar :: w ::: Double <?> "Documentation for the bar flag"--- >     } deriving (Generic)--- >--- > instance ParseRecord (Example Wrapped)--- > deriving instance Show (Example Unwrapped)--- >--- > main = do--- >     x <- unwrapRecord "Test program"--- >     print (x :: Example Unwrapped)+-- @+-- {-# LANGUAGE DataKinds          #-}+-- {-# LANGUAGE DeriveGeneric      #-}+-- {-# LANGUAGE FlexibleInstances  #-}  -- One more extension.+-- {-# LANGUAGE OverloadedStrings  #-}+-- {-# LANGUAGE StandaloneDeriving #-}  -- To derive Show+-- {-# LANGUAGE TypeOperators      #-} ----- @Example Unwrapped@ is equivalent to a record type with simple fields:+-- import Options.Generic --+-- data Example w = Example+--     { foo :: w ::: Int    '<?>' "Documentation for the foo flag"+--     , bar :: w ::: Double '<?>' "Documentation for the bar flag"+--     } deriving (Generic)+--+-- instance 'ParseRecord' (Example 'Wrapped')+-- deriving instance Show (Example 'Unwrapped')+--+-- main = do+--     x <- 'unwrapRecord' "Test program"+--     print (x :: Example Unwrapped)+-- @+--+-- @Example 'Unwrapped'@ is equivalent to a record type with simple fields:+-- -- > $ stack runghc Example.hs -- --foo 1 --bar 2.5 -- > Example {foo = 1, bar = 2.5} -- -- You can also add default values to each `Read`able field, like this: ----- > {-# LANGUAGE DataKinds         #-}--- > {-# LANGUAGE DeriveGeneric     #-}--- > {-# LANGUAGE OverloadedStrings #-}--- > {-# LANGUAGE TypeOperators     #-}--- > --- > import Options.Generic--- > --- > data Example = Example--- >     { foo :: Int    <!> "1"--- >     , bar :: String <!> "hello"--- >     } deriving (Generic, Show)--- > --- > instance ParseRecord Example--- > --- > main = do--- >     x <- getRecord "Test program"--- >     print (x :: Example)+-- @+-- {-# LANGUAGE DataKinds         #-}+-- {-# LANGUAGE DeriveGeneric     #-}+-- {-# LANGUAGE OverloadedStrings #-}+-- {-# LANGUAGE TypeOperators     #-} --+-- import Options.Generic+--+-- data Example = Example+--     { foo :: Int    '<!>' "1"+--     , bar :: String '<!>' "hello"+--     } deriving (Generic, Show)+--+-- instance 'ParseRecord' Example+--+-- main = do+--     x <- 'getRecord' "Test program"+--     print (x :: Example)+-- @+-- -- Default values will work alongside help descriptions and unwrapping. -- -- For the following examples I encourage you to test what @--help@ output they@@ -181,7 +189,7 @@ -- > Example {switch = True, list = [1,2], optional = Just 1, first = First  -- > {getFirst = Just 1}, last = Last {getLast = Just 2}, sum = Sum {getSum = -- > 3}, product = Product {getProduct = 2}}--- > +-- > -- > $ stack runghc Example.hs -- > Example {switch = False, list = [], optional = Nothing, first = First -- > {getFirst = Nothing}, second = Last {getLast = Nothing}, sum = Sum {getSum@@ -204,32 +212,38 @@ -- This library also provides out-of-the-box support for many existing types, -- like tuples and `Either`. ----- > {-# LANGUAGE DeriveGeneric     #-}--- > {-# LANGUAGE OverloadedStrings #-}--- > --- > import Options.Generic--- > --- > main = do--- >     x <- getRecord "Test program"--- >     print (x :: Either Double Int)+-- @+-- {-# LANGUAGE DeriveGeneric     #-}+-- {-# LANGUAGE OverloadedStrings #-} --+-- import Options.Generic+--+-- main = do+--     x <- 'getRecord' "Test program"+--     print (x :: Either Double Int)+-- @+-- -- > $ stack runghc Example.hs -- left 1.0 -- > Left 1.0 -- > $ stack runghc Example.hs -- right 2 -- > Right 2--- --- > main = do--- >     x <- getRecord "Test program"--- >     print (x :: (Double, Int)) --+-- @+-- main = do+--     x <- 'getRecord' "Test program"+--     print (x :: (Double, Int))+-- @+-- -- > $ stack runghc Example.hs -- 1.0 2 -- > (1.0,2) -- -- ... and you can also just parse a single value: ----- > main = do--- >     x <- getRecord "Test program"--- >     print (x :: Int)+-- @+-- main = do+--     x <- 'getRecord' "Test program"+--     print (x :: Int)+-- @ -- -- > $ stack runghc Example.hs -- 2 -- > 2@@ -262,27 +276,29 @@ -- >     In the instance declaration for ‘ParseRecord TheTypeOfYourRecord’ -- -- You can customize the library's default behavior using the--- `parseRecordWithModifiers` utility, like this:+-- '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)+-- @+-- {-# 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@@ -338,29 +354,37 @@ import Data.Int (Int8, Int16, Int32, Int64) import Data.Maybe (listToMaybe) import Data.Monoid-import Data.List.NonEmpty (NonEmpty((:|)))+import Data.List.NonEmpty (NonEmpty) import Data.Proxy import Data.Text (Text)+import Data.Time.Format.ISO8601 (ISO8601) import Data.Tuple.Only (Only(..)) import Data.Typeable (Typeable) import Data.Void (Void) import Data.Word (Word8, Word16, Word32, Word64)-import Data.Foldable (foldMap)-import Filesystem.Path (FilePath) import GHC.Generics-import Prelude hiding (FilePath)+import Prelude import Options.Applicative (Parser, ReadM) +import Data.Time+    ( CalendarDiffDays+    , CalendarDiffTime+    , Day+    , LocalTime+    , TimeOfDay+    , TimeZone+    , UTCTime+    , ZonedTime+    )+ import qualified Data.Text import qualified Data.Text.Encoding import qualified Data.Text.Lazy import qualified Data.Text.Lazy.Encoding-import qualified Data.Time.Calendar-import qualified Data.Time.Format+import qualified Data.Time.Format.ISO8601     as ISO8601 import qualified Data.Typeable import qualified Data.ByteString import qualified Data.ByteString.Lazy-import qualified Filesystem.Path.CurrentOS    as Filesystem import qualified Options.Applicative          as Options import qualified Options.Applicative.Types    as Options import qualified Options.Applicative.NonEmpty as Options.NonEmpty@@ -376,6 +400,10 @@ import Numeric.Natural (Natural) #endif +#if MIN_VERSION_filepath(1,4,100)+import System.OsPath+#endif+ auto :: Read a => ReadM a auto = do     s <- Options.readerAsk@@ -520,14 +548,14 @@  parseHelpfulString     :: String -> Maybe Text -> Maybe Text -> Maybe Char -> Maybe String -> Parser String-parseHelpfulString metavar h m c d =+parseHelpfulString metavar_ h m c d =     case m of         Nothing   -> do-            let fs =  Options.metavar metavar+            let fs =  Options.metavar metavar_                    <> foldMap (Options.help . Data.Text.unpack) h             Options.argument Options.str fs         Just name -> do-            let fs =  Options.metavar metavar+            let fs =  Options.metavar metavar_                    <> Options.long (Data.Text.unpack name)                    <> foldMap (Options.help . Data.Text.unpack) h                    <> foldMap Options.short c@@ -546,21 +574,66 @@ instance ParseField Data.ByteString.Lazy.ByteString where     parseField h m c d = fmap Data.Text.Lazy.Encoding.encodeUtf8 (parseField h m c d) -instance ParseField FilePath where-    parseField h m c d = Filesystem.decodeString <$> parseHelpfulString "FILEPATH" h m c d-    readField = Options.str+readISO8601Field :: forall a . (ParseField a, ISO8601 a) => ReadM a+readISO8601Field = Options.eitherReader reader+  where+    reader string =+      case ISO8601.iso8601ParseM string of+          Nothing -> Left ("expected " <> metavar (Proxy :: Proxy a))+          Just t -> Right t -instance ParseField Data.Time.Calendar.Day where-    metavar _ = "YYYY-MM-DD"-    readField = Options.eitherReader-              $ runReadS . Data.Time.Format.readSTime-                            False-                            Data.Time.Format.defaultTimeLocale-                            "%F"-        where-            runReadS [(day, "")] = Right day-            runReadS _           = Left "expected YYYY-MM-DD"+instance ParseField CalendarDiffDays where+    metavar _ = "PyYmMdD" +    readField = readISO8601Field++instance ParseField Day where+    metavar _ = "yyyy-mm-dd"++    readField = readISO8601Field++instance ParseField UTCTime where+    metavar _ = "yyyy-mm-ddThh:mm:ss[.sss]Z"++    readField = readISO8601Field++instance ParseField CalendarDiffTime where+    metavar _ = "PyYmMdDThHmMs[.sss]S"++    readField = readISO8601Field++instance ParseField TimeZone where+    metavar _ = "±hh:mm"++    readField = readISO8601Field++instance ParseField TimeOfDay where+    metavar _ = "hh:mm:ss[.sss]"++    readField = readISO8601Field++instance ParseField LocalTime where+    metavar _ = "yyyy-mm-ddThh:mm:ss[.sss]"++    readField = readISO8601Field++instance ParseField ZonedTime where+    metavar _ = "yyyy-mm-ddThh:mm:ss[.sss]±hh:mm"++    readField = readISO8601Field++#if MIN_VERSION_filepath(1,4,100)+instance ParseField OsString where+  metavar _ = "PATH"+  readField = Options.eitherReader reader+    where+      reader string =+        case encodeUtf string of+            Left err -> Left ("Invalid PATH: " ++ show err)+            Right t -> pure t++#endif+ {-| A class for all types that can be parsed from zero or more arguments/options     on the command line @@ -601,9 +674,19 @@ instance ParseFields Data.ByteString.Lazy.ByteString instance ParseFields Data.Text.Text instance ParseFields Data.Text.Lazy.Text-instance ParseFields FilePath-instance ParseFields Data.Time.Calendar.Day+instance ParseFields CalendarDiffDays+instance ParseFields Day+instance ParseFields UTCTime+instance ParseFields CalendarDiffTime+instance ParseFields TimeZone+instance ParseFields TimeOfDay+instance ParseFields LocalTime+instance ParseFields ZonedTime +#if MIN_VERSION_filepath(1,4,100)+instance ParseFields OsString+#endif+ #if MIN_VERSION_base(4,8,0) instance ParseFields Natural #endif@@ -664,7 +747,7 @@ >     , bar :: Double <?> "Documentation for the bar flag" >     } deriving (Generic, Show) -}-newtype (<?>) (field :: *) (help :: Symbol) = Helpful { unHelpful :: field } deriving (Generic, Show, Data)+newtype (<?>) field (help :: Symbol) = Helpful { unHelpful :: field } deriving (Generic, Show, Data, Eq)  instance (ParseField a, KnownSymbol h) => ParseField (a <?> h) where     parseField _ m c d = Helpful <$>@@ -685,7 +768,7 @@ >     , bar :: Double <!> "0.5" >     } deriving (Generic, Show) -}-newtype (<!>) (field :: *) (value :: Symbol) = DefValue { unDefValue :: field } deriving (Generic, Show, Data)+newtype (<!>) field (value :: Symbol) = DefValue { unDefValue :: field } deriving (Generic, Show, Data, Eq)  instance (ParseField a, KnownSymbol d) => ParseField (a <!> d) where     parseField h m c _ = DefValue <$> parseField h m c (Just (symbolVal (Proxy :: Proxy d)))@@ -705,7 +788,7 @@ >     , bar :: Double <#> "b" >     } deriving (Generic, Show) -}-newtype (<#>) (field :: *) (value :: Symbol) = ShortName { unShortName :: field } deriving (Generic, Show, Data)+newtype (<#>) field (value :: Symbol) = ShortName { unShortName :: field } deriving (Generic, Show, Data, Eq)  instance (ParseField a, KnownSymbol c) => ParseField (a <#> c) where     parseField h m _ d = ShortName <$> parseField h m (listToMaybe (symbolVal (Proxy :: Proxy c))) d@@ -805,18 +888,41 @@ instance ParseRecord All where     parseRecord = fmap getOnly parseRecord -instance ParseRecord FilePath where-    parseRecord = fmap getOnly parseRecord- instance ParseRecord Data.ByteString.ByteString where     parseRecord = fmap getOnly parseRecord  instance ParseRecord Data.ByteString.Lazy.ByteString where     parseRecord = fmap getOnly parseRecord -instance ParseRecord Data.Time.Calendar.Day where+instance ParseRecord CalendarDiffDays where     parseRecord = fmap getOnly parseRecord +instance ParseRecord Day where+    parseRecord = fmap getOnly parseRecord++instance ParseRecord UTCTime where+    parseRecord = fmap getOnly parseRecord++instance ParseRecord CalendarDiffTime where+    parseRecord = fmap getOnly parseRecord++instance ParseRecord TimeZone where+    parseRecord = fmap getOnly parseRecord++instance ParseRecord TimeOfDay where+    parseRecord = fmap getOnly parseRecord++instance ParseRecord LocalTime where+    parseRecord = fmap getOnly parseRecord++instance ParseRecord ZonedTime where+    parseRecord = fmap getOnly parseRecord++#if MIN_VERSION_filepath(1,4,100)+instance ParseRecord OsString where+    parseRecord = fmap getOnly parseRecord+#endif+ instance ParseField a => ParseRecord (Maybe a) where     parseRecord = fmap getOnly parseRecord @@ -1127,7 +1233,7 @@     prefs  = Options.prefs (defaultParserPrefs <> prefsMods)     info   = Options.info parseRecord infoMods --- | Marshal any value that implements `ParseRecord` from the commmand line+-- | Marshal any value that implements `ParseRecord` from the command line -- alongside an io action that prints the help message. getWithHelp     :: (MonadIO io, ParseRecord a)@@ -1137,7 +1243,7 @@     -- ^ (options, io action to print help message) getWithHelp desc = getWithHelpWith desc mempty --- | Marshal any value that implements `ParseRecord` from the commmand line+-- | Marshal any value that implements `ParseRecord` from the command line -- alongside an io action that prints the help message. getWithHelpWith     :: (MonadIO io, ParseRecord a)