mmzk-env-0.6.0.0: src/Data/Env/TypeParserW.hs
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE UndecidableInstances #-}
-- |
-- Module: Data.Env.TypeParserW
-- Description: Type class provides parsers for types parameterized by witness types.
--
-- This module provides the 'TypeParserW' type class, which allows parsing
-- environment variables using witness types to control parsing behavior.
-- This enables multiple parsing strategies for the same type by using
-- different witness types.
module Data.Env.TypeParserW (
TypeParserW (..),
) where
import Data.Env.TypeParser
import Data.Proxy ( Proxy(..) )
import Data.Tuple ( Solo )
-- | Type class for parsers parameterized by a witness type.
--
-- This is similar to 'TypeParser'', but parameterised by a witness type @p@ that
-- determines the parsing strategy, giving you explicit control over behaviour.
--
-- The witness pattern is useful when you need multiple parsing strategies
-- for the same type or want to compose parsers in different ways.
--
-- The functional dependency @p -> a@ ensures that each witness type uniquely
-- determines the result type. For example, 'Data.Env.Witness.DefaultNum.DefaultNum' 5432 Int
-- uniquely determines the result type as Int.
--
-- See 'Data.Env.Witness.DefaultNum.DefaultNum' for an example of how to use witness types with
-- this class.
class TypeParserW p a | p -> a where
-- | Parse a value from its string representation.
parseTypeW :: Proxy p -> String -> Either String a
-- | Result to use when the environment variable is absent.
-- Default delegates to @'parseTypeW' proxy ""@. Override for witnesses that
-- delegate to 'TypeParser' — see the 'Solo' instance below.
parseMissingW :: Proxy p -> Either String a
parseMissingW proxy = parseTypeW proxy ""
{-# INLINE parseMissingW #-}
-- | Convenience wrapper that returns 'Maybe' instead of 'Either'.
parseTypeW' :: Proxy p -> String -> Maybe a
parseTypeW' proxy str = case parseTypeW proxy str of
Right val -> Just val
Left _ -> Nothing
instance TypeParser a => TypeParserW (Solo a) a where
parseTypeW :: Proxy (Solo a) -> String -> Either String a
parseTypeW _ = parseType
-- | Delegates to 'parseMissing' from 'TypeParser', so a 'Solo' field
-- with no default behaves the same as a plain 'TypeParser' field.
parseMissingW :: Proxy (Solo a) -> Either String a
parseMissingW _ = parseMissing @a
-- | Compose two witnesses into a pipeline: @p1@ preprocesses the raw string
-- (e.g. trims or validates it), and its output is fed as the input string to
-- @p2@, which resolves the final value of type @a@. Only @p1@ is constrained
-- to produce 'String' — @p2@ (and hence the composed witness) may resolve to
-- any type.
instance (TypeParserW p1 String, TypeParserW p2 a) => TypeParserW (p1, p2) a where
parseTypeW :: Proxy (p1, p2) -> String -> Either String a
parseTypeW _ str = parseTypeW @p1 Proxy str >>= parseTypeW @p2 Proxy