harg 0.1.0.1 → 0.1.3.0
raw patch · 10 files changed
+384/−126 lines, 10 filesdep +splitdep ~barbiesdep ~higgledyPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies added: split
Dependency ranges changed: barbies, higgledy
API changes (from Hackage documentation)
+ Options.Harg: manyParser :: String -> OptReader a -> OptReader [a]
+ Options.Harg: optDefaultStr :: (HasDefaultStr o attr, NotInAttrs OptDefault attr (DuplicateAttrMultipleErr "optDefaultStr" '["optDefault", "optRequired"]), NotInAttrs OptOptional attr (IncompatibleAttrsErr "optDefaultStr" "optOptional")) => String -> o attr a -> o (OptDefault : attr) a
+ Options.Harg: optRequired :: (HasRequired o attr, NotInAttrs OptDefault attr (DuplicateAttrMultipleErr "optRequired" '["optDefault", "optDefaultStr"]), NotInAttrs OptOptional attr (IncompatibleAttrsErr "optRequired" "optOptional")) => o attr a -> o (OptDefault : attr) a
+ Options.Harg.Construct: class HasDefaultStr o (attr :: [OptAttr])
+ Options.Harg.Construct: class HasRequired o (attr :: [OptAttr])
+ Options.Harg.Construct: instance Options.Harg.Construct.HasDefaultStr Options.Harg.Types.ArgumentOpt a
+ Options.Harg.Construct: instance Options.Harg.Construct.HasDefaultStr Options.Harg.Types.OptionOpt a
+ Options.Harg.Construct: instance Options.Harg.Construct.HasRequired Options.Harg.Types.ArgumentOpt a
+ Options.Harg.Construct: instance Options.Harg.Construct.HasRequired Options.Harg.Types.OptionOpt a
+ Options.Harg.Construct: manyParser :: String -> OptReader a -> OptReader [a]
+ Options.Harg.Construct: optDefaultStr :: (HasDefaultStr o attr, NotInAttrs OptDefault attr (DuplicateAttrMultipleErr "optDefaultStr" '["optDefault", "optRequired"]), NotInAttrs OptOptional attr (IncompatibleAttrsErr "optDefaultStr" "optOptional")) => String -> o attr a -> o (OptDefault : attr) a
+ Options.Harg.Construct: optRequired :: (HasRequired o attr, NotInAttrs OptDefault attr (DuplicateAttrMultipleErr "optRequired" '["optDefault", "optDefaultStr"]), NotInAttrs OptOptional attr (IncompatibleAttrsErr "optRequired" "optOptional")) => o attr a -> o (OptDefault : attr) a
+ Options.Harg.Construct: type DuplicateAttrErr attr = QuoteSym attr :<>: 'Text " is already specified."
+ Options.Harg.Construct: type DuplicateAttrMultipleErr attr rest = QuoteSym attr :<>: 'Text (CommaSep rest) :<>: 'Text " has already been specified."
+ Options.Harg.Construct: type IncompatibleAttrsErr l r = QuoteSym l :<>: 'Text " and " :<>: QuoteSym r :<>: 'Text " cannot be mixed in an option definition."
+ Options.Harg.Sources: hiddenSources :: HiddenSources f
+ Options.Harg.Sources: type DefaultSources = EnvSource
+ Options.Harg.Sources: type HiddenSources = DefaultStrSource
+ Options.Harg.Sources.DefaultStr: DefaultStrSource :: DefaultStrSource
+ Options.Harg.Sources.DefaultStr: DefaultStrSourceVal :: DefaultStrSourceVal
+ Options.Harg.Sources.DefaultStr: data DefaultStrSource (f :: Type -> Type)
+ Options.Harg.Sources.DefaultStr: data DefaultStrSourceVal
+ Options.Harg.Sources.DefaultStr: instance Data.Barbie.Internal.Functor.FunctorB Options.Harg.Sources.DefaultStr.DefaultStrSource
+ Options.Harg.Sources.DefaultStr: instance Data.Barbie.Internal.Functor.FunctorB a => Options.Harg.Sources.Types.RunSource Options.Harg.Sources.DefaultStr.DefaultStrSourceVal a
+ Options.Harg.Sources.DefaultStr: instance Data.Barbie.Internal.Product.ProductB Options.Harg.Sources.DefaultStr.DefaultStrSource
+ Options.Harg.Sources.DefaultStr: instance Data.Barbie.Internal.Traversable.TraversableB Options.Harg.Sources.DefaultStr.DefaultStrSource
+ Options.Harg.Sources.DefaultStr: instance GHC.Generics.Generic (Options.Harg.Sources.DefaultStr.DefaultStrSource f)
+ Options.Harg.Sources.DefaultStr: instance Options.Harg.Sources.Types.GetSource Options.Harg.Sources.DefaultStr.DefaultStrSource f
+ Options.Harg.Sources.DefaultStr: runDefaultStrSource :: forall a f. (FunctorB a, Applicative f) => a (Compose Opt f) -> a (Compose SourceRunResult f)
+ Options.Harg.Types: [_aDefaultStr] :: ArgumentOpt a -> Maybe String
+ Options.Harg.Types: [_oDefaultStr] :: OptionOpt a -> Maybe String
+ Options.Harg.Types: [_optDefaultStr] :: Opt a -> Maybe String
- Options.Harg: defaultSources :: EnvSource f
+ Options.Harg: defaultSources :: DefaultSources f
- Options.Harg: execCommands :: forall c ts xs. (TraversableB (VariantF xs), TraversableB c, ProductB c, Subcommands ts xs, GetSource c Identity, All (RunSource (SourceVal c)) xs, All (RunSource ()) xs, MapAssocList xs) => c Opt -> AssocListF ts xs Opt -> IO (VariantF xs Identity)
+ Options.Harg: execCommands :: forall c ts xs. (TraversableB (VariantF xs), TraversableB c, ProductB c, Subcommands ts xs, GetSource c Identity, All (RunSource (SourceVal (c :* HiddenSources))) xs, All (RunSource ()) xs, MapAssocList xs) => c Opt -> AssocListF ts xs Opt -> IO (VariantF xs Identity)
- Options.Harg: execCommandsDef :: forall ts xs. (TraversableB (VariantF xs), Subcommands ts xs, All (RunSource EnvSourceVal) xs, All (RunSource ()) xs, MapAssocList xs) => AssocListF ts xs Opt -> IO (VariantF xs Identity)
+ Options.Harg: execCommandsDef :: forall ts xs. (TraversableB (VariantF xs), Subcommands ts xs, All (RunSource (SourceVal (DefaultSources :* HiddenSources))) xs, All (RunSource ()) xs, MapAssocList xs) => AssocListF ts xs Opt -> IO (VariantF xs Identity)
- Options.Harg: optDefault :: (HasDefault o attr, NotInAttrs OptOptional attr "optDefault" "optOptional") => a -> o attr a -> o (OptDefault : attr) a
+ Options.Harg: optDefault :: (HasDefault o attr, NotInAttrs OptDefault attr (DuplicateAttrMultipleErr "optDefault" '["optDefaultStr", "optRequired"]), NotInAttrs OptOptional attr (IncompatibleAttrsErr "optDefault" "optOptional")) => a -> o attr a -> o (OptDefault : attr) a
- Options.Harg: optOptional :: (HasOptional o attr, NotInAttrs OptDefault attr "optOptional" "optDefault") => o attr a -> o (OptOptional : attr) (Maybe a)
+ Options.Harg: optOptional :: (HasOptional o attr, NotInAttrs OptOptional attr (DuplicateAttrErr "optOptional"), NotInAttrs OptDefault attr (IncompatibleAttrsErr "optOptional" "optDefault")) => o attr a -> o (OptOptional : attr) (Maybe a)
- Options.Harg.Construct: optDefault :: (HasDefault o attr, NotInAttrs OptOptional attr "optDefault" "optOptional") => a -> o attr a -> o (OptDefault : attr) a
+ Options.Harg.Construct: optDefault :: (HasDefault o attr, NotInAttrs OptDefault attr (DuplicateAttrMultipleErr "optDefault" '["optDefaultStr", "optRequired"]), NotInAttrs OptOptional attr (IncompatibleAttrsErr "optDefault" "optOptional")) => a -> o attr a -> o (OptDefault : attr) a
- Options.Harg.Construct: optOptional :: (HasOptional o attr, NotInAttrs OptDefault attr "optOptional" "optDefault") => o attr a -> o (OptOptional : attr) (Maybe a)
+ Options.Harg.Construct: optOptional :: (HasOptional o attr, NotInAttrs OptOptional attr (DuplicateAttrErr "optOptional"), NotInAttrs OptDefault attr (IncompatibleAttrsErr "optOptional" "optDefault")) => o attr a -> o (OptOptional : attr) (Maybe a)
- Options.Harg.Construct: type family NotInAttrs (x :: k) (xs :: [k]) (l :: Symbol) (r :: Symbol) :: Constraint
+ Options.Harg.Construct: type family CommaSep' (s :: Symbol) (xs :: [Symbol]) :: Symbol
- Options.Harg.Operations: execCommands :: forall c ts xs. (TraversableB (VariantF xs), TraversableB c, ProductB c, Subcommands ts xs, GetSource c Identity, All (RunSource (SourceVal c)) xs, All (RunSource ()) xs, MapAssocList xs) => c Opt -> AssocListF ts xs Opt -> IO (VariantF xs Identity)
+ Options.Harg.Operations: execCommands :: forall c ts xs. (TraversableB (VariantF xs), TraversableB c, ProductB c, Subcommands ts xs, GetSource c Identity, All (RunSource (SourceVal (c :* HiddenSources))) xs, All (RunSource ()) xs, MapAssocList xs) => c Opt -> AssocListF ts xs Opt -> IO (VariantF xs Identity)
- Options.Harg.Operations: execCommandsDef :: forall ts xs. (TraversableB (VariantF xs), Subcommands ts xs, All (RunSource EnvSourceVal) xs, All (RunSource ()) xs, MapAssocList xs) => AssocListF ts xs Opt -> IO (VariantF xs Identity)
+ Options.Harg.Operations: execCommandsDef :: forall ts xs. (TraversableB (VariantF xs), Subcommands ts xs, All (RunSource (SourceVal (DefaultSources :* HiddenSources))) xs, All (RunSource ()) xs, MapAssocList xs) => AssocListF ts xs Opt -> IO (VariantF xs Identity)
- Options.Harg.Operations: execCommandsWithCtx :: forall c ts xs. (TraversableB (VariantF xs), TraversableB c, ProductB c, Subcommands ts xs, GetSource c Identity, All (RunSource (SourceVal c)) xs, All (RunSource ()) xs, MapAssocList xs) => HargCtx -> c Opt -> AssocListF ts xs Opt -> IO (VariantF xs Identity)
+ Options.Harg.Operations: execCommandsWithCtx :: forall c ts xs. (TraversableB (VariantF xs), TraversableB c, ProductB c, Subcommands ts xs, GetSource c Identity, All (RunSource (SourceVal (c :* HiddenSources))) xs, All (RunSource ()) xs, MapAssocList xs) => HargCtx -> c Opt -> AssocListF ts xs Opt -> IO (VariantF xs Identity)
- Options.Harg.Operations: execCommandsWithCtxDef :: forall ts xs. (TraversableB (VariantF xs), Subcommands ts xs, All (RunSource EnvSourceVal) xs, All (RunSource ()) xs, MapAssocList xs) => HargCtx -> AssocListF ts xs Opt -> IO (VariantF xs Identity)
+ Options.Harg.Operations: execCommandsWithCtxDef :: forall ts xs. (TraversableB (VariantF xs), Subcommands ts xs, All (RunSource (SourceVal (DefaultSources :* HiddenSources))) xs, All (RunSource ()) xs, MapAssocList xs) => HargCtx -> AssocListF ts xs Opt -> IO (VariantF xs Identity)
- Options.Harg.Sources: defaultSources :: EnvSource f
+ Options.Harg.Sources: defaultSources :: DefaultSources f
- Options.Harg.Types: ArgumentOpt :: Maybe String -> Maybe String -> Maybe String -> Maybe a -> OptReader a -> ArgumentOpt a
+ Options.Harg.Types: ArgumentOpt :: Maybe String -> Maybe String -> Maybe String -> Maybe a -> Maybe String -> OptReader a -> ArgumentOpt a
- Options.Harg.Types: Opt :: Maybe String -> Maybe Char -> Maybe String -> Maybe String -> Maybe String -> Maybe a -> OptReader a -> OptType a -> Opt a
+ Options.Harg.Types: Opt :: Maybe String -> Maybe Char -> Maybe String -> Maybe String -> Maybe String -> Maybe a -> Maybe String -> OptReader a -> OptType a -> Opt a
- Options.Harg.Types: OptionOpt :: Maybe String -> Maybe Char -> Maybe String -> Maybe String -> Maybe String -> Maybe a -> OptReader a -> OptionOpt a
+ Options.Harg.Types: OptionOpt :: Maybe String -> Maybe Char -> Maybe String -> Maybe String -> Maybe String -> Maybe a -> Maybe String -> OptReader a -> OptionOpt a
Files
- CHANGELOG.md +20/−1
- README.lhs +50/−5
- README.md +50/−5
- harg.cabal +7/−5
- src/Options/Harg.hs +3/−0
- src/Options/Harg/Construct.hs +138/−67
- src/Options/Harg/Operations.hs +24/−21
- src/Options/Harg/Sources.hs +11/−1
- src/Options/Harg/Sources/DefaultStr.hs +57/−0
- src/Options/Harg/Types.hs +24/−21
CHANGELOG.md view
@@ -1,3 +1,22 @@ # Changelog for harg -## Unreleased changes+## 0.1.3.0 [2019.08.28]++- Add `manyParser` to parse list of options separated by delimiter++## 0.1.2.0 [2019.08.19]++- Add `optRequired` to mark option as required++## 0.1.1.0 [2019.08.16]++- Add `optDefaultStr` to provide defaults as unparsed strings+- Bump dependencies (`barbies` and `higgledy`)++## 0.1.0.1 [2019.07.19]++- Minor documentation changes++## 0.1.0.0 [2019.07.18]++- Initial release
README.lhs view
@@ -1,9 +1,10 @@-# `harg` :nut_and_bolt:+# harg -[](https://travis-ci.org/alexpeits/harg) [](https://hackage.haskell.org/package/harg)+[](https://travis-ci.org/alexpeits/harg)+[](https://hackage.haskell.org/package/harg) `harg` is a library for configuring programs by scanning command line arguments, environment-variables and default values. Under the hood, it uses a subset of+variables, default values and more. Under the hood, it uses a subset of [`optparse-applicative`](https://hackage.haskell.org/package/optparse-applicative) to expose regular arguments, switch arguments and subcommands. The library relies heavily on the use of higher kinded data (HKD) thanks to the [`barbies`](https://hackage.haskell.org/package/barbies) library. Using@@ -17,8 +18,6 @@ tl;dr: Take a look at the [example](Example.hs). -(WIP)- Here are some different usage scenarios. Let's first enable some language extensions and add some imports: @@ -151,6 +150,24 @@ -> Maybe a` use `parseWith`, which runs the parser and in case of failure uses a default error message. For example, `readParser` is defined as `parseWith readMaybe`. +Finally, an optional option with type `a` can be specified by setting its type to `Maybe a`. The+declaration is exactly the same as it would be for `a`, and adding `optOptional` to the modifiers+turns turns the parser from `String -> Either String a` to `String -> Either String (Maybe a)` but+without using the `Read` instance for `Maybe`:++``` haskell ignore+someOpt :: Opt (Maybe Int)+someOpt+ = toOpt ( option readParser+ & optLong "something"+ & optOptional+ )+```++Note that `optOptional` can't be used with `optDefault`. Using them together raises a type error at+compile time, to ensure there's no ambiguous behaviour (e.g. the order of declaration of modifiers+should not influence the resulting option).+ There are 3 ways to configure this datatype. ### 1. Using a `barbie` type@@ -706,6 +723,34 @@ & optHelp "JSON config filepath" ) ```++Here, the type of the option for the JSON source is `ConfigFile`. This type is a wrapper around+`FilePath`, which looks like this:++``` haskell ignore+data ConfigFile+ = ConfigFile FilePath+ | NoConfigFile+```++This has the advantage that, if the user wants to specify an optional configuration file, they can+simply say:++``` haskell+jsonOpt :: Opt ConfigFile+jsonOpt+ = toOpt ( option strParser+ & optLong "json"+ & optDefault NoConfigFile+ )+```++Also, because `ConfigFile` has an `IsString` instance, there's no need to say+`optLong (ConfigFile "json")` (if `OverloadedStrings` is enabled).++There's a bit of a disconnect between `ConfigFile` and the ability to make optional options using+`Maybe` and `optOptional`. The reason for it is that the type that `JSONSource` wraps is not+polymorphic, since it needs to be a filepath specifically. # Roadmap
README.md view
@@ -1,9 +1,10 @@-# `harg` :nut_and_bolt:+# harg -[](https://travis-ci.org/alexpeits/harg) [](https://hackage.haskell.org/package/harg)+[](https://travis-ci.org/alexpeits/harg)+[](https://hackage.haskell.org/package/harg) `harg` is a library for configuring programs by scanning command line arguments, environment-variables and default values. Under the hood, it uses a subset of+variables, default values and more. Under the hood, it uses a subset of [`optparse-applicative`](https://hackage.haskell.org/package/optparse-applicative) to expose regular arguments, switch arguments and subcommands. The library relies heavily on the use of higher kinded data (HKD) thanks to the [`barbies`](https://hackage.haskell.org/package/barbies) library. Using@@ -17,8 +18,6 @@ tl;dr: Take a look at the [example](Example.hs). -(WIP)- Here are some different usage scenarios. Let's first enable some language extensions and add some imports: @@ -151,6 +150,24 @@ -> Maybe a` use `parseWith`, which runs the parser and in case of failure uses a default error message. For example, `readParser` is defined as `parseWith readMaybe`. +Finally, an optional option with type `a` can be specified by setting its type to `Maybe a`. The+declaration is exactly the same as it would be for `a`, and adding `optOptional` to the modifiers+turns turns the parser from `String -> Either String a` to `String -> Either String (Maybe a)` but+without using the `Read` instance for `Maybe`:++``` haskell ignore+someOpt :: Opt (Maybe Int)+someOpt+ = toOpt ( option readParser+ & optLong "something"+ & optOptional+ )+```++Note that `optOptional` can't be used with `optDefault`. Using them together raises a type error at+compile time, to ensure there's no ambiguous behaviour (e.g. the order of declaration of modifiers+should not influence the resulting option).+ There are 3 ways to configure this datatype. ### 1. Using a `barbie` type@@ -706,6 +723,34 @@ & optHelp "JSON config filepath" ) ```++Here, the type of the option for the JSON source is `ConfigFile`. This type is a wrapper around+`FilePath`, which looks like this:++``` haskell ignore+data ConfigFile+ = ConfigFile FilePath+ | NoConfigFile+```++This has the advantage that, if the user wants to specify an optional configuration file, they can+simply say:++``` haskell+jsonOpt :: Opt ConfigFile+jsonOpt+ = toOpt ( option strParser+ & optLong "json"+ & optDefault NoConfigFile+ )+```++Also, because `ConfigFile` has an `IsString` instance, there's no need to say+`optLong (ConfigFile "json")` (if `OverloadedStrings` is enabled).++There's a bit of a disconnect between `ConfigFile` and the ability to make optional options using+`Maybe` and `optOptional`. The reason for it is that the type that `JSONSource` wraps is not+polymorphic, since it needs to be a filepath specifically. # Roadmap
harg.cabal view
@@ -1,8 +1,8 @@ cabal-version: 2.2 name: harg-version: 0.1.0.1-synopsis: Haskell program configuration from multiple sources+version: 0.1.3.0+synopsis: Haskell program configuration using higher kinded data description: Please see the README on GitHub at <https://github.com/alexpeits/harg#readme> homepage: https://github.com/alexpeits/harg bug-reports: https://github.com/alexpeits/harg/issues@@ -13,7 +13,7 @@ license: BSD-3-Clause license-file: LICENSE build-type: Simple-category: System, CLI, Options, Parsing+category: System, CLI, Options, Parsing, HKD extra-source-files: README.md CHANGELOG.md@@ -38,6 +38,7 @@ Options.Harg.Pretty Options.Harg.Single Options.Harg.Sources+ Options.Harg.Sources.DefaultStr Options.Harg.Sources.Env Options.Harg.Sources.JSON Options.Harg.Sources.NoSource@@ -62,11 +63,12 @@ -Wno-unticked-promoted-constructors build-depends: base >=4.7 && <5 , aeson >= 1.4.2 && < 1.5- , barbies >= 1.0.0 && < 1.2+ , barbies >= 1.1.0 && < 1.2 , bytestring >= 0.10.8 && < 0.11 , directory >= 1.3.3 && < 1.4- , higgledy >= 0.2.0 && < 0.3+ , higgledy >= 0.3.0 && < 0.4 , optparse-applicative >= 0.14.3 && < 0.15+ , split >= 0.2.3 && < 0.3 , text >= 1.2.3 && < 1.3 , yaml >= 0.11.0 && < 0.12 default-language: Haskell2010
src/Options/Harg.hs view
@@ -37,6 +37,8 @@ , optMetavar , optEnvVar , optDefault+ , optDefaultStr+ , optRequired , optOptional , toOpt , Opt@@ -46,6 +48,7 @@ , readParser , strParser , boolParser+ , manyParser -- ** Executing options , execOpt
src/Options/Harg/Construct.hs view
@@ -7,9 +7,11 @@ import Data.Char (toLower) import Data.Kind (Constraint) import Data.String (IsString(..))-import GHC.TypeLits (ErrorMessage(..), TypeError, Symbol)+import GHC.TypeLits (ErrorMessage(..), TypeError, Symbol, AppendSymbol) import Text.Read (readMaybe) +import Data.List.Split (splitOn)+ import Options.Harg.Types @@ -72,9 +74,11 @@ class HasDefault o (attr :: [OptAttr]) where -- | Add a default value to an option. Cannot be used in conjuction with- -- 'optOptional'.+ -- with 'optRequired', 'optDefaultStr' or 'optOptional'. optDefault- :: NotInAttrs OptOptional attr "optDefault" "optOptional"+ :: ( NotInAttrs OptDefault attr (DuplicateAttrMultipleErr "optDefault" '["optDefaultStr", "optRequired"])+ , NotInAttrs OptOptional attr (IncompatibleAttrsErr "optDefault" "optOptional")+ ) => a -> o attr a -> o (OptDefault ': attr) a instance HasDefault OptionOpt a where@@ -83,10 +87,40 @@ instance HasDefault ArgumentOpt a where optDefault a o = o { _aDefault = Just a } --- optional--- | Class for options that can be optional. Cannot be used in--- conjunction with 'HasDefault'. Note that this will turn a parser for @a@--- into a parser for @Maybe a@, modifying the reader function appropriately.+class HasDefaultStr o (attr :: [OptAttr]) where+ -- | Add a default unparsed value to an option. Cannot be used in conjuction+ -- with 'optDefault', 'optRequired' or 'optOptional'.+ optDefaultStr+ :: ( NotInAttrs OptDefault attr (DuplicateAttrMultipleErr "optDefaultStr" '["optDefault", "optRequired"])+ , NotInAttrs OptOptional attr (IncompatibleAttrsErr "optDefaultStr" "optOptional")+ )+ => String -> o attr a -> o (OptDefault ': attr) a++instance HasDefaultStr OptionOpt a where+ optDefaultStr s o = o { _oDefaultStr = Just s }++instance HasDefaultStr ArgumentOpt a where+ optDefaultStr s o = o { _aDefaultStr = Just s }++class HasRequired o (attr :: [OptAttr]) where+ -- | Mark an option as required. Cannot be used in conjunction with+ -- 'optOptional', 'optDefault' or 'optRequiredStr'.+ optRequired+ :: ( NotInAttrs OptDefault attr (DuplicateAttrMultipleErr "optRequired" '["optDefault", "optDefaultStr"])+ , NotInAttrs OptOptional attr (IncompatibleAttrsErr "optRequired" "optOptional")+ )+ => o attr a -> o (OptDefault ': attr) a++instance HasRequired OptionOpt a where+ optRequired o = o { _oDefault = Nothing }++instance HasRequired ArgumentOpt a where+ optRequired o = o { _aDefault = Nothing }++-- | Class for options that can be optional. Cannot be used in conjunction with+-- 'HasDefault', 'HasDefaultStr' or 'HasRequired'. Note that this will turn a+-- parser for @a@ into a parser for @Maybe a@, modifying the reader function+-- appropriately. -- For example: -- -- @@@ -99,31 +133,36 @@ -- @ class HasOptional o (attr :: [OptAttr]) where -- | Specify that an option is optional. This will convert an @Opt a@ to an- -- @Opt (Maybe a)@+ -- @Opt (Maybe a)@. Cannot be used in conjunction with 'optDefault', 'optDefaultStr'+ -- or 'optRequired'. optOptional- :: NotInAttrs OptDefault attr "optOptional" "optDefault"+ :: ( NotInAttrs OptOptional attr (DuplicateAttrErr "optOptional")+ , NotInAttrs OptDefault attr (IncompatibleAttrsErr "optOptional" "optDefault")+ ) => o attr a -> o (OptOptional ': attr) (Maybe a) instance HasOptional OptionOpt a where optOptional OptionOpt{..} = OptionOpt- { _oLong = _oLong- , _oShort = _oShort- , _oHelp = _oHelp- , _oMetavar = _oMetavar- , _oEnvVar = _oEnvVar- , _oDefault = Just Nothing- , _oReader = fmap Just . _oReader+ { _oLong = _oLong+ , _oShort = _oShort+ , _oHelp = _oHelp+ , _oMetavar = _oMetavar+ , _oEnvVar = _oEnvVar+ , _oDefault = Just Nothing+ , _oDefaultStr = Nothing+ , _oReader = fmap Just . _oReader } instance HasOptional ArgumentOpt a where optOptional ArgumentOpt{..} = ArgumentOpt- { _aHelp = _aHelp- , _aMetavar = _aMetavar- , _aEnvVar = _aEnvVar- , _aDefault = Just Nothing- , _aReader = fmap Just . _aReader+ { _aHelp = _aHelp+ , _aMetavar = _aMetavar+ , _aEnvVar = _aEnvVar+ , _aDefault = Just Nothing+ , _aDefaultStr = Nothing+ , _aReader = fmap Just . _aReader } -- | Class to convert an intermediate option type into 'Opt'. Instances@@ -135,40 +174,43 @@ instance IsOpt OptionOpt attr where toOpt OptionOpt{..} = Opt- { _optLong = _oLong- , _optShort = _oShort- , _optHelp = _oHelp- , _optMetavar = _oMetavar- , _optEnvVar = _oEnvVar- , _optDefault = _oDefault- , _optReader = _oReader- , _optType = OptionOptType+ { _optLong = _oLong+ , _optShort = _oShort+ , _optHelp = _oHelp+ , _optMetavar = _oMetavar+ , _optEnvVar = _oEnvVar+ , _optDefault = _oDefault+ , _optDefaultStr = _oDefaultStr+ , _optReader = _oReader+ , _optType = OptionOptType } instance IsOpt FlagOpt attr where toOpt FlagOpt{..} = Opt- { _optLong = _fLong- , _optShort = _fShort- , _optHelp = _fHelp- , _optMetavar = Nothing- , _optEnvVar = _fEnvVar- , _optDefault = Just _fDefault- , _optReader = _fReader- , _optType = FlagOptType _fActive+ { _optLong = _fLong+ , _optShort = _fShort+ , _optHelp = _fHelp+ , _optMetavar = Nothing+ , _optEnvVar = _fEnvVar+ , _optDefault = Just _fDefault+ , _optDefaultStr = Nothing+ , _optReader = _fReader+ , _optType = FlagOptType _fActive } instance IsOpt ArgumentOpt attr where toOpt ArgumentOpt{..} = Opt- { _optLong = Nothing- , _optShort = Nothing- , _optHelp = _aHelp- , _optMetavar = _aMetavar- , _optEnvVar = _aEnvVar- , _optDefault = _aDefault- , _optReader = _aReader- , _optType = ArgumentOptType+ { _optLong = Nothing+ , _optShort = Nothing+ , _optHelp = _aHelp+ , _optMetavar = _aMetavar+ , _optEnvVar = _aEnvVar+ , _optDefault = _aDefault+ , _optDefaultStr = _aDefaultStr+ , _optReader = _aReader+ , _optType = ArgumentOptType } -- | Create an option parser, equivalent to 'Options.Applicative.option'. The@@ -188,13 +230,14 @@ -> OptionOpt '[] a option p = OptionOpt- { _oLong = Nothing- , _oShort = Nothing- , _oHelp = Nothing- , _oMetavar = Nothing- , _oEnvVar = Nothing- , _oDefault = Nothing- , _oReader = p+ { _oLong = Nothing+ , _oShort = Nothing+ , _oHelp = Nothing+ , _oMetavar = Nothing+ , _oEnvVar = Nothing+ , _oDefault = Nothing+ , _oDefaultStr = Nothing+ , _oReader = p } -- | Similar to 'option', but accepts a modifier function and returns an 'Opt'@@ -333,11 +376,12 @@ -> ArgumentOpt '[] a argument p = ArgumentOpt- { _aHelp = Nothing- , _aMetavar = Nothing- , _aEnvVar = Nothing- , _aDefault = Nothing- , _aReader = p+ { _aHelp = Nothing+ , _aMetavar = Nothing+ , _aEnvVar = Nothing+ , _aDefault = Nothing+ , _aDefaultStr = Nothing+ , _aReader = p } -- | Similar to 'argument', but accepts a modifier function and returns an@@ -393,6 +437,13 @@ "false" -> Right False _ -> Left ("Unable to parse " <> s <> " to Bool") +-- | A parser that can parse many items, returning a list.+manyParser+ :: String -- ^ Separator+ -> OptReader a -- ^ Parser for each string+ -> OptReader [a]+manyParser sep parser+ = traverse parser . splitOn sep -- | Wrap a symbol in quotes, for pretty printing in type errors. type QuoteSym (s :: Symbol)@@ -403,15 +454,35 @@ type family NotInAttrs (x :: k) (xs :: [k])- (l :: Symbol)- (r :: Symbol)+ (err :: ErrorMessage) :: Constraint where- NotInAttrs _ '[] _ _+ NotInAttrs _ '[] _ = ()- NotInAttrs x (x ': _) l r- = TypeError- ( QuoteSym l :<>: 'Text " and " :<>: QuoteSym r- :<>: 'Text " cannot be mixed in an option definition."- )- NotInAttrs x (y ': xs) l r- = NotInAttrs x xs l r+ NotInAttrs x (x ': _) err+ = TypeError err+ NotInAttrs x (y ': xs) err+ = NotInAttrs x xs err++type family CommaSep (xs :: [Symbol]) :: Symbol where+ CommaSep '[] = ""+ CommaSep '[x] = " or " `AppendSymbol` x+ CommaSep (x ': xs) = " or one of " `AppendSymbol` CommaSep' x xs++type family CommaSep' (s :: Symbol) (xs :: [Symbol]) :: Symbol where+ CommaSep' s '[] = s+ CommaSep' s (x ': xs) = CommaSep' (s `AppendSymbol` ", " `AppendSymbol` x) xs++type DuplicateAttrErr attr+ = QuoteSym attr+ :<>: 'Text " is already specified."++type DuplicateAttrMultipleErr attr rest+ = QuoteSym attr+ :<>: 'Text (CommaSep rest)+ :<>: 'Text " has already been specified."++type IncompatibleAttrsErr l r+ = QuoteSym l+ :<>: 'Text " and "+ :<>: QuoteSym r+ :<>: 'Text " cannot be mixed in an option definition."
src/Options/Harg/Operations.hs view
@@ -6,23 +6,26 @@ {-# LANGUAGE UndecidableInstances #-} module Options.Harg.Operations where -import Data.Functor.Identity (Identity(..))+import Data.Functor.Identity (Identity(..)) -import qualified Data.Barbie as B-import qualified Options.Applicative as Optparse+import qualified Data.Barbie as B+import qualified Options.Applicative as Optparse -import Options.Harg.Cmdline (mkOptparseParser)-import Options.Harg.Config (mkConfigParser, getConfig)-import Options.Harg.Het.All (All)-import Options.Harg.Het.HList (AssocListF, MapAssocList(..))-import Options.Harg.Het.Variant (VariantF)-import Options.Harg.Pretty (ppWarning, ppError)-import Options.Harg.Sources (accumSourceResults, defaultSources)-import Options.Harg.Sources.Env (EnvSourceVal)-import Options.Harg.Sources.Types (GetSource(..), RunSource(..))-import Options.Harg.Subcommands (Subcommands(..))-import Options.Harg.Types (HargCtx(..), getCtx, Opt, OptError)-import Options.Harg.Util (toDummyOpts, allToDummyOpts, compose)+import Options.Harg.Cmdline (mkOptparseParser)+import Options.Harg.Config (mkConfigParser, getConfig)+import Options.Harg.Het.All (All)+import Options.Harg.Het.HList (AssocListF, MapAssocList(..))+import Options.Harg.Het.Prod ((:*)(..))+import Options.Harg.Het.Variant (VariantF)+import Options.Harg.Pretty (ppWarning, ppError)+import Options.Harg.Sources ( accumSourceResults+ , DefaultSources, defaultSources+ , HiddenSources, hiddenSources+ )+import Options.Harg.Sources.Types (GetSource(..), RunSource(..))+import Options.Harg.Subcommands (Subcommands(..))+import Options.Harg.Types (HargCtx(..), getCtx, Opt, OptError)+import Options.Harg.Util (toDummyOpts, allToDummyOpts, compose) -- | Run the option parser and combine with values from the specified sources,@@ -43,7 +46,7 @@ execOptWithCtx ctx conf opts = do let- configParser = mkConfigParser ctx (compose Identity conf)+ configParser = mkConfigParser ctx $ compose Identity (conf :* hiddenSources) dummyParser = mkOptparseParser [] (toDummyOpts @String opts) config <- getConfig ctx configParser dummyParser sourceVals <- getSource ctx config@@ -107,7 +110,7 @@ , B.ProductB c , Subcommands ts xs , GetSource c Identity- , All (RunSource (SourceVal c)) xs+ , All (RunSource (SourceVal (c :* HiddenSources))) xs , All (RunSource ()) xs , MapAssocList xs )@@ -118,7 +121,7 @@ execCommandsWithCtx ctx conf opts = do let- configParser = mkConfigParser ctx (compose Identity conf)+ configParser = mkConfigParser ctx $ compose Identity (conf :* hiddenSources) (_, dummyCommands) = mapSubcommand () (allToDummyOpts @String opts) dummyParser@@ -144,7 +147,7 @@ , B.ProductB c , Subcommands ts xs , GetSource c Identity- , All (RunSource (SourceVal c)) xs+ , All (RunSource (SourceVal (c :* HiddenSources))) xs , All (RunSource ()) xs , MapAssocList xs )@@ -162,7 +165,7 @@ :: forall ts xs. ( B.TraversableB (VariantF xs) , Subcommands ts xs- , All (RunSource EnvSourceVal) xs+ , All (RunSource (SourceVal (DefaultSources :* HiddenSources))) xs , All (RunSource ()) xs , MapAssocList xs )@@ -178,7 +181,7 @@ :: forall ts xs. ( B.TraversableB (VariantF xs) , Subcommands ts xs- , All (RunSource EnvSourceVal) xs+ , All (RunSource (SourceVal (DefaultSources :* HiddenSources))) xs , All (RunSource ()) xs , MapAssocList xs )
src/Options/Harg/Sources.hs view
@@ -9,6 +9,7 @@ import qualified Data.Barbie as B +import Options.Harg.Sources.DefaultStr import Options.Harg.Sources.Env import Options.Harg.Sources.Types import Options.Harg.Types@@ -40,7 +41,16 @@ OptParsed a -> ([], Compose (Just a)) _ -> ([], Compose Nothing) +type HiddenSources = DefaultStrSource++-- | Sources hidden from user that are always enabled+hiddenSources :: HiddenSources f+hiddenSources+ = DefaultStrSource++type DefaultSources = EnvSource+ -- | Default sources, equivalent to 'EnvSource'-defaultSources :: EnvSource f+defaultSources :: DefaultSources f defaultSources = EnvSource
+ src/Options/Harg/Sources/DefaultStr.hs view
@@ -0,0 +1,57 @@+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE TypeFamilies #-}+module Options.Harg.Sources.DefaultStr where++import Data.Functor.Compose (Compose (..))+import Data.Kind (Type)+import GHC.Generics (Generic)++import qualified Data.Barbie as B++import Options.Harg.Sources.Types+import Options.Harg.Types+++-- | Source that enables a parser to read options from defaults that are provided+-- as strings (unparsed).+data DefaultStrSource (f :: Type -> Type) = DefaultStrSource+ deriving (Generic, B.FunctorB, B.TraversableB, B.ProductB)++-- | Value of 'DefaultStrSource' is a dummy value, as the default string option can+-- be found inside the 'Opt' ('_optDefaultStr').+data DefaultStrSourceVal = DefaultStrSourceVal++instance GetSource DefaultStrSource f where+ type SourceVal DefaultStrSource = DefaultStrSourceVal+ getSource HargCtx{..} _+ = pure DefaultStrSourceVal++instance+ B.FunctorB a => RunSource DefaultStrSourceVal a where+ runSource DefaultStrSourceVal opt+ = [runDefaultStrSource opt]++runDefaultStrSource+ :: forall a f.+ ( B.FunctorB a+ , Applicative f+ )+ => a (Compose Opt f)+ -> a (Compose SourceRunResult f)+runDefaultStrSource+ = B.bmap go+ where+ go :: Compose Opt f x -> Compose SourceRunResult f x+ go (Compose opt@Opt{..})+ = case _optDefaultStr of+ Nothing+ -> Compose $ pure <$> OptNotFound+ Just str+ -> Compose $ tryParse str+ where+ tryParse+ = either+ (OptFoundNoParse . toOptError opt (Just "DefaultStrSource"))+ OptParsed+ . _optReader
src/Options/Harg/Types.hs view
@@ -8,15 +8,16 @@ -- | The basic option type data Opt a = Opt- { _optLong :: Maybe String -- ^ Modifier for long options (e.g. @--user@)- , _optShort :: Maybe Char -- ^ Modifier for short options (e.g. @-u@)- , _optHelp :: Maybe String -- ^ Option help to be shown when invoked- -- with @--help/-h@ or in case of error- , _optMetavar :: Maybe String -- ^ Metavar to be shown in the help description- , _optEnvVar :: Maybe String -- ^ Environment variable for use with 'EnvSource'- , _optDefault :: Maybe a -- ^ Default value- , _optReader :: OptReader a -- ^ Option parser- , _optType :: OptType a -- ^ Option type+ { _optLong :: Maybe String -- ^ Modifier for long options (e.g. @--user@)+ , _optShort :: Maybe Char -- ^ Modifier for short options (e.g. @-u@)+ , _optHelp :: Maybe String -- ^ Option help to be shown when invoked+ -- with @--help/-h@ or in case of error+ , _optMetavar :: Maybe String -- ^ Metavar to be shown in the help description+ , _optEnvVar :: Maybe String -- ^ Environment variable for use with 'EnvSource'+ , _optDefault :: Maybe a -- ^ Default value+ , _optDefaultStr :: Maybe String -- ^ Default value as string (unparsed)+ , _optReader :: OptReader a -- ^ Option parser+ , _optType :: OptType a -- ^ Option type } deriving Functor @@ -36,13 +37,14 @@ -- | Option for flags with arguments. Corresponds to 'Options.Applicative.option'. data OptionOpt (attr :: [OptAttr]) a = OptionOpt- { _oLong :: Maybe String- , _oShort :: Maybe Char- , _oHelp :: Maybe String- , _oMetavar :: Maybe String- , _oEnvVar :: Maybe String- , _oDefault :: Maybe a- , _oReader :: OptReader a+ { _oLong :: Maybe String+ , _oShort :: Maybe Char+ , _oHelp :: Maybe String+ , _oMetavar :: Maybe String+ , _oEnvVar :: Maybe String+ , _oDefault :: Maybe a+ , _oDefaultStr :: Maybe String+ , _oReader :: OptReader a } -- | Option for flags that act like switches between a default and an active@@ -62,11 +64,12 @@ -- 'Options.Applicative.argument'. data ArgumentOpt (attr :: [OptAttr]) a = ArgumentOpt- { _aHelp :: Maybe String- , _aMetavar :: Maybe String- , _aEnvVar :: Maybe String- , _aDefault :: Maybe a- , _aReader :: OptReader a+ { _aHelp :: Maybe String+ , _aMetavar :: Maybe String+ , _aEnvVar :: Maybe String+ , _aDefault :: Maybe a+ , _aDefaultStr :: Maybe String+ , _aReader :: OptReader a } -- | Datatype that holds errors that arise when running the sources.