{-# LANGUAGE DuplicateRecordFields #-}

-- |
--  Module      : Cfg
--  Copyright   : © Jonathan Lorimer, 2023
--  License     : MIT
--  Maintainer  : jonathanlorimer@pm.me
--  Stability   : stable
--
-- @since 0.0.1.0
--
-- This package provides an api for representing configuration as a haskell
-- type. This entails three general considerations: a simplified
-- representation of our haskell type so that it maps better to existing
-- configuration formats, an adapter to translate between the simplified
-- representation and a concrete configuration "source" (i.e. environment
-- variables, yaml files, etc.), and a parser that can recover the structure of
-- the haskell type from the simplified representation.
--
-- While this package provides a default source (environment variables), the
-- intention is that other packages will provide additional sources.
module Cfg
  ( -- * Concepts

    -- |
    --
    -- The core concepts in this package are:
    --
    --    * __A simplified type representation:__ The type chosen to represent our
    --    underlying haskell type is 'KeyTree.KeyTree'. This reflects the
    --    potentially nested structure of configuration, and makes it easy
    --    simply append values as leaf nodes.
    --
    --    * __Sources:__ These represent a way to build a simplified representation
    --    from as Haskell type. Source may seem like an odd name, but other
    --    names like \"Rep\", or \"Representation\" are taken and overloaded.
    --    The tree structures created by the typeclasses in "Cfg.Source" are
    --    used to request values from a configuration source.
    --
    --    * __Parsers:__ Once a request for configuration values has been made to a
    --    source, and the actual values are appended as leaf nodes on the tree
    --    representation we require a parser to pull that information out and
    --    construct a Haskell type. The parser traverses the tree and makes sure
    --    that it structurally matches our Haskell type, and then it will parse the
    --    'Data.Text.Text' values at the leaves into actual Haskell types.
    --    The api that corresponds to this can be found in "Cfg.Parser".
    --
    --    * __Deriving:__ It is a design principle of this library that the
    --    vast majority (if not all) functionality should be derivable. For
    --    this we use "GHC.Generics", and [deriving
    --    via](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/deriving_via.html).
    --    You can always hand write instances for custom functionality, but
    --    there are also a handful of options that can be specified using the
    --    deriving machinery. Documentation on those options can be found in
    --    "Cfg.Deriving".

    -- * Quickstart guide

    -- |
    --
    -- Here we will introduce some sample code that should get you up and running
    -- quickly. We will also explain some of the internals so you can see how
    -- things are wired together

    -- ** Initial configuration

    -- |
    --
    -- Let's start out with a couple types that represent some imaginary
    -- configuration for an imaginary application. This is the most basic
    -- kind of configuration we can have.
    --
    -- You will probably notice that records derive their instances via a
    -- `Config` newtype, while base types (types that represent the actual
    -- configuration values) are derived via a `Value` newtype. Values first
    -- derive a `ValueParser` instance, and then that makes it possible to
    -- derive a `ConfigParser` instance (no deriving via machinery necessary
    -- for that instance).
    --
    -- @
    -- {\-# LANGUAGE DeriveGeneric #-\}
    -- {\-# LANGUAGE DerivingVia #-\}
    --
    -- import "Cfg.Deriving.Config"
    -- import "Cfg.Deriving.Value"
    -- import "Cfg.Parser"
    -- import "Cfg.Source"
    -- import "Cfg.Source.Default"
    -- import Data.ByteString (ByteString)
    -- import GHC.Generics
    --
    -- data Environment = Development | Production
    --   deriving (Generic, Show, 'DefaultSource')
    --   deriving ('ConfigSource', 'ValueParser') via ('Value' Environment)
    --   -- Note: This is derivable via ConigParser's default instance, because we provided a ValueParser instance
    --   deriving ('ConfigParser')
    --
    -- data WarpConfig = WarpConfig
    --   { warpConfigPort :: Int
    --   , warpConfigTimeout :: Int
    --   , warpConfigHTTP2Enabled :: Bool
    --   , warpConfigServerName :: ByteString
    --   }
    --   deriving (Generic, Show, 'DefaultSource')
    --   deriving ('ConfigSource', 'ConfigParser') via ('Config' WarpConfig)
    --
    -- data RedisConfig = RedisConfig
    --   { redisConfigHost :: Text
    --   , redisConfigPort :: Int
    --   , redisConfigConnectAuth :: Maybe ByteString
    --   }
    --   deriving (Generic, Show, 'DefaultSource')
    --   deriving ('ConfigSource', 'ConfigParser') via ('Config' RedisConfig)
    --
    -- data AppConfig = AppConfig
    --   { appConfigWarpSettings :: WarpConfig
    --   , appConfigRedisSettings :: RedisConfig
    --   , appConfigEnvironment :: Environment
    --   }
    --   deriving (Generic, Show, 'DefaultSource')
    --   deriving ('ConfigSource', 'ConfigParser') via ('Config' AppConfig)
    -- @
    --
    -- And here is the result of generating the keys for this configuration setup
    --
    -- >>> import Cfg
    -- >>> import Text.Pretty.Simple
    -- >>> import Cfg.Env.Keys
    -- >>> pPrint $ showEnvKeys @AppConfig "_"
    -- [ "appConfigEnvironment"
    -- , "appConfigRedisSettings_redisConfigConnectAuth"
    -- , "appConfigRedisSettings_redisConfigHost"
    -- , "appConfigRedisSettings_redisConfigPort"
    -- , "appConfigWarpSettings_warpConfigHTTP2Enabled"
    -- , "appConfigWarpSettings_warpConfigPort"
    -- , "appConfigWarpSettings_warpConfigServerName"
    -- , "appConfigWarpSettings_warpConfigTimeout"
    -- ]

    -- ** Basic key modifiers

    -- |
    --
    -- This is okay, but there are some changes we may want to make. We will go
    -- through a series of tweaks to the example above to format the keys. We
    -- format the keys by providing formatting options through a new newtype
    -- 'ConfigOpts' that accepts a type parameter.
    --
    -- @
    -- data WarpConfig = ...
    --   deriving (Generic, Show, 'DefaultSource')
    --   deriving ('ConfigSource', 'ConfigParser') via ('ConfigOpts' 'ToUpper' WarpConfig)
    --
    -- data RedisConfig = ...
    --   deriving (Generic, Show, 'DefaultSource')
    --   deriving ('ConfigSource', 'ConfigParser') via ('ConfigOpts' 'ToUpper' WarpConfig)
    --
    -- data AppConfig = ...
    --   deriving (Generic, Show, 'DefaultSource')
    --   deriving ('ConfigSource', 'ConfigParser') via ('ConfigOpts' 'ToUpper' AppConfig)
    -- @
    --
    -- Let's print the keys out again (note, we have to add a numbered suffix
    -- to the constructor so we don't get namespace collisions in the doc
    -- tests)
    --
    -- >>> import Cfg
    -- >>> import Text.Pretty.Simple
    -- >>> import Cfg.Env.Keys
    -- >>> pPrint $ showEnvKeys @AppConfig2 "_"
    -- [ "APPCONFIGENVIRONMENT"
    -- , "APPCONFIGREDISSETTINGS_REDISCONFIGCONNECTAUTH"
    -- , "APPCONFIGREDISSETTINGS_REDISCONFIGHOST"
    -- , "APPCONFIGREDISSETTINGS_REDISCONFIGPORT"
    -- , "APPCONFIGWARPSETTINGS_WARPCONFIGHTTP2ENABLED"
    -- , "APPCONFIGWARPSETTINGS_WARPCONFIGPORT"
    -- , "APPCONFIGWARPSETTINGS_WARPCONFIGSERVERNAME"
    -- , "APPCONFIGWARPSETTINGS_WARPCONFIGTIMEOUT"
    -- ]

    -- ** Multiple key modifiers

    -- |
    --
    -- This is close, but we probably want to remove the record field suffixes
    -- for our configuration. We can provide more than one formatter through
    -- tuples (up to a cardinality of 4) or a type level list. These formatters
    -- apply in order from left to right.
    --
    -- @
    -- data WarpConfig = WarpConfig
    --   { warpConfigPort :: Int
    --   , warpConfigTimeout :: Int
    --   , warpConfigHTTP2Enabled :: Bool
    --   , warpConfigServerName :: ByteString
    --   }
    --   deriving (Generic, Show, 'DefaultSource')
    --   deriving ('ConfigSource', 'ConfigParser') via ('ConfigOpts' ('StripPrefix' "warpConfig", 'ToUpper') WarpConfig)
    --
    -- data RedisConfig = RedisConfig
    --   { redisConfigHost :: Text
    --   , redisConfigPort :: Int
    --   , redisConfigConnectAuth :: Maybe ByteString
    --   }
    --   deriving (Generic, Show, 'DefaultSource')
    --   deriving ('ConfigSource', 'ConfigParser') via ('ConfigOpts' ['StripPrefix' "redisConfig", 'ToUpper'] WarpConfig)
    --
    -- data AppConfig = AppConfig
    --   { appConfigWarpSettings :: WarpConfig
    --   , appConfigRedisSettings :: RedisConfig
    --   , appConfigEnvironment :: Environment
    --   }
    --   deriving (Generic, Show, 'DefaultSource')
    --   deriving ('ConfigSource', 'ConfigParser')
    --    via ('ConfigOpts' ['StripPrefix' "appConfig", 'StripSuffix' \"Settings\", ToUpper] AppConfig)
    -- @
    --
    -- >>> import Cfg
    -- >>> import Text.Pretty.Simple
    -- >>> import Cfg.Env.Keys
    -- >>> pPrint $ showEnvKeys @AppConfig3 "_"
    -- [ "ENVIRONMENT"
    -- , "REDIS_CONNECTAUTH"
    -- , "REDIS_HOST"
    -- , "REDIS_PORT"
    -- , "WARP_HTTP2ENABLED"
    -- , "WARP_PORT"
    -- , "WARP_SERVERNAME"
    -- , "WARP_TIMEOUT"
    -- ]

    -- ** Root key

    -- |
    --
    -- This is much better, but we might even want to go a step further and
    -- namespace our config with a rootkey. We can do this by deriving via a
    -- special type on our root config record.
    --
    -- @
    -- data AppConfig = AppConfig
    --   { appConfigWarpSettings :: WarpConfig
    --   , appConfigRedisSettings :: RedisConfig
    --   , appConfigEnvironment :: Environment
    --   }
    -- deriving (Generic, Show, 'DefaultSource')
    -- deriving ('ConfigSource', 'ConfigParser')
    --   via (
    --     'ConfigRoot'
    --       (''TypeName' ['StripSuffix' "Config", 'ToUpper'])
    --       ['StripPrefix' "appConfig", 'StripSuffix' \"Settings\", 'ToUpper']
    --       AppConfig
    --   )
    -- @
    --
    -- The first parameter to 'ConfigRoot' is either 'TypeName' or
    -- 'ConstructorName', this indicates which name will be used for the root
    -- key. You can then provide key formatters to manipulate that name.
    --
    -- >>> import Cfg
    -- >>> import Text.Pretty.Simple
    -- >>> import Cfg.Env.Keys
    -- >>> pPrint $ showEnvKeys @AppConfig4 "_"
    -- [ "APP_ENVIRONMENT"
    -- , "APP_REDIS_CONNECTAUTH"
    -- , "APP_REDIS_HOST"
    -- , "APP_REDIS_PORT"
    -- , "APP_WARP_HTTP2ENABLED"
    -- , "APP_WARP_PORT"
    -- , "APP_WARP_SERVERNAME"
    -- , "APP_WARP_TIMEOUT"
    -- ]

    -- ** Defaults

    -- |
    --
    -- The defaulting machinery is admittedly a bit crude. You must define a
    -- 'DefaultSource' instance for the record that contains the value you want
    -- to default. The reason the defaulting needs to be defined on the record
    -- is that we use the record field key to identify the defaulted value the
    -- value you want to default.
    --
    -- __There are a bunch of gotchas with defaulting__:
    --
    --    - Since the type of 'defaults' is @Text -> Maybe Text@, the onus is
    --    on the implementor to make sure that this function correctly matches
    --    the record field name.
    --
    --    - The way it is currently implemented we use the 'defaults' function
    --    on the record field /before/ applying key modifiers.
    --
    --    - If there is a mismatch it will fail silently by not defaulting.
    --    This may result in an error when parsing (due to a missing value).
    --
    --    - You can only set defaults via their textual representation, so your
    --    defaults might fail to parse!
    --
    --    - If you declare a default on a field that is supposed to hold nested config this will break, and there is nothing at the type level to prevent you from making this mistake
    --
    -- Considering all of the above, it may be preferable to do some defaulting
    -- on the configuration side (i.e. make sure default environment variables
    -- are set, or provide default configuration files that can be modified).
    --
    -- If you still want to do defaulting on the haskell side here is how:
    --
    -- @
    -- data AppConfig = AppConfig
    --   { appConfigWarpSettings :: WarpConfig
    --   , appConfigRedisSettings :: RedisConfig
    --   , appConfigEnvironment :: Environment
    --   }
    --   deriving (Generic, Show)
    --   deriving ('ConfigSource', 'ConfigParser')
    --    via ('ConfigOpts' ['StripPrefix' "appConfig", 'StripSuffix' \"Settings\", ToUpper] AppConfig)
    --
    -- -- NOTE: If I provide a default for WarpConfig or RedisConfig this will break the configuration machinery
    -- -- so I only match on the field for @Environment@
    --
    -- instance 'DefaultSource' AppConfig where
    --   'defaults' "appConfigEnvironment" = Just "Development"
    --   'defaults' _ = Nothing
    -- @

    -- * Exports
    getConfigRaw
  , getConfig
  )
where

-- Haddock example imports

import Cfg.Deriving
import Cfg.Options
import Cfg.Parser
import Cfg.Parser ()
import Cfg.Source
import Cfg.Source ()
import Cfg.Source.Default
import Data.ByteString (ByteString)
import Data.Text (Text)
import GHC.Generics
import KeyTree

-- | @since 0.0.1.0
getConfigRaw
  :: (Monad m)
  => KeyTree Text Text
  -> (KeyTree Text Text -> m (KeyTree Text Text))
  -> (KeyTree Text Text -> Either e a)
  -> m (Either e a)
getConfigRaw :: forall (m :: * -> *) e a.
Monad m =>
KeyTree Text Text
-> (KeyTree Text Text -> m (KeyTree Text Text))
-> (KeyTree Text Text -> Either e a)
-> m (Either e a)
getConfigRaw KeyTree Text Text
keyTree KeyTree Text Text -> m (KeyTree Text Text)
source KeyTree Text Text -> Either e a
parse = KeyTree Text Text -> Either e a
parse (KeyTree Text Text -> Either e a)
-> m (KeyTree Text Text) -> m (Either e a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> KeyTree Text Text -> m (KeyTree Text Text)
source KeyTree Text Text
keyTree

-- | @since 0.0.1.0
getConfig
  :: forall a m
   . (Monad m, ConfigSource a, ConfigParser a)
  => FetchSource m
  -> m (Either ConfigParseError a)
getConfig :: forall a (m :: * -> *).
(Monad m, ConfigSource a, ConfigParser a) =>
FetchSource m -> m (Either ConfigParseError a)
getConfig FetchSource m
fetch = forall a.
ConfigParser a =>
KeyTree Text Text -> Either ConfigParseError a
parseConfig @a (KeyTree Text Text -> Either ConfigParseError a)
-> m (KeyTree Text Text) -> m (Either ConfigParseError a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> FetchSource m
fetch (forall a. ConfigSource a => KeyTree Text Text
forall {k} (a :: k). ConfigSource a => KeyTree Text Text
configSource @a)

-------------------------------------------------------
-- Examples for haddocks
-------------------------------------------------------
data Environment = Development | Production
  deriving ((forall x. Environment -> Rep Environment x)
-> (forall x. Rep Environment x -> Environment)
-> Generic Environment
forall x. Rep Environment x -> Environment
forall x. Environment -> Rep Environment x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. Environment -> Rep Environment x
from :: forall x. Environment -> Rep Environment x
$cto :: forall x. Rep Environment x -> Environment
to :: forall x. Rep Environment x -> Environment
Generic, Int -> Environment -> ShowS
[Environment] -> ShowS
Environment -> String
(Int -> Environment -> ShowS)
-> (Environment -> String)
-> ([Environment] -> ShowS)
-> Show Environment
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Environment -> ShowS
showsPrec :: Int -> Environment -> ShowS
$cshow :: Environment -> String
show :: Environment -> String
$cshowList :: [Environment] -> ShowS
showList :: [Environment] -> ShowS
Show, Text -> Maybe Text
(Text -> Maybe Text) -> DefaultSource Environment
forall {k} (a :: k). (Text -> Maybe Text) -> DefaultSource a
$cdefaults :: Text -> Maybe Text
defaults :: Text -> Maybe Text
DefaultSource)
  deriving (KeyTree Text Text
KeyTree Text Text -> ConfigSource Environment
forall {k} (a :: k). KeyTree Text Text -> ConfigSource a
$cconfigSource :: KeyTree Text Text
configSource :: KeyTree Text Text
ConfigSource, Parser Environment
Parser Environment -> ValueParser Environment
forall a. Parser a -> ValueParser a
$cparser :: Parser Environment
parser :: Parser Environment
ValueParser) via (Value Environment)
  deriving (KeyTree Text Text -> Either ConfigParseError Environment
(KeyTree Text Text -> Either ConfigParseError Environment)
-> ConfigParser Environment
forall a.
(KeyTree Text Text -> Either ConfigParseError a) -> ConfigParser a
$cparseConfig :: KeyTree Text Text -> Either ConfigParseError Environment
parseConfig :: KeyTree Text Text -> Either ConfigParseError Environment
ConfigParser) -- Note: This is derivable via ConigParser's default instance, because we provided a ValueParser instance

-- Example 1

data WarpConfig = WarpConfig
  { WarpConfig -> Int
warpConfigPort :: Int
  , WarpConfig -> Int
warpConfigTimeout :: Int
  , WarpConfig -> Bool
warpConfigHTTP2Enabled :: Bool
  , WarpConfig -> ByteString
warpConfigServerName :: ByteString
  }
  deriving ((forall x. WarpConfig -> Rep WarpConfig x)
-> (forall x. Rep WarpConfig x -> WarpConfig) -> Generic WarpConfig
forall x. Rep WarpConfig x -> WarpConfig
forall x. WarpConfig -> Rep WarpConfig x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. WarpConfig -> Rep WarpConfig x
from :: forall x. WarpConfig -> Rep WarpConfig x
$cto :: forall x. Rep WarpConfig x -> WarpConfig
to :: forall x. Rep WarpConfig x -> WarpConfig
Generic, Int -> WarpConfig -> ShowS
[WarpConfig] -> ShowS
WarpConfig -> String
(Int -> WarpConfig -> ShowS)
-> (WarpConfig -> String)
-> ([WarpConfig] -> ShowS)
-> Show WarpConfig
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> WarpConfig -> ShowS
showsPrec :: Int -> WarpConfig -> ShowS
$cshow :: WarpConfig -> String
show :: WarpConfig -> String
$cshowList :: [WarpConfig] -> ShowS
showList :: [WarpConfig] -> ShowS
Show, Text -> Maybe Text
(Text -> Maybe Text) -> DefaultSource WarpConfig
forall {k} (a :: k). (Text -> Maybe Text) -> DefaultSource a
$cdefaults :: Text -> Maybe Text
defaults :: Text -> Maybe Text
DefaultSource)
  deriving (KeyTree Text Text
KeyTree Text Text -> ConfigSource WarpConfig
forall {k} (a :: k). KeyTree Text Text -> ConfigSource a
$cconfigSource :: KeyTree Text Text
configSource :: KeyTree Text Text
ConfigSource, KeyTree Text Text -> Either ConfigParseError WarpConfig
(KeyTree Text Text -> Either ConfigParseError WarpConfig)
-> ConfigParser WarpConfig
forall a.
(KeyTree Text Text -> Either ConfigParseError a) -> ConfigParser a
$cparseConfig :: KeyTree Text Text -> Either ConfigParseError WarpConfig
parseConfig :: KeyTree Text Text -> Either ConfigParseError WarpConfig
ConfigParser) via (Config WarpConfig)

data RedisConfig = RedisConfig
  { RedisConfig -> Text
redisConfigHost :: Text
  , RedisConfig -> Int
redisConfigPort :: Int
  , RedisConfig -> Maybe ByteString
redisConfigConnectAuth :: Maybe ByteString
  }
  deriving ((forall x. RedisConfig -> Rep RedisConfig x)
-> (forall x. Rep RedisConfig x -> RedisConfig)
-> Generic RedisConfig
forall x. Rep RedisConfig x -> RedisConfig
forall x. RedisConfig -> Rep RedisConfig x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. RedisConfig -> Rep RedisConfig x
from :: forall x. RedisConfig -> Rep RedisConfig x
$cto :: forall x. Rep RedisConfig x -> RedisConfig
to :: forall x. Rep RedisConfig x -> RedisConfig
Generic, Int -> RedisConfig -> ShowS
[RedisConfig] -> ShowS
RedisConfig -> String
(Int -> RedisConfig -> ShowS)
-> (RedisConfig -> String)
-> ([RedisConfig] -> ShowS)
-> Show RedisConfig
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> RedisConfig -> ShowS
showsPrec :: Int -> RedisConfig -> ShowS
$cshow :: RedisConfig -> String
show :: RedisConfig -> String
$cshowList :: [RedisConfig] -> ShowS
showList :: [RedisConfig] -> ShowS
Show, Text -> Maybe Text
(Text -> Maybe Text) -> DefaultSource RedisConfig
forall {k} (a :: k). (Text -> Maybe Text) -> DefaultSource a
$cdefaults :: Text -> Maybe Text
defaults :: Text -> Maybe Text
DefaultSource)
  deriving (KeyTree Text Text
KeyTree Text Text -> ConfigSource RedisConfig
forall {k} (a :: k). KeyTree Text Text -> ConfigSource a
$cconfigSource :: KeyTree Text Text
configSource :: KeyTree Text Text
ConfigSource, KeyTree Text Text -> Either ConfigParseError RedisConfig
(KeyTree Text Text -> Either ConfigParseError RedisConfig)
-> ConfigParser RedisConfig
forall a.
(KeyTree Text Text -> Either ConfigParseError a) -> ConfigParser a
$cparseConfig :: KeyTree Text Text -> Either ConfigParseError RedisConfig
parseConfig :: KeyTree Text Text -> Either ConfigParseError RedisConfig
ConfigParser) via (Config RedisConfig)

data AppConfig = AppConfig
  { AppConfig -> WarpConfig
appConfigWarpSettings :: WarpConfig
  , AppConfig -> RedisConfig
appConfigRedisSettings :: RedisConfig
  , AppConfig -> Environment
appConfigEnvironment :: Environment
  }
  deriving ((forall x. AppConfig -> Rep AppConfig x)
-> (forall x. Rep AppConfig x -> AppConfig) -> Generic AppConfig
forall x. Rep AppConfig x -> AppConfig
forall x. AppConfig -> Rep AppConfig x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. AppConfig -> Rep AppConfig x
from :: forall x. AppConfig -> Rep AppConfig x
$cto :: forall x. Rep AppConfig x -> AppConfig
to :: forall x. Rep AppConfig x -> AppConfig
Generic, Int -> AppConfig -> ShowS
[AppConfig] -> ShowS
AppConfig -> String
(Int -> AppConfig -> ShowS)
-> (AppConfig -> String)
-> ([AppConfig] -> ShowS)
-> Show AppConfig
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> AppConfig -> ShowS
showsPrec :: Int -> AppConfig -> ShowS
$cshow :: AppConfig -> String
show :: AppConfig -> String
$cshowList :: [AppConfig] -> ShowS
showList :: [AppConfig] -> ShowS
Show, Text -> Maybe Text
(Text -> Maybe Text) -> DefaultSource AppConfig
forall {k} (a :: k). (Text -> Maybe Text) -> DefaultSource a
$cdefaults :: Text -> Maybe Text
defaults :: Text -> Maybe Text
DefaultSource)
  deriving (KeyTree Text Text
KeyTree Text Text -> ConfigSource AppConfig
forall {k} (a :: k). KeyTree Text Text -> ConfigSource a
$cconfigSource :: KeyTree Text Text
configSource :: KeyTree Text Text
ConfigSource, KeyTree Text Text -> Either ConfigParseError AppConfig
(KeyTree Text Text -> Either ConfigParseError AppConfig)
-> ConfigParser AppConfig
forall a.
(KeyTree Text Text -> Either ConfigParseError a) -> ConfigParser a
$cparseConfig :: KeyTree Text Text -> Either ConfigParseError AppConfig
parseConfig :: KeyTree Text Text -> Either ConfigParseError AppConfig
ConfigParser) via (Config AppConfig)

-- Example 2
data WarpConfig2 = WarpConfig2
  { WarpConfig2 -> Int
warpConfigPort :: Int
  , WarpConfig2 -> Int
warpConfigTimeout :: Int
  , WarpConfig2 -> Bool
warpConfigHTTP2Enabled :: Bool
  , WarpConfig2 -> ByteString
warpConfigServerName :: ByteString
  }
  deriving ((forall x. WarpConfig2 -> Rep WarpConfig2 x)
-> (forall x. Rep WarpConfig2 x -> WarpConfig2)
-> Generic WarpConfig2
forall x. Rep WarpConfig2 x -> WarpConfig2
forall x. WarpConfig2 -> Rep WarpConfig2 x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. WarpConfig2 -> Rep WarpConfig2 x
from :: forall x. WarpConfig2 -> Rep WarpConfig2 x
$cto :: forall x. Rep WarpConfig2 x -> WarpConfig2
to :: forall x. Rep WarpConfig2 x -> WarpConfig2
Generic, Int -> WarpConfig2 -> ShowS
[WarpConfig2] -> ShowS
WarpConfig2 -> String
(Int -> WarpConfig2 -> ShowS)
-> (WarpConfig2 -> String)
-> ([WarpConfig2] -> ShowS)
-> Show WarpConfig2
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> WarpConfig2 -> ShowS
showsPrec :: Int -> WarpConfig2 -> ShowS
$cshow :: WarpConfig2 -> String
show :: WarpConfig2 -> String
$cshowList :: [WarpConfig2] -> ShowS
showList :: [WarpConfig2] -> ShowS
Show, Text -> Maybe Text
(Text -> Maybe Text) -> DefaultSource WarpConfig2
forall {k} (a :: k). (Text -> Maybe Text) -> DefaultSource a
$cdefaults :: Text -> Maybe Text
defaults :: Text -> Maybe Text
DefaultSource)
  deriving (KeyTree Text Text
KeyTree Text Text -> ConfigSource WarpConfig2
forall {k} (a :: k). KeyTree Text Text -> ConfigSource a
$cconfigSource :: KeyTree Text Text
configSource :: KeyTree Text Text
ConfigSource, KeyTree Text Text -> Either ConfigParseError WarpConfig2
(KeyTree Text Text -> Either ConfigParseError WarpConfig2)
-> ConfigParser WarpConfig2
forall a.
(KeyTree Text Text -> Either ConfigParseError a) -> ConfigParser a
$cparseConfig :: KeyTree Text Text -> Either ConfigParseError WarpConfig2
parseConfig :: KeyTree Text Text -> Either ConfigParseError WarpConfig2
ConfigParser) via (ConfigOpts ToUpper WarpConfig2)

data RedisConfig2 = RedisConfig2
  { RedisConfig2 -> Text
redisConfigHost :: Text
  , RedisConfig2 -> Int
redisConfigPort :: Int
  , RedisConfig2 -> Maybe ByteString
redisConfigConnectAuth :: Maybe ByteString
  }
  deriving ((forall x. RedisConfig2 -> Rep RedisConfig2 x)
-> (forall x. Rep RedisConfig2 x -> RedisConfig2)
-> Generic RedisConfig2
forall x. Rep RedisConfig2 x -> RedisConfig2
forall x. RedisConfig2 -> Rep RedisConfig2 x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. RedisConfig2 -> Rep RedisConfig2 x
from :: forall x. RedisConfig2 -> Rep RedisConfig2 x
$cto :: forall x. Rep RedisConfig2 x -> RedisConfig2
to :: forall x. Rep RedisConfig2 x -> RedisConfig2
Generic, Int -> RedisConfig2 -> ShowS
[RedisConfig2] -> ShowS
RedisConfig2 -> String
(Int -> RedisConfig2 -> ShowS)
-> (RedisConfig2 -> String)
-> ([RedisConfig2] -> ShowS)
-> Show RedisConfig2
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> RedisConfig2 -> ShowS
showsPrec :: Int -> RedisConfig2 -> ShowS
$cshow :: RedisConfig2 -> String
show :: RedisConfig2 -> String
$cshowList :: [RedisConfig2] -> ShowS
showList :: [RedisConfig2] -> ShowS
Show, Text -> Maybe Text
(Text -> Maybe Text) -> DefaultSource RedisConfig2
forall {k} (a :: k). (Text -> Maybe Text) -> DefaultSource a
$cdefaults :: Text -> Maybe Text
defaults :: Text -> Maybe Text
DefaultSource)
  deriving (KeyTree Text Text
KeyTree Text Text -> ConfigSource RedisConfig2
forall {k} (a :: k). KeyTree Text Text -> ConfigSource a
$cconfigSource :: KeyTree Text Text
configSource :: KeyTree Text Text
ConfigSource, KeyTree Text Text -> Either ConfigParseError RedisConfig2
(KeyTree Text Text -> Either ConfigParseError RedisConfig2)
-> ConfigParser RedisConfig2
forall a.
(KeyTree Text Text -> Either ConfigParseError a) -> ConfigParser a
$cparseConfig :: KeyTree Text Text -> Either ConfigParseError RedisConfig2
parseConfig :: KeyTree Text Text -> Either ConfigParseError RedisConfig2
ConfigParser) via (ConfigOpts ToUpper RedisConfig2)

data AppConfig2 = AppConfig2
  { AppConfig2 -> WarpConfig2
appConfigWarpSettings :: WarpConfig2
  , AppConfig2 -> RedisConfig2
appConfigRedisSettings :: RedisConfig2
  , AppConfig2 -> Environment
appConfigEnvironment :: Environment
  }
  deriving ((forall x. AppConfig2 -> Rep AppConfig2 x)
-> (forall x. Rep AppConfig2 x -> AppConfig2) -> Generic AppConfig2
forall x. Rep AppConfig2 x -> AppConfig2
forall x. AppConfig2 -> Rep AppConfig2 x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. AppConfig2 -> Rep AppConfig2 x
from :: forall x. AppConfig2 -> Rep AppConfig2 x
$cto :: forall x. Rep AppConfig2 x -> AppConfig2
to :: forall x. Rep AppConfig2 x -> AppConfig2
Generic, Int -> AppConfig2 -> ShowS
[AppConfig2] -> ShowS
AppConfig2 -> String
(Int -> AppConfig2 -> ShowS)
-> (AppConfig2 -> String)
-> ([AppConfig2] -> ShowS)
-> Show AppConfig2
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> AppConfig2 -> ShowS
showsPrec :: Int -> AppConfig2 -> ShowS
$cshow :: AppConfig2 -> String
show :: AppConfig2 -> String
$cshowList :: [AppConfig2] -> ShowS
showList :: [AppConfig2] -> ShowS
Show, Text -> Maybe Text
(Text -> Maybe Text) -> DefaultSource AppConfig2
forall {k} (a :: k). (Text -> Maybe Text) -> DefaultSource a
$cdefaults :: Text -> Maybe Text
defaults :: Text -> Maybe Text
DefaultSource)
  deriving (KeyTree Text Text
KeyTree Text Text -> ConfigSource AppConfig2
forall {k} (a :: k). KeyTree Text Text -> ConfigSource a
$cconfigSource :: KeyTree Text Text
configSource :: KeyTree Text Text
ConfigSource, KeyTree Text Text -> Either ConfigParseError AppConfig2
(KeyTree Text Text -> Either ConfigParseError AppConfig2)
-> ConfigParser AppConfig2
forall a.
(KeyTree Text Text -> Either ConfigParseError a) -> ConfigParser a
$cparseConfig :: KeyTree Text Text -> Either ConfigParseError AppConfig2
parseConfig :: KeyTree Text Text -> Either ConfigParseError AppConfig2
ConfigParser) via (ConfigOpts ToUpper AppConfig2)

-- Example 3
data WarpConfig3 = WarpConfig3
  { WarpConfig3 -> Int
warpConfigPort :: Int
  , WarpConfig3 -> Int
warpConfigTimeout :: Int
  , WarpConfig3 -> Bool
warpConfigHTTP2Enabled :: Bool
  , WarpConfig3 -> ByteString
warpConfigServerName :: ByteString
  }
  deriving ((forall x. WarpConfig3 -> Rep WarpConfig3 x)
-> (forall x. Rep WarpConfig3 x -> WarpConfig3)
-> Generic WarpConfig3
forall x. Rep WarpConfig3 x -> WarpConfig3
forall x. WarpConfig3 -> Rep WarpConfig3 x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. WarpConfig3 -> Rep WarpConfig3 x
from :: forall x. WarpConfig3 -> Rep WarpConfig3 x
$cto :: forall x. Rep WarpConfig3 x -> WarpConfig3
to :: forall x. Rep WarpConfig3 x -> WarpConfig3
Generic, Int -> WarpConfig3 -> ShowS
[WarpConfig3] -> ShowS
WarpConfig3 -> String
(Int -> WarpConfig3 -> ShowS)
-> (WarpConfig3 -> String)
-> ([WarpConfig3] -> ShowS)
-> Show WarpConfig3
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> WarpConfig3 -> ShowS
showsPrec :: Int -> WarpConfig3 -> ShowS
$cshow :: WarpConfig3 -> String
show :: WarpConfig3 -> String
$cshowList :: [WarpConfig3] -> ShowS
showList :: [WarpConfig3] -> ShowS
Show, Text -> Maybe Text
(Text -> Maybe Text) -> DefaultSource WarpConfig3
forall {k} (a :: k). (Text -> Maybe Text) -> DefaultSource a
$cdefaults :: Text -> Maybe Text
defaults :: Text -> Maybe Text
DefaultSource)
  deriving
    (KeyTree Text Text
KeyTree Text Text -> ConfigSource WarpConfig3
forall {k} (a :: k). KeyTree Text Text -> ConfigSource a
$cconfigSource :: KeyTree Text Text
configSource :: KeyTree Text Text
ConfigSource, KeyTree Text Text -> Either ConfigParseError WarpConfig3
(KeyTree Text Text -> Either ConfigParseError WarpConfig3)
-> ConfigParser WarpConfig3
forall a.
(KeyTree Text Text -> Either ConfigParseError a) -> ConfigParser a
$cparseConfig :: KeyTree Text Text -> Either ConfigParseError WarpConfig3
parseConfig :: KeyTree Text Text -> Either ConfigParseError WarpConfig3
ConfigParser)
    via (ConfigOpts (StripPrefix "warpConfig", ToUpper) WarpConfig3)

data RedisConfig3 = RedisConfig3
  { RedisConfig3 -> Text
redisConfigHost :: Text
  , RedisConfig3 -> Int
redisConfigPort :: Int
  , RedisConfig3 -> Maybe ByteString
redisConfigConnectAuth :: Maybe ByteString
  }
  deriving ((forall x. RedisConfig3 -> Rep RedisConfig3 x)
-> (forall x. Rep RedisConfig3 x -> RedisConfig3)
-> Generic RedisConfig3
forall x. Rep RedisConfig3 x -> RedisConfig3
forall x. RedisConfig3 -> Rep RedisConfig3 x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. RedisConfig3 -> Rep RedisConfig3 x
from :: forall x. RedisConfig3 -> Rep RedisConfig3 x
$cto :: forall x. Rep RedisConfig3 x -> RedisConfig3
to :: forall x. Rep RedisConfig3 x -> RedisConfig3
Generic, Int -> RedisConfig3 -> ShowS
[RedisConfig3] -> ShowS
RedisConfig3 -> String
(Int -> RedisConfig3 -> ShowS)
-> (RedisConfig3 -> String)
-> ([RedisConfig3] -> ShowS)
-> Show RedisConfig3
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> RedisConfig3 -> ShowS
showsPrec :: Int -> RedisConfig3 -> ShowS
$cshow :: RedisConfig3 -> String
show :: RedisConfig3 -> String
$cshowList :: [RedisConfig3] -> ShowS
showList :: [RedisConfig3] -> ShowS
Show, Text -> Maybe Text
(Text -> Maybe Text) -> DefaultSource RedisConfig3
forall {k} (a :: k). (Text -> Maybe Text) -> DefaultSource a
$cdefaults :: Text -> Maybe Text
defaults :: Text -> Maybe Text
DefaultSource)
  deriving
    (KeyTree Text Text
KeyTree Text Text -> ConfigSource RedisConfig3
forall {k} (a :: k). KeyTree Text Text -> ConfigSource a
$cconfigSource :: KeyTree Text Text
configSource :: KeyTree Text Text
ConfigSource, KeyTree Text Text -> Either ConfigParseError RedisConfig3
(KeyTree Text Text -> Either ConfigParseError RedisConfig3)
-> ConfigParser RedisConfig3
forall a.
(KeyTree Text Text -> Either ConfigParseError a) -> ConfigParser a
$cparseConfig :: KeyTree Text Text -> Either ConfigParseError RedisConfig3
parseConfig :: KeyTree Text Text -> Either ConfigParseError RedisConfig3
ConfigParser)
    via (ConfigOpts [StripPrefix "redisConfig", ToUpper] RedisConfig3)

data AppConfig3 = AppConfig3
  { AppConfig3 -> WarpConfig3
appConfigWarpSettings :: WarpConfig3
  , AppConfig3 -> RedisConfig3
appConfigRedisSettings :: RedisConfig3
  , AppConfig3 -> Environment
appConfigEnvironment :: Environment
  }
  deriving ((forall x. AppConfig3 -> Rep AppConfig3 x)
-> (forall x. Rep AppConfig3 x -> AppConfig3) -> Generic AppConfig3
forall x. Rep AppConfig3 x -> AppConfig3
forall x. AppConfig3 -> Rep AppConfig3 x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. AppConfig3 -> Rep AppConfig3 x
from :: forall x. AppConfig3 -> Rep AppConfig3 x
$cto :: forall x. Rep AppConfig3 x -> AppConfig3
to :: forall x. Rep AppConfig3 x -> AppConfig3
Generic, Int -> AppConfig3 -> ShowS
[AppConfig3] -> ShowS
AppConfig3 -> String
(Int -> AppConfig3 -> ShowS)
-> (AppConfig3 -> String)
-> ([AppConfig3] -> ShowS)
-> Show AppConfig3
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> AppConfig3 -> ShowS
showsPrec :: Int -> AppConfig3 -> ShowS
$cshow :: AppConfig3 -> String
show :: AppConfig3 -> String
$cshowList :: [AppConfig3] -> ShowS
showList :: [AppConfig3] -> ShowS
Show, Text -> Maybe Text
(Text -> Maybe Text) -> DefaultSource AppConfig3
forall {k} (a :: k). (Text -> Maybe Text) -> DefaultSource a
$cdefaults :: Text -> Maybe Text
defaults :: Text -> Maybe Text
DefaultSource)
  deriving
    (KeyTree Text Text
KeyTree Text Text -> ConfigSource AppConfig3
forall {k} (a :: k). KeyTree Text Text -> ConfigSource a
$cconfigSource :: KeyTree Text Text
configSource :: KeyTree Text Text
ConfigSource, KeyTree Text Text -> Either ConfigParseError AppConfig3
(KeyTree Text Text -> Either ConfigParseError AppConfig3)
-> ConfigParser AppConfig3
forall a.
(KeyTree Text Text -> Either ConfigParseError a) -> ConfigParser a
$cparseConfig :: KeyTree Text Text -> Either ConfigParseError AppConfig3
parseConfig :: KeyTree Text Text -> Either ConfigParseError AppConfig3
ConfigParser)
    via (ConfigOpts [StripPrefix "appConfig", StripSuffix "Settings", ToUpper] AppConfig3)

-- Example 4
data AppConfig4 = AppConfig4
  { AppConfig4 -> WarpConfig3
appConfigWarpSettings :: WarpConfig3
  , AppConfig4 -> RedisConfig3
appConfigRedisSettings :: RedisConfig3
  , AppConfig4 -> Environment
appConfigEnvironment :: Environment
  }
  deriving ((forall x. AppConfig4 -> Rep AppConfig4 x)
-> (forall x. Rep AppConfig4 x -> AppConfig4) -> Generic AppConfig4
forall x. Rep AppConfig4 x -> AppConfig4
forall x. AppConfig4 -> Rep AppConfig4 x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. AppConfig4 -> Rep AppConfig4 x
from :: forall x. AppConfig4 -> Rep AppConfig4 x
$cto :: forall x. Rep AppConfig4 x -> AppConfig4
to :: forall x. Rep AppConfig4 x -> AppConfig4
Generic, Int -> AppConfig4 -> ShowS
[AppConfig4] -> ShowS
AppConfig4 -> String
(Int -> AppConfig4 -> ShowS)
-> (AppConfig4 -> String)
-> ([AppConfig4] -> ShowS)
-> Show AppConfig4
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> AppConfig4 -> ShowS
showsPrec :: Int -> AppConfig4 -> ShowS
$cshow :: AppConfig4 -> String
show :: AppConfig4 -> String
$cshowList :: [AppConfig4] -> ShowS
showList :: [AppConfig4] -> ShowS
Show, Text -> Maybe Text
(Text -> Maybe Text) -> DefaultSource AppConfig4
forall {k} (a :: k). (Text -> Maybe Text) -> DefaultSource a
$cdefaults :: Text -> Maybe Text
defaults :: Text -> Maybe Text
DefaultSource)
  deriving
    (KeyTree Text Text
KeyTree Text Text -> ConfigSource AppConfig4
forall {k} (a :: k). KeyTree Text Text -> ConfigSource a
$cconfigSource :: KeyTree Text Text
configSource :: KeyTree Text Text
ConfigSource, KeyTree Text Text -> Either ConfigParseError AppConfig4
(KeyTree Text Text -> Either ConfigParseError AppConfig4)
-> ConfigParser AppConfig4
forall a.
(KeyTree Text Text -> Either ConfigParseError a) -> ConfigParser a
$cparseConfig :: KeyTree Text Text -> Either ConfigParseError AppConfig4
parseConfig :: KeyTree Text Text -> Either ConfigParseError AppConfig4
ConfigParser)
    via ( ConfigRoot
            ('TypeName [StripSuffix "Config4", ToUpper])
            [StripPrefix "appConfig", StripSuffix "Settings", ToUpper]
            AppConfig4
        )

-- Example 5
data AppConfig5 = AppConfig5
  { AppConfig5 -> WarpConfig
appConfigWarpSettings :: WarpConfig
  , AppConfig5 -> RedisConfig
appConfigRedisSettings :: RedisConfig
  , AppConfig5 -> Environment
appConfigEnvironment :: Environment
  }
  deriving ((forall x. AppConfig5 -> Rep AppConfig5 x)
-> (forall x. Rep AppConfig5 x -> AppConfig5) -> Generic AppConfig5
forall x. Rep AppConfig5 x -> AppConfig5
forall x. AppConfig5 -> Rep AppConfig5 x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. AppConfig5 -> Rep AppConfig5 x
from :: forall x. AppConfig5 -> Rep AppConfig5 x
$cto :: forall x. Rep AppConfig5 x -> AppConfig5
to :: forall x. Rep AppConfig5 x -> AppConfig5
Generic, Int -> AppConfig5 -> ShowS
[AppConfig5] -> ShowS
AppConfig5 -> String
(Int -> AppConfig5 -> ShowS)
-> (AppConfig5 -> String)
-> ([AppConfig5] -> ShowS)
-> Show AppConfig5
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> AppConfig5 -> ShowS
showsPrec :: Int -> AppConfig5 -> ShowS
$cshow :: AppConfig5 -> String
show :: AppConfig5 -> String
$cshowList :: [AppConfig5] -> ShowS
showList :: [AppConfig5] -> ShowS
Show)
  deriving
    (KeyTree Text Text
KeyTree Text Text -> ConfigSource AppConfig5
forall {k} (a :: k). KeyTree Text Text -> ConfigSource a
$cconfigSource :: KeyTree Text Text
configSource :: KeyTree Text Text
ConfigSource, KeyTree Text Text -> Either ConfigParseError AppConfig5
(KeyTree Text Text -> Either ConfigParseError AppConfig5)
-> ConfigParser AppConfig5
forall a.
(KeyTree Text Text -> Either ConfigParseError a) -> ConfigParser a
$cparseConfig :: KeyTree Text Text -> Either ConfigParseError AppConfig5
parseConfig :: KeyTree Text Text -> Either ConfigParseError AppConfig5
ConfigParser)
    via (ConfigOpts [StripPrefix "appConfig", StripSuffix "Settings", ToUpper] AppConfig5)

instance DefaultSource AppConfig5 where
  defaults :: Text -> Maybe Text
defaults Text
"appConfigEnvironment" = Text -> Maybe Text
forall a. a -> Maybe a
Just Text
"Development"
  defaults Text
_ = Maybe Text
forall a. Maybe a
Nothing