cfg-0.0.2.2: Type directed application configuration parsing and accessors
Copyright© Jonathan Lorimer 2023
LicenseMIT
Maintainerjonathanlorimer@pm.me
Stabilitystable
Safe HaskellNone
LanguageHaskell2010

Cfg.Source.Config

Description

This module contains the generic machinery for generating a tree representation of your configuration, this tree representation is intended to be used with various sources. The tree structure should match the structure of your (potentially) nested record type.

It is important to note that defaults are injected here, and not in the parser stage. We use a DefaultSource instance to inject Pure values at the leaves that can be used if the source fetcher doesn't return a value for that key. In the case that there is no default a 'Free Data.Map.empty' is placed to represent a required value.

Since: 0.0.2.0

Synopsis

Default Source Generator

defaultConfigSource :: (DefaultSource a, Generic a, GConfigSource (Rep a)) => ConfigOptions -> KeyTree Text Text Source #

This function is used by the deriving via machinery to dispatch to the generic machinery, the user should never have to invoke it directly. This takes in a ConfigOptions which are retrieved from the deriving via newtypes, and also threads a DefaultSource instance through so that we can dispatch to defaults in the K1 case.

Since: 0.0.2.0

Generic Machinery

class GConfigSource (a :: Type -> Type) where Source #

This class is the generic version of 'ConfigSource. It recurses on the generic structure of a type, building up KeyTree representation.

Since: 0.0.2.0

Instances

Instances details
(GConfigSource a, GConfigSource b) => GConfigSource (a :*: b) Source #

This instance handles product types and is pretty important. We need to check that recursive calls to gConfigSource generate sub-trees, and then we merge the sub-trees.

You may wonder what happens if there is a Pure value in one of the record fields, well that would be represented like so:

Free $ M.singleton fieldName (Pure value)

since we need to account for the key corresponding to the record field. So we really should never hit a case were a recursive call to gConfigSource yields a raw Pure.

Since: 0.0.2.0

Instance details

Defined in Cfg.Source.Config

GConfigSource (a :+: b) Source #

Sum types should represent base values, so Free M.empty is the right thing to do here, although we should probably never hit this case, since sum types should be nested under record fields as base values.

Since: 0.0.2.0

Instance details

Defined in Cfg.Source.Config

ConfigSource a => GConfigSource (K1 R a :: Type -> Type) Source #

This is the "base case", since GHC.Generics don't recurse the generic represetation multiple levels, a is just a plain type. Therefore we call configSource on it. a may be another nested record, in which case gParseConfig will probably get called again, but for the generic representation of a sub-tree. It will do this until it finds a ConfigSource instance for Value which will just add a 'Free Data.Map.empty' (indicating a hole to be filled when we fetch the configuration).

Since: 0.0.2.0

Instance details

Defined in Cfg.Source.Config

(Constructor c, GConfigSource f) => GConfigSource (M1 C c f) Source #

This is the data constructor case, if we are dealing with a ConfigRoot instance, then we have to create an extra layer with the "root key" as a key and then a subtree (calculated by recursively calling gConfigSource) as the value.

Since: 0.0.2.0

Instance details

Defined in Cfg.Source.Config

(Datatype d, GConfigSource f) => GConfigSource (M1 D d f) Source #

This is the type constructor case, if we are dealing with a ConfigRoot instance, then we have to create an extra layer with the "root key" as a key and then a subtree (calculated by recursively calling gConfigSource) as the value.

Since: 0.0.2.0

Instance details

Defined in Cfg.Source.Config

(Selector s, GConfigSource f) => GConfigSource (M1 S s f) Source #

This instance is important because it does the work of pulling off the field selector name, and creating a sub-tree under that key by calling gConfigSource recursively. If there is a default for that selector then no sub-tree is created, instead we insert a "placeholder" value tagged by Pure to represent that it is the end of the tree.

We detect if a default exists by calling defaults from the DefaultSource instance on the selector, defaults is of type Text -> Maybe Text, so the Nothing case indicates no default.

Since: 0.0.2.0

Instance details

Defined in Cfg.Source.Config