packages feed

mmzk-env 0.2.1.1 → 0.3.0.0

raw patch · 6 files changed

+108/−66 lines, 6 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Data.Env.DefaultBool: DefaultBool :: Bool -> DefaultBool (def :: Bool)
- Data.Env.DefaultBool: boolDefault :: BoolDefault b => Proxy b -> Bool
- Data.Env.DefaultBool: class BoolDefault (b :: Bool)
- Data.Env.DefaultBool: instance Data.Env.DefaultBool.BoolDefault 'GHC.Types.False
- Data.Env.DefaultBool: instance Data.Env.DefaultBool.BoolDefault 'GHC.Types.True
- Data.Env.DefaultBool: instance Data.Env.DefaultBool.BoolDefault def => Data.Env.TypeParser.TypeParser (Data.Env.DefaultBool.DefaultBool def)
- Data.Env.DefaultBool: instance GHC.Classes.Eq (Data.Env.DefaultBool.DefaultBool def)
- Data.Env.DefaultBool: instance GHC.Classes.Ord (Data.Env.DefaultBool.DefaultBool def)
- Data.Env.DefaultBool: instance GHC.Show.Show (Data.Env.DefaultBool.DefaultBool def)
- Data.Env.DefaultBool: newtype DefaultBool (def :: Bool)
+ Data.Env.Witness.DefaultBool: data DefaultBool (b :: Bool) a
+ Data.Env.Witness.DefaultBool: instance Data.Env.Witness.DefaultBool.BoolVal 'GHC.Types.False
+ Data.Env.Witness.DefaultBool: instance Data.Env.Witness.DefaultBool.BoolVal 'GHC.Types.True
+ Data.Env.Witness.DefaultBool: instance Data.Env.Witness.DefaultBool.BoolVal b => Data.Env.TypeParserW.TypeParserW (Data.Env.Witness.DefaultBool.DefaultBool b GHC.Types.Bool) GHC.Types.Bool

Files

CHANGELOG.md view
@@ -1,6 +1,14 @@ # Revision history for mmzk-env  +## 0.3.0.0 -- 2026-03-22++* Fixed `DefaultBool` to follow the proper witness pattern:+  * Changed from a newtype with `TypeParser` to a phantom type with `TypeParserW`.+  * Added second type parameter (consistent with `DefaultNum` and `DefaultString`) enabling use with the `Di` type alias.+* `DefaultBool` now accepts `True`, `False`, `true`, `false`, `T`, `F`, `t`, `f`, `1`, `0`.++ ## 0.2.1.1 -- 2026-03-22  * Added `DefaultBool` for `Bool` fields with a type-level default value.
mmzk-env.cabal view
@@ -1,6 +1,6 @@ cabal-version:      2.4 name:               mmzk-env-version:            0.2.1.1+version:            0.3.0.0  synopsis:           Read environment variables into a user-defined data type description:@@ -51,13 +51,13 @@     import:           settings     exposed-modules:         Data.Env-        Data.Env.DefaultBool         Data.Env.EnumParser         Data.Env.ExtractFields         Data.Env.RecordParser         Data.Env.RecordParserW         Data.Env.TypeParser         Data.Env.TypeParserW+        Data.Env.Witness.DefaultBool         Data.Env.Witness.DefaultNum         Data.Env.Witness.DefaultString     build-depends:
− src/Data/Env/DefaultBool.hs
@@ -1,43 +0,0 @@--- |--- Module: Data.Env.DefaultBool--- Description: A helper type for Bool fields with a type-level default value.------ This module provides 'DefaultBool', a newtype wrapping 'Bool' whose--- 'TypeParser' instance returns a type-level default when the input is empty.------ Example usage:------ > data Config = Config--- >   { debug   :: DefaultBool 'False--- >   , verbose :: DefaultBool 'True--- >   } deriving (Show, Generic, EnvSchema)--- >--- > -- DEBUG unset → DefaultBool False--- > -- DEBUG=True  → DefaultBool True--- > -- DEBUG=False → DefaultBool False-module Data.Env.DefaultBool where--import Data.Env.TypeParser (TypeParser(..))-import Data.Proxy          (Proxy(..))---- | A 'Bool' field with a type-level default.------ When the environment variable is absent (empty string) the field takes the--- value @def@; otherwise the string is parsed as a regular 'Bool'.-newtype DefaultBool (def :: Bool) = DefaultBool Bool-  deriving (Show, Eq, Ord)---- | Reflect a type-level 'Bool' to a value-level 'Bool'.-class BoolDefault (b :: Bool) where-  boolDefault :: Proxy b -> Bool--instance BoolDefault 'True where-  boolDefault _ = True--instance BoolDefault 'False where-  boolDefault _ = False--instance BoolDefault def => TypeParser (DefaultBool def) where-  parseType "" = Right (DefaultBool (boolDefault (Proxy :: Proxy def)))-  parseType s  = DefaultBool <$> parseType s-  {-# INLINE parseType #-}
+ src/Data/Env/Witness/DefaultBool.hs view
@@ -0,0 +1,62 @@+{-# LANGUAGE MultiParamTypeClasses #-}++{- |+Witness type for parsing 'Bool' with a type-level default value.+-}+module Data.Env.Witness.DefaultBool (+  DefaultBool,+) where++import Data.Env.TypeParser+import Data.Env.TypeParserW+import Data.Proxy++-- | Witness type for parsing 'Bool' with a default value.+--+-- The type parameter @b@ is a type-level 'Bool' that specifies the default+-- value used when the environment variable is absent (empty string).+-- The type parameter @a@ is the parsed type (always 'Bool').+--+-- ==== __Examples__+--+-- >>> parseTypeW (Proxy @(DefaultBool 'False Bool)) ""+-- Right False+--+-- >>> parseTypeW (Proxy @(DefaultBool 'True Bool)) ""+-- Right True+--+-- >>> parseTypeW (Proxy @(DefaultBool 'False Bool)) "True"+-- Right True+--+-- >>> parseTypeW' (Proxy @(DefaultBool 'False Bool)) "invalid"+-- Nothing+data DefaultBool (b :: Bool) a++class BoolVal (b :: Bool) where+  boolVal :: Proxy b -> Bool++instance BoolVal 'True where+  boolVal _ = True++instance BoolVal 'False where+  boolVal _ = False++-- | Parse a 'Bool' with a default fallback.+--+-- When the input string is empty, returns the default value specified by the+-- type-level 'Bool' @b@. Otherwise, attempts to parse the string as 'Bool',+-- accepting @True@, @False@, @true@, @false@, @T@, @F@, @t@, @f@, @1@, @0@.+instance BoolVal b => TypeParserW (DefaultBool b Bool) Bool where+  parseTypeW :: Proxy (DefaultBool b Bool) -> String -> Either String Bool+  parseTypeW _ str = case str of+    ""      -> Right (boolVal (Proxy :: Proxy b))+    "true"  -> Right True+    "false" -> Right False+    "T"     -> Right True+    "F"     -> Right False+    "t"     -> Right True+    "f"     -> Right False+    "1"     -> Right True+    "0"     -> Right False+    _       -> parseType str+  {-# INLINE parseTypeW #-}
test/DefaultBoolSpec.hs view
@@ -1,23 +1,33 @@ module DefaultBoolSpec ( spec ) where  import Data.Either ( isLeft )-import Data.Env.DefaultBool-import Data.Env.TypeParser+import Data.Env.TypeParserW+import Data.Env.Witness.DefaultBool+import Data.Proxy import Test.Hspec  spec :: Spec spec = describe "DefaultBool" do-  it "parses True" do-    parseType @(DefaultBool 'False) "True" `shouldBe` Right (DefaultBool True)-    parseType @(DefaultBool 'True)  "True" `shouldBe` Right (DefaultBool True)-  it "parses False" do-    parseType @(DefaultBool 'False) "False" `shouldBe` Right (DefaultBool False)-    parseType @(DefaultBool 'True)  "False" `shouldBe` Right (DefaultBool False)   it "defaults to False when empty" do-    parseType @(DefaultBool 'False) "" `shouldBe` Right (DefaultBool False)+    parseTypeW (Proxy @(DefaultBool 'False Bool)) "" `shouldBe` Right False   it "defaults to True when empty" do-    parseType @(DefaultBool 'True) "" `shouldBe` Right (DefaultBool True)+    parseTypeW (Proxy @(DefaultBool 'True Bool)) "" `shouldBe` Right True+  it "parses True / False" do+    parseTypeW (Proxy @(DefaultBool 'False Bool)) "True"  `shouldBe` Right True+    parseTypeW (Proxy @(DefaultBool 'False Bool)) "False" `shouldBe` Right False+  it "parses true / false" do+    parseTypeW (Proxy @(DefaultBool 'False Bool)) "true"  `shouldBe` Right True+    parseTypeW (Proxy @(DefaultBool 'False Bool)) "false" `shouldBe` Right False+  it "parses T / F" do+    parseTypeW (Proxy @(DefaultBool 'False Bool)) "T" `shouldBe` Right True+    parseTypeW (Proxy @(DefaultBool 'False Bool)) "F" `shouldBe` Right False+  it "parses t / f" do+    parseTypeW (Proxy @(DefaultBool 'False Bool)) "t" `shouldBe` Right True+    parseTypeW (Proxy @(DefaultBool 'False Bool)) "f" `shouldBe` Right False+  it "parses 1 / 0" do+    parseTypeW (Proxy @(DefaultBool 'False Bool)) "1" `shouldBe` Right True+    parseTypeW (Proxy @(DefaultBool 'False Bool)) "0" `shouldBe` Right False   it "fails to parse other values" do-    parseType @(DefaultBool 'False) "1"     `shouldSatisfy` isLeft-    parseType @(DefaultBool 'False) "true"  `shouldSatisfy` isLeft-    parseType @(DefaultBool 'False) "false" `shouldSatisfy` isLeft+    parseTypeW (Proxy @(DefaultBool 'False Bool)) "yes"     `shouldSatisfy` isLeft+    parseTypeW (Proxy @(DefaultBool 'False Bool)) "no"      `shouldSatisfy` isLeft+    parseTypeW (Proxy @(DefaultBool 'False Bool)) "invalid" `shouldSatisfy` isLeft
test/RecordParserWSpec.hs view
@@ -11,6 +11,7 @@ import Data.Map qualified as M import GHC.Generics import Test.Hspec+import Data.Env.Witness.DefaultBool (DefaultBool)  data NameW @@ -23,9 +24,10 @@   data Config c = Config-  { port :: Di (DefaultNum 5432) c Int-  , env  :: Di Solo c String-  , name :: Column c NameW String+  { port  :: Di (DefaultNum 5432) c Int+  , env   :: Di Solo c String+  , name  :: Column c NameW String+  , debug :: Di (DefaultBool 'False) c Bool   }   deriving (Generic) @@ -34,15 +36,18 @@  spec :: Spec spec = describe "parseRecordW for Config" do-  it "uses default port 5432 when port not specified" do+  it "uses default port 5432 and default debug False when not specified" do     let envMap = M.fromList [("env", "production"), ("name", "name")]-    parseRecordW @(Config 'Dec) envMap `shouldBe` Right (Config 5432 "production" "name")-  it "parses config with custom port" do-    let envMap = M.fromList [("port", "8080"), ("env", "development"), ("name", "name")]-    parseRecordW @(Config 'Dec) envMap `shouldBe` Right (Config 8080 "development" "name")+    parseRecordW @(Config 'Dec) envMap `shouldBe` Right (Config 5432 "production" "name" False)+  it "parses config with custom port and explicit debug" do+    let envMap = M.fromList [("port", "8080"), ("env", "development"), ("name", "name"), ("debug", "True")]+    parseRecordW @(Config 'Dec) envMap `shouldBe` Right (Config 8080 "development" "name" True)   it "fails to parse invalid port value" do     let envMap = M.fromList [("port", "not-a-number"), ("env", "test"), ("name", "name")]     parseRecordW @(Config 'Dec) envMap `shouldSatisfy` isLeft   it "fails to parse invalid name value" do     let envMap = M.fromList [("env", "test"), ("name", "1234567890poiuytrewq")]+    parseRecordW @(Config 'Dec) envMap `shouldSatisfy` isLeft+  it "fails to parse invalid debug value" do+    let envMap = M.fromList [("env", "test"), ("name", "name"), ("debug", "yes")]     parseRecordW @(Config 'Dec) envMap `shouldSatisfy` isLeft