cfg 0.0.2.3 → 0.0.2.4
raw patch · 6 files changed
+176/−9 lines, 6 filesdep ~basedep ~containersdep ~doctestPVP: minor bump suggested
API additions: PVP suggests at least a minor version bump
Dependency ranges changed: base, containers, doctest
API changes (from Hackage documentation)
+ Cfg: instance Cfg.Parser.ConfigParser Cfg.AppConfig6
+ Cfg: instance Cfg.Source.ConfigSource Cfg.AppConfig6
+ Cfg: instance Cfg.Source.Default.DefaultSource Cfg.AppConfig6
+ Cfg: instance GHC.Generics.Generic Cfg.AppConfig6
+ Cfg: instance GHC.Show.Show Cfg.AppConfig6
+ Cfg.Optional: OptionalConfig :: Maybe a -> OptionalConfig a
+ Cfg.Optional: [getOptionalConfig] :: OptionalConfig a -> Maybe a
+ Cfg.Optional: allAbsent :: KeyTree k v -> Bool
+ Cfg.Optional: instance Cfg.Parser.ConfigParser a => Cfg.Parser.ConfigParser (Cfg.Optional.OptionalConfig a)
+ Cfg.Optional: instance Cfg.Source.ConfigSource a => Cfg.Source.ConfigSource (Cfg.Optional.OptionalConfig a)
+ Cfg.Optional: instance GHC.Classes.Eq a => GHC.Classes.Eq (Cfg.Optional.OptionalConfig a)
+ Cfg.Optional: instance GHC.Classes.Ord a => GHC.Classes.Ord (Cfg.Optional.OptionalConfig a)
+ Cfg.Optional: instance GHC.Show.Show a => GHC.Show.Show (Cfg.Optional.OptionalConfig a)
+ Cfg.Optional: newtype OptionalConfig a
+ Cfg.Parser: ExpectedForestFoundValue :: Text -> ConfigParseError
Files
- cfg.cabal +6/−4
- src/Cfg.hs +16/−0
- src/Cfg/Optional.hs +25/−0
- src/Cfg/Parser.hs +8/−4
- src/Cfg/Parser/Config.hs +1/−1
- test/Cfg/OptionalSpec.hs +120/−0
cfg.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: cfg-version: 0.0.2.3+version: 0.0.2.4 synopsis: Type directed application configuration parsing and accessors @@ -59,8 +59,8 @@ UndecidableInstances build-depends:- , base >=4.16.0 && <4.20- , containers >=0.6 && <0.7+ , base >=4.16.0 && <5+ , containers >=0.6 , free >=5.0.1 && <5.3 , mtl >=2.1 && <2.4 , pretty-simple >=4.0 && <4.2@@ -86,6 +86,7 @@ Cfg.Deriving.Value Cfg.Env Cfg.Env.Keys+ Cfg.Optional Cfg.Options Cfg.Parser Cfg.Parser.Config@@ -112,6 +113,7 @@ Cfg.Deriving.KeyModifierSpec Cfg.Env.KeysSpec Cfg.EnvSpec+ Cfg.OptionalSpec Cfg.ParserSpec Cfg.SourceSpec Spec@@ -134,4 +136,4 @@ main-is: Main.hs build-depends: , cfg- , doctest >=0.21.1 && <0.23+ , doctest >=0.21.1
src/Cfg.hs view
@@ -341,6 +341,7 @@ import Data.Text (Text) import GHC.Generics import KeyTree+import Cfg.Optional (OptionalConfig) -- | @since 0.0.1.0 getConfigRaw@@ -479,5 +480,20 @@ via (ConfigOpts [StripPrefix "appConfig", StripSuffix "Settings", ToUpper] AppConfig5) instance DefaultSource AppConfig5 where+ defaults "appConfigEnvironment" = Just "Development"+ defaults _ = Nothing++-- Example 6+data AppConfig6 = AppConfig6+ { appConfigWarpSettings :: OptionalConfig WarpConfig+ , appConfigRedisSettings :: RedisConfig+ , appConfigEnvironment :: Environment+ }+ deriving (Generic, Show)+ deriving+ (ConfigSource, ConfigParser)+ via (ConfigOpts [StripPrefix "appConfig", StripSuffix "Settings", ToUpper] AppConfig6)++instance DefaultSource AppConfig6 where defaults "appConfigEnvironment" = Just "Development" defaults _ = Nothing
+ src/Cfg/Optional.hs view
@@ -0,0 +1,25 @@+module Cfg.Optional where++import Cfg.Parser (ConfigParser (..), ConfigParseError (ExpectedForestFoundValue))+import Cfg.Source (ConfigSource (..))+import Control.Monad.Free+import Data.Map.Strict qualified as M+import KeyTree++-- True when no leaf in the subtree carries a value.+allAbsent :: KeyTree k v -> Bool+allAbsent (Free m) = all allAbsent (M.elems m)+allAbsent (Pure _) = False++newtype OptionalConfig a = OptionalConfig {getOptionalConfig :: Maybe a}+ deriving newtype (Eq, Ord, Show)++instance ConfigParser a => ConfigParser (OptionalConfig a) where+ parseConfig cfg@(Free _) =+ if allAbsent cfg+ then Right $ OptionalConfig Nothing+ else OptionalConfig . Just <$> parseConfig cfg+ parseConfig (Pure val) = Left $ ExpectedForestFoundValue val++instance ConfigSource a => ConfigSource (OptionalConfig a) where+ configSource = configSource @a
src/Cfg/Parser.hs view
@@ -80,6 +80,10 @@ Text -- ^ The key that was missing Text+ | -- | Expected to find a subtree aka a 'Free' with a map in it, but instead+ -- we found a 'Pure'.+ ExpectedForestFoundValue+ Text -- ^ The value that was found | -- | Expected to find a 'Pure' with a value but instead found a subtree ExpectedValueFoundForest@@ -122,7 +126,7 @@ -- | @since 0.0.1.0 instance ValueParser () where- parser = string "()" >> pure ()+ parser = void $ string "()" -- | @since 0.0.1.0 instance ConfigParser ()@@ -203,7 +207,7 @@ -- | @since 0.0.2.0 minus :: Parser Text-minus = liftA2 (T.cons) (char '-') number+minus = liftA2 T.cons (char '-') number -- | @since 0.0.2.0 number :: Parser Text@@ -211,7 +215,7 @@ -- | @since 0.0.2.0 decimal :: Parser Text-decimal = option "" $ (T.cons) <$> char '.' <*> number+decimal = option "" $ T.cons <$> char '.' <*> number -- | @since 0.0.2.0 integral :: (Read a) => Parser a@@ -219,7 +223,7 @@ -- | @since 0.0.2.0 fractional :: (Read a) => Parser a-fractional = fmap rd $ liftA2 (<>) integral decimal+fractional = rd <$> liftA2 (<>) integral decimal -- | @since 0.0.1.0 instance ValueParser Double where
src/Cfg/Parser/Config.hs view
@@ -42,7 +42,7 @@ => ConfigOptions -> KeyTree Text Text -> Either ConfigParseError a-defaultParseConfig opts tree = fmap to $ gParseConfig opts tree+defaultParseConfig opts tree = to <$> gParseConfig opts tree -- | This class is the generic version of 'ConfigParser'. It recurses on the -- generic structure of a type, building up a return type for the parser.
+ test/Cfg/OptionalSpec.hs view
@@ -0,0 +1,120 @@+module Cfg.OptionalSpec where++import Cfg.Deriving.Config+import Cfg.Parser+import Data.Map.Strict (empty, fromList)+import Data.Text (Text)+import GHC.Generics (Generic (..))+import KeyTree+import Test.Hspec+import Cfg.Source (ConfigSource (..))+import Cfg.Optional (OptionalConfig (..))+import Cfg.Source.Default (DefaultSource)++data SubSubTyCon = SubSubDataCon+ { subSubKey1 :: Text+ , subSubKey2 :: Int+ }+ deriving (Generic, Show, Eq, DefaultSource)+ deriving (ConfigParser, ConfigSource) via (Config SubSubTyCon)++data SubTyCon = SubDataCon+ { subKey2 :: Int+ , subKey1 :: OptionalConfig SubSubTyCon+ }+ deriving (Generic, Show, Eq, DefaultSource)+ deriving (ConfigParser, ConfigSource) via (Config SubTyCon)++data RootTyCon = RootDataCon+ { key1 :: Bool+ , key2 :: SubTyCon+ }+ deriving (Generic, Show, Eq, DefaultSource)+ deriving (ConfigParser, ConfigSource) via (Config RootTyCon)++spec :: Spec+spec = do+ describe "configParser" $ do+ it "should parse a type from the sample config containing a present optional config" $ do+ let+ subSubConfig = SubSubDataCon "Hello World" 27+ subConfig = SubDataCon 64 (OptionalConfig $ Just subSubConfig)+ expected = RootDataCon True subConfig+ underTest =+ Free $+ fromList+ [ ("key1", Pure "True")+ ,+ ( "key2"+ , Free $+ fromList+ [ ("subKey1"+ , Free $ fromList+ [ ("subSubKey1", Pure "Hello World")+ , ("subSubKey2", Pure "27")+ ]+ )+ , ("subKey2", Pure "64")+ ]+ )+ ]+ parseConfig underTest `shouldBe` Right expected+ it "should parse a type from the sample config with a missing optional config" $ do+ let+ subConfig = SubDataCon 64 (OptionalConfig Nothing)+ expected = RootDataCon True subConfig+ underTest =+ Free $+ fromList+ [ ("key1", Pure "True")+ , ("key2"+ , Free $ fromList+ [ ("subKey2", Pure "64")+ , ("subKey1", Free empty)+ ]+ )+ ]+ parseConfig underTest `shouldBe` Right expected+ it "should parse Nothing when optional config has key stubs but no values (env source style)" $ do+ let+ subConfig = SubDataCon 64 (OptionalConfig Nothing)+ expected = RootDataCon True subConfig+ underTest =+ Free $+ fromList+ [ ("key1", Pure "True")+ , ("key2"+ , Free $ fromList+ [ ("subKey2", Pure "64")+ , ("subKey1"+ , Free $ fromList+ [ ("subSubKey1", Free empty)+ , ("subSubKey2", Free empty)+ ]+ )+ ]+ )+ ]+ parseConfig underTest `shouldBe` Right expected+ describe "configSource" $ do+ it "should create a tree from the sample config" $ do+ let+ expected =+ Free $+ fromList+ [ ("key1", Free empty)+ ,+ ( "key2"+ , Free $+ fromList+ [ ("subKey1"+ , Free $ fromList+ [ ("subSubKey1", Free empty)+ , ("subSubKey2", Free empty)+ ]+ )+ , ("subKey2", Free empty)+ ]+ )+ ]+ configSource @RootTyCon `shouldBe` expected