packages feed

mmzk-env 0.1.0.0 → 0.1.1.0

raw patch · 6 files changed

+89/−17 lines, 6 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Data.Env.EnumParser: EnumParser :: a -> EnumParser a
+ Data.Env.EnumParser: instance (GHC.Enum.Enum a, GHC.Show.Show a, GHC.Enum.Bounded a) => Data.Env.TypeParser.TypeParser (Data.Env.EnumParser.EnumParser a)
+ Data.Env.EnumParser: instance GHC.Classes.Eq a => GHC.Classes.Eq (Data.Env.EnumParser.EnumParser a)
+ Data.Env.EnumParser: instance GHC.Show.Show a => GHC.Show.Show (Data.Env.EnumParser.EnumParser a)
+ Data.Env.EnumParser: newtype EnumParser a

Files

CHANGELOG.md view
@@ -1,5 +1,10 @@ # Revision history for mmzk-env +## 0.1.1.0 -- 2025-09-28++* Added support for parsing enumerated types using `EnumParser`.+  * Updated documentation to include examples of enum parsing.+  ## 0.1.0.0 -- 2025-08-03 
README.md view
@@ -16,10 +16,10 @@  -- | Example: Define an environment schema data Config = Config-    { port :: Int-    , name :: String+    { port     :: Int+    , name     :: String     , mainHost :: String-    , debug :: Maybe Bool }+    , debug    :: Maybe Bool }     deriving (Show, Generic, EnvSchema)  -- | Run the validation@@ -34,3 +34,20 @@ With this setup, it requires the environment variables `PORT`, `NAME`, `MAIN_HOST`, and `DEBUG` to be set according to the types defined in the `Config` data type. The library will automatically parse these variables and validate them against the schema.  If any variable is missing or has an incorrect type, the validation will fail, and an error message will be printed.++## Enum Support++The library also supports automatic parsing of enumerated types. You can define an enum and derive the `TypeParser` instance using the helper type `EnumParser`.++The extension `DerivingVia` is required for this feature.++```Haskell+{-# LANGUAGE DerivingVia #-}++data Gender = Male | Female+  deriving (Show, Eq, Enum, Bounded)+  deriving TypeParser via (EnumParser Gender)++print $ parseEnum @Gender "Male"   -- Right Male+print $ parseEnum @Gender "Female" -- Right Female+```
mmzk-env.cabal view
@@ -1,6 +1,6 @@ cabal-version:      2.4 name:               mmzk-env-version:            0.1.0.0+version:            0.1.1.0  synopsis:           Read environment variables into a user-defined data type description:@@ -8,12 +8,12 @@     variables into user-defined data types, allowing for flexible and type-safe     configuration management. -homepage:           -bug-reports:        +homepage:           https://github.com/MMZK1526/mmzk-typeid+bug-reports:        https://github.com/MMZK1526/mmzk-typeid/issues license:            MIT author:             Yitang Chen <mmzk1526@outlook.com> maintainer:         Yitang Chen <mmzk1526@outlook.com>-category:            Data, Environment+category:           Data, Environment extra-source-files:     CHANGELOG.md     LICENSE@@ -26,6 +26,7 @@         BlockArguments         DeriveAnyClass         DeriveGeneric+        DerivingVia         FlexibleContexts         FlexibleInstances         InstanceSigs@@ -47,6 +48,7 @@     import:           settings     exposed-modules:         Data.Env+        Data.Env.EnumParser         Data.Env.ExtractFields         Data.Env.RecordParser         Data.Env.TypeParser@@ -63,6 +65,7 @@     type:             exitcode-stdio-1.0     other-modules:         Data.Env+        Data.Env.EnumParser         Data.Env.ExtractFields         Data.Env.RecordParser         Data.Env.TypeParser
+ src/Data/Env/EnumParser.hs view
@@ -0,0 +1,32 @@+{-# LANGUAGE UndecidableInstances #-}++-- |+-- Module: Data.Env.EnumParser+-- Description: A helper type for parsing Bounded Enums.+--+-- This module provides a 'TypeParser' instance for any type that is an+-- instance of 'Enum', 'Bounded', and 'Show'.+--+-- Example usage:+--+-- > data Gender = Male | Female+-- >   deriving (Show, Eq, Enum, Bounded)+-- >   deriving TypeParser via (EnumParser Gender)+-- >+-- > parseType @Gender "Male" `shouldBe` Right Male+-- > parseType @Gender "Female" `shouldBe` Right Female+-- > parseType @Gender "Other" `shouldSatisfy` isLeft+module Data.Env.EnumParser where++import           Data.Env.TypeParser (TypeParser(..))++-- | A helper type for parsing Bounded Enums.+newtype EnumParser a = EnumParser a+  deriving (Show, Eq)++instance (Enum a, Show a, Bounded a) => TypeParser (EnumParser a) where+  parseType s = case lookup s enumMap of+    Just v  -> Right (EnumParser v)+    Nothing -> Left $ "Cannot parse value: " ++ s ++ ". Valid values are: " ++ show (map fst enumMap)+    where+      enumMap = [(show e, e) | e <- [minBound..maxBound]]
src/Data/Env/ExtractFields.hs view
@@ -30,6 +30,9 @@  -- | Type class for extracting field names from a record type. class ExtractFields a where+  -- | Extract field names from a record type. It uses a 'Proxy' to avoid+  -- needing a value of type $a$, and we recommend using 'extractFields'+  -- instead.   extractFields' :: Proxy a -> [String]  -- | Extract field names from a record type.@@ -54,11 +57,6 @@ getEnvRawLowerToUpperSnake = getEnvRaw @a (map toUpper) {-# INLINE getEnvRawLowerToUpperSnake #-} ------------------------------------------------------------------------------------- Generic instances---------------------------------------------------------------------------------- -- | Retrieve environment variables based on field names, converting them from -- camel case (record field naming) to upper snake case (environment variable -- naming).@@ -67,6 +65,13 @@ getEnvRawCamelCaseToUpperSnake = getEnvRaw @a camelToUpperSnake {-# INLINE getEnvRawCamelCaseToUpperSnake #-} ++--------------------------------------------------------------------------------+-- Generic instances+--------------------------------------------------------------------------------++-- | Generic helper class for extracting field names from a generic+-- representation. class GExtractFields f where   gExtractFields :: Proxy f -> [String] 
test/Spec.hs view
@@ -1,4 +1,5 @@ import           Data.Either (isLeft)+import           Data.Env.EnumParser import           Data.Env.RecordParser import           Data.Env.TypeParser import           Data.Int (Int8, Int16, Int32, Int64)@@ -144,21 +145,30 @@       parseType @(Maybe Int) "hello" `shouldSatisfy` isLeft       parseType @(Maybe Bool) "fALSE" `shouldSatisfy` isLeft ++data Gender = Male | Female+  deriving (Show, Eq, Enum, Bounded)+  deriving TypeParser via (EnumParser Gender)+ data Person = Person-  { name :: String-  , age  :: Int+  { name   :: String+  , age    :: Int+  , gender :: Maybe Gender   } deriving (Show, Eq, Generic)  recordParserSpec :: Spec recordParserSpec = describe "parseRecord" do   it "parses a valid record" do     let env = M.fromList [("name", "Alice"), ("age", "30")]-    parseRecord @Person env `shouldBe` Right (Person "Alice" 30)+    parseRecord @Person env `shouldBe` Right (Person "Alice" 30 Nothing)   it "allows extra fields" do     let env = M.fromList [("name", "Alice"), ("age", "30"), ("extra", "value")]-    parseRecord @Person env `shouldBe` Right (Person "Alice" 30)+    parseRecord @Person env `shouldBe` Right (Person "Alice" 30 Nothing)+  it "parses optional fields" do+    let env = M.fromList [("name", "Alice"), ("age", "30"), ("gender", "Female")]+    parseRecord @Person env `shouldBe` Right (Person "Alice" 30 (Just Female))   it "fails to parse an invalid record" do-    let env = M.fromList [("name", "Alice"), ("age", "thirty")]+    let env = M.fromList [("name", "Alice"), ("age", "30"), ("gender", "Other")]     parseRecord @Person env `shouldSatisfy` isLeft   it "fails to parse a missing field" do     let env = M.fromList [("name", "Alice")]