-- |
--  Module      : Cfg.Env.Keys
--  Copyright   : © Jonathan Lorimer, 2023
--  License     : MIT
--  Maintainer  : jonathanlorimer@pm.me
--  Stability   : stable
--
-- @since 0.0.2.0
--
-- This module provides helper functions for manipulating the keys on our
-- internal tree representation of a configuration; 'Cfg.KeyTree.KeyTree'.
--
-- These helper functions are generally oriented towards using environment
-- variables as your configuration source.
module Cfg.Env.Keys where

import Cfg.Source (ConfigSource (..))
import Data.Foldable
import Data.List (intersperse)
import Data.Map.Strict (empty)
import Data.Text (Text)
import KeyTree

-- | This function takes a separator and a list of keys and joins them from the
-- end of the list to the beginning, interspersed with the provided separator.
--
-- >>> getEnvKey "_" ["A", "B", "C"]
-- "A_B_C"
--
-- @since 0.0.1.0
getEnvKey
  :: Text
  -- ^ Separator
  -> [Text]
  -- ^ List of keys
  -> Text
getEnvKey :: Text -> [Text] -> Text
getEnvKey Text
sep = [Text] -> Text
forall m. Monoid m => [m] -> m
forall (t :: * -> *) m. (Foldable t, Monoid m) => t m -> m
fold ([Text] -> Text) -> ([Text] -> [Text]) -> [Text] -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> [Text] -> [Text]
forall a. a -> [a] -> [a]
intersperse Text
sep

-- | Folds a 'Cfg.KeyTree.KeyTree' from leaf to root, into distinct key paths.
-- This is necessary for the way that hierarchical structures are represented
-- in environment variables (i.e. \"KEYA_SUBKEYA\", \"KEYA_SUBKEYB\").
--
-- Here is a visual representation of how the keys would get folded
--
-- @
--       A
--      / \\
--     B   C
--
--  [ [ "A", "B" ]
--  , [ "A", "C" ]
--  ]
-- @
-- >>> import KeyTree
-- >>> import Data.Map qualified as M
-- >>> getKeys $ Free $ M.singleton "A" $ Free (M.fromList [("B", Free M.empty), ("C", Free M.empty)])
-- [["A","B"],["A","C"]]
--
-- @since 0.0.1.0
getKeys :: KeyTree Text Text -> [[Text]]
getKeys :: KeyTree Text Text -> [[Text]]
getKeys = (Text -> [[Text]])
-> (Text -> KeyTree Text Text -> [[Text]] -> [[Text]])
-> [[Text]]
-> KeyTree Text Text
-> [[Text]]
forall k v a.
(Eq k, Eq v) =>
(v -> a)
-> (k -> Free (Map k) v -> a -> a) -> a -> Free (Map k) v -> a
foldKeyTree Text -> [[Text]]
valF Text -> KeyTree Text Text -> [[Text]] -> [[Text]]
stepF []
 where
  stepF :: Text -> Free (Map Text) Text -> [[Text]] -> [[Text]]
  stepF :: Text -> KeyTree Text Text -> [[Text]] -> [[Text]]
stepF Text
key (Pure Text
_) [[Text]]
acc = [Text
key] [Text] -> [[Text]] -> [[Text]]
forall a. a -> [a] -> [a]
: [[Text]]
acc
  stepF Text
key t :: KeyTree Text Text
t@(Free Map Text (KeyTree Text Text)
m) [[Text]]
acc =
    if Map Text (KeyTree Text Text)
m Map Text (KeyTree Text Text)
-> Map Text (KeyTree Text Text) -> Bool
forall a. Eq a => a -> a -> Bool
== Map Text (KeyTree Text Text)
forall k a. Map k a
empty
      then [Text
key] [Text] -> [[Text]] -> [[Text]]
forall a. a -> [a] -> [a]
: [[Text]]
acc
      else ([Text] -> [Text]) -> [[Text]] -> [[Text]]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Text
key Text -> [Text] -> [Text]
forall a. a -> [a] -> [a]
:) (KeyTree Text Text -> [[Text]]
getKeys KeyTree Text Text
t) [[Text]] -> [[Text]] -> [[Text]]
forall a. Semigroup a => a -> a -> a
<> [[Text]]
acc

  valF :: Text -> [[Text]]
  valF :: Text -> [[Text]]
valF Text
_ = []

-- | Gets all the keys from a configuration tree, and flattens the hierarchy so that
-- each key is prefixed with its path through the tree.
--
-- Accepts separator to individuate the key prefixes.
--
-- >>> import KeyTree
-- >>> import Data.Map qualified as M
-- >>> showEnvKeys' "-" $ Free $ M.singleton "A" $ Free (M.fromList [("B", Free M.empty), ("C", Free M.empty)])
-- ["A-B","A-C"]
--
-- @since 0.0.1.0
showEnvKeys'
  :: Text
  -- ^ Separator
  -> KeyTree Text Text
  -- ^ Configuration tree
  -> [Text]
showEnvKeys' :: Text -> KeyTree Text Text -> [Text]
showEnvKeys' Text
sep KeyTree Text Text
tree = Text -> [Text] -> Text
getEnvKey Text
sep ([Text] -> Text) -> [[Text]] -> [Text]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> KeyTree Text Text -> [[Text]]
getKeys KeyTree Text Text
tree

-- | Same as 'showEnvKeys'' but the 'KeyTree.KeyTree' is generated via a 'configSource'
--
-- >>> import GHC.Generics (Generic (..))
-- >>> import Cfg.Source (ConfigSource(..))
-- >>> import Cfg.Parser (ConfigParser(..))
-- >>> import Cfg.Deriving.Config (Config(..))
-- >>> import Cfg.Source.Default (DefaultSource(..))
-- >>> :{
-- data Sub = Sub { c :: Int, d :: Bool }
--   deriving (Generic, Show, DefaultSource)
--   deriving (ConfigSource, ConfigParser) via Config Sub
-- data TypeCon = DataCon { a :: Sub }
--   deriving (Generic, Show, DefaultSource)
--   deriving (ConfigSource, ConfigParser) via Config TypeCon
-- :}
--
-- >>> showEnvKeys @TypeCon "_"
-- ["a_c","a_d"]
--
-- @since 0.0.1.0
showEnvKeys :: forall a. (ConfigSource a) => Text -> [Text]
showEnvKeys :: forall {k} (a :: k). ConfigSource a => Text -> [Text]
showEnvKeys Text
sep = Text -> [Text] -> Text
getEnvKey Text
sep ([Text] -> Text) -> [[Text]] -> [Text]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (KeyTree Text Text -> [[Text]]
getKeys (KeyTree Text Text -> [[Text]]) -> KeyTree Text Text -> [[Text]]
forall a b. (a -> b) -> a -> b
$ forall (a :: k). ConfigSource a => KeyTree Text Text
forall {k} (a :: k). ConfigSource a => KeyTree Text Text
configSource @a)