packages feed

conferer-1.1.0.0: src/Conferer/Source/Env.hs

-- |
-- Copyright: (c) 2019 Lucas David Traverso
-- License: MPL-2.0
-- Maintainer: Lucas David Traverso <lucas6246@gmail.com>
-- Stability: stable
-- Portability: portable
--
-- Environment based source
{-# LANGUAGE RecordWildCards #-}
module Conferer.Source.Env where

import Data.Text (Text)
import qualified Data.Text as Text
import qualified System.Environment as System
import Data.Maybe (mapMaybe)

import Conferer.Source
import qualified Conferer.Source.InMemory as InMemory

-- | Source that interfaces with the environment transforming keys
-- by uppercasing and interspersing underscores, and using a prefix
-- to avoid clashing with system env vars
--
-- so with "app" prefix, @"some.key"@ turns into @APP_SOME_KEY@
data EnvSource =
  EnvSource
  { environment :: RawEnvironment
  , keyPrefix :: Prefix
  , innerSource :: Source
  } deriving (Show)

-- | Type alias for the environment
type RawEnvironment = [(String, String)]

-- | Type alias for the env vars prefix
type Prefix = Text

instance IsSource EnvSource where
  getKeyInSource EnvSource{..} key = do
    getKeyInSource innerSource key
  getSubkeysInSource EnvSource{..} key = do
    getSubkeysInSource innerSource key

-- | Create a 'SourceCreator' using 'fromEnv'
fromConfig :: Prefix -> SourceCreator
fromConfig prefix _config = do
  fromEnv prefix

-- | Create a 'Source' using the real environment
fromEnv :: Prefix -> IO Source
fromEnv prefix = do
  rawEnvironment <- System.getEnvironment
  return $ fromEnvList rawEnvironment prefix

-- | Create a 'Source' using a hardcoded list of env vars
fromEnvList :: RawEnvironment -> Prefix -> Source
fromEnvList environment keyPrefix =
  let
    mappings =
      mapMaybe (\(key, v) -> do
        k <- envVarToKey keyPrefix $ Text.pack key
        return (k, Text.pack v)
      )
      environment
    innerSource = InMemory.fromAssociations mappings
  in Source EnvSource {..}

-- | Get the env name from a prefix and a key by uppercasing and
-- intercalating underscores
keyToEnvVar :: Prefix -> Key -> Text
keyToEnvVar prefix keys =
  Text.toUpper
  $ Text.intercalate "_"
  $ filter (/= mempty)
  $ prefix : rawKeyComponents keys

-- | The opossite of 'keyToEnvVar'
envVarToKey :: Prefix -> Text -> Maybe Key
envVarToKey prefix envVar =
  let
    splitEnvVar = fromText $ Text.replace "_"  "." envVar
  in
    stripKeyPrefix (fromText prefix) splitEnvVar