packages feed

cfg-0.0.1.0: src/Cfg/Source/NestedConfig.hs

module Cfg.Source.NestedConfig where

import Cfg.Options (ConfigOptions (..))
import Cfg.Source (NestedConfig (..))
import Data.Kind (Type)
import Data.Text (Text)
import Data.Text qualified as T
import Data.Tree (Tree (..))
import GHC.Generics

defaultToNestedConfig :: forall a. (Generic a, GConfigForest (Rep a)) => ConfigOptions -> [Tree Text]
defaultToNestedConfig opts = gToForest @(Rep a) opts

{- | Generic typeclass machinery for inducting on the structure
 of the type, such that we can thread `Display` instances through
 the structure of the type. The primary use case is for implementing
 `RecordInstance`, which does this "threading" for record fields. This
 machinery does, crucially, depend on child types (i.e. the type of a
 record field) having a `Display` instance.

 @since 0.0.1.0
-}
class GConfigForest (a :: Type -> Type) where
    gToForest :: ConfigOptions -> [Tree Text]

instance GConfigForest V1 where
    gToForest _ = []

instance GConfigForest U1 where
    gToForest _ = []

instance NestedConfig a => GConfigForest (K1 R a) where
    gToForest _ = toNestedConfig @a

instance GConfigForest f => GConfigForest (M1 D s f) where
    gToForest opts = gToForest @f opts 

instance (Constructor c, GConfigForest f) => GConfigForest (M1 C c f) where
    gToForest opts = gToForest @f opts

instance (Selector s, GConfigForest f) => GConfigForest (M1 S s f) where
    gToForest opts =
        if selName m == ""
            then error "Can only create a tree for named product types i.e. Records with named fields"
            else
                [ Node
                    (configOptionsLabelModifier opts $ T.pack (selName m))
                    (gToForest @f opts)
                ]
      where
        m :: t s f a
        m = undefined

instance (GConfigForest a, GConfigForest b) => GConfigForest (a :*: b) where
    gToForest opts = gToForest @a opts <> gToForest @b opts

instance GConfigForest (a :+: b) where
    gToForest _ = []