-- | An 'Environment' contains textual key value pairs, relavant for string template
-- substitution.
--
-- The variables are passed to the B9 build either via command line, OS environment
-- variables or configuration file.
--
-- @since 0.5.62
module B9.Environment
  ( Environment (),
    fromStringPairs,
    addBinding,
    addStringBinding,
    addLocalStringBinding,
    addPositionalArguments,
    addLocalPositionalArguments,
    EnvironmentReader,
    hasKey,
    runEnvironmentReader,
    askEnvironment,
    localEnvironment,
    lookupOrThrow,
    lookupEither,
    KeyNotFound (..),
    DuplicateKey (..),
  )
where

import B9.B9Error
import B9.Text
import Control.Arrow ((***))
import Control.Eff as Eff
import Control.Eff.Reader.Lazy as Eff
import Control.Exception (Exception)
import Control.Parallel.Strategies
import Data.Data
import Data.Foldable
import Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as HashMap
import Data.Maybe
  ( isJust,
    maybe,
  )
import GHC.Generics (Generic)

-- | A map of textual keys to textual values.
--
-- @since 0.5.62
data Environment
  = MkEnvironment
      { Environment -> Int
nextPosition :: Int,
        Environment -> HashMap Text Text
fromEnvironment :: HashMap Text Text
      }
  deriving (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
showList :: [Environment] -> ShowS
$cshowList :: [Environment] -> ShowS
show :: Environment -> String
$cshow :: Environment -> String
showsPrec :: Int -> Environment -> ShowS
$cshowsPrec :: Int -> Environment -> ShowS
Show, Typeable, Typeable Environment
DataType
Constr
Typeable Environment
-> (forall (c :: * -> *).
    (forall d b. Data d => c (d -> b) -> d -> c b)
    -> (forall g. g -> c g) -> Environment -> c Environment)
-> (forall (c :: * -> *).
    (forall b r. Data b => c (b -> r) -> c r)
    -> (forall r. r -> c r) -> Constr -> c Environment)
-> (Environment -> Constr)
-> (Environment -> DataType)
-> (forall (t :: * -> *) (c :: * -> *).
    Typeable t =>
    (forall d. Data d => c (t d)) -> Maybe (c Environment))
-> (forall (t :: * -> * -> *) (c :: * -> *).
    Typeable t =>
    (forall d e. (Data d, Data e) => c (t d e))
    -> Maybe (c Environment))
-> ((forall b. Data b => b -> b) -> Environment -> Environment)
-> (forall r r'.
    (r -> r' -> r)
    -> r -> (forall d. Data d => d -> r') -> Environment -> r)
-> (forall r r'.
    (r' -> r -> r)
    -> r -> (forall d. Data d => d -> r') -> Environment -> r)
-> (forall u. (forall d. Data d => d -> u) -> Environment -> [u])
-> (forall u.
    Int -> (forall d. Data d => d -> u) -> Environment -> u)
-> (forall (m :: * -> *).
    Monad m =>
    (forall d. Data d => d -> m d) -> Environment -> m Environment)
-> (forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> Environment -> m Environment)
-> (forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> Environment -> m Environment)
-> Data Environment
Environment -> DataType
Environment -> Constr
(forall b. Data b => b -> b) -> Environment -> Environment
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> Environment -> c Environment
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c Environment
forall a.
Typeable a
-> (forall (c :: * -> *).
    (forall d b. Data d => c (d -> b) -> d -> c b)
    -> (forall g. g -> c g) -> a -> c a)
-> (forall (c :: * -> *).
    (forall b r. Data b => c (b -> r) -> c r)
    -> (forall r. r -> c r) -> Constr -> c a)
-> (a -> Constr)
-> (a -> DataType)
-> (forall (t :: * -> *) (c :: * -> *).
    Typeable t =>
    (forall d. Data d => c (t d)) -> Maybe (c a))
-> (forall (t :: * -> * -> *) (c :: * -> *).
    Typeable t =>
    (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c a))
-> ((forall b. Data b => b -> b) -> a -> a)
-> (forall r r'.
    (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> a -> r)
-> (forall r r'.
    (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> a -> r)
-> (forall u. (forall d. Data d => d -> u) -> a -> [u])
-> (forall u. Int -> (forall d. Data d => d -> u) -> a -> u)
-> (forall (m :: * -> *).
    Monad m =>
    (forall d. Data d => d -> m d) -> a -> m a)
-> (forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> a -> m a)
-> (forall (m :: * -> *).
    MonadPlus m =>
    (forall d. Data d => d -> m d) -> a -> m a)
-> Data a
forall u. Int -> (forall d. Data d => d -> u) -> Environment -> u
forall u. (forall d. Data d => d -> u) -> Environment -> [u]
forall r r'.
(r -> r' -> r)
-> r -> (forall d. Data d => d -> r') -> Environment -> r
forall r r'.
(r' -> r -> r)
-> r -> (forall d. Data d => d -> r') -> Environment -> r
forall (m :: * -> *).
Monad m =>
(forall d. Data d => d -> m d) -> Environment -> m Environment
forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> Environment -> m Environment
forall (c :: * -> *).
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c Environment
forall (c :: * -> *).
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> Environment -> c Environment
forall (t :: * -> *) (c :: * -> *).
Typeable t =>
(forall d. Data d => c (t d)) -> Maybe (c Environment)
forall (t :: * -> * -> *) (c :: * -> *).
Typeable t =>
(forall d e. (Data d, Data e) => c (t d e))
-> Maybe (c Environment)
$cMkEnvironment :: Constr
$tEnvironment :: DataType
gmapMo :: (forall d. Data d => d -> m d) -> Environment -> m Environment
$cgmapMo :: forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> Environment -> m Environment
gmapMp :: (forall d. Data d => d -> m d) -> Environment -> m Environment
$cgmapMp :: forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> Environment -> m Environment
gmapM :: (forall d. Data d => d -> m d) -> Environment -> m Environment
$cgmapM :: forall (m :: * -> *).
Monad m =>
(forall d. Data d => d -> m d) -> Environment -> m Environment
gmapQi :: Int -> (forall d. Data d => d -> u) -> Environment -> u
$cgmapQi :: forall u. Int -> (forall d. Data d => d -> u) -> Environment -> u
gmapQ :: (forall d. Data d => d -> u) -> Environment -> [u]
$cgmapQ :: forall u. (forall d. Data d => d -> u) -> Environment -> [u]
gmapQr :: (r' -> r -> r)
-> r -> (forall d. Data d => d -> r') -> Environment -> r
$cgmapQr :: forall r r'.
(r' -> r -> r)
-> r -> (forall d. Data d => d -> r') -> Environment -> r
gmapQl :: (r -> r' -> r)
-> r -> (forall d. Data d => d -> r') -> Environment -> r
$cgmapQl :: forall r r'.
(r -> r' -> r)
-> r -> (forall d. Data d => d -> r') -> Environment -> r
gmapT :: (forall b. Data b => b -> b) -> Environment -> Environment
$cgmapT :: (forall b. Data b => b -> b) -> Environment -> Environment
dataCast2 :: (forall d e. (Data d, Data e) => c (t d e))
-> Maybe (c Environment)
$cdataCast2 :: forall (t :: * -> * -> *) (c :: * -> *).
Typeable t =>
(forall d e. (Data d, Data e) => c (t d e))
-> Maybe (c Environment)
dataCast1 :: (forall d. Data d => c (t d)) -> Maybe (c Environment)
$cdataCast1 :: forall (t :: * -> *) (c :: * -> *).
Typeable t =>
(forall d. Data d => c (t d)) -> Maybe (c Environment)
dataTypeOf :: Environment -> DataType
$cdataTypeOf :: Environment -> DataType
toConstr :: Environment -> Constr
$ctoConstr :: Environment -> Constr
gunfold :: (forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c Environment
$cgunfold :: forall (c :: * -> *).
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c Environment
gfoldl :: (forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> Environment -> c Environment
$cgfoldl :: forall (c :: * -> *).
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> Environment -> c Environment
$cp1Data :: Typeable Environment
Data, Environment -> Environment -> Bool
(Environment -> Environment -> Bool)
-> (Environment -> Environment -> Bool) -> Eq Environment
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Environment -> Environment -> Bool
$c/= :: Environment -> Environment -> Bool
== :: Environment -> Environment -> Bool
$c== :: Environment -> Environment -> Bool
Eq, (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
$cto :: forall x. Rep Environment x -> Environment
$cfrom :: forall x. Environment -> Rep Environment x
Generic)

instance NFData Environment

instance Semigroup Environment where
  Environment
e1 <> :: Environment -> Environment -> Environment
<> Environment
e2 =
    MkEnvironment :: Int -> HashMap Text Text -> Environment
MkEnvironment
      { nextPosition :: Int
nextPosition = case (Environment -> Int
nextPosition Environment
e1, Environment -> Int
nextPosition Environment
e2) of
          (Int
1, Int
1) -> Int
1
          (Int
1, Int
p2) -> Int
p2
          (Int
p1, Int
1) -> Int
p1
          (Int, Int)
_ ->
            String -> Int
forall a. HasCallStack => String -> a
error
              ( String
"Overlapping positional arguments (<>): ("
                  String -> ShowS
forall a. [a] -> [a] -> [a]
++ Environment -> String
forall a. Show a => a -> String
show Environment
e1
                  String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
") <> ("
                  String -> ShowS
forall a. [a] -> [a] -> [a]
++ Environment -> String
forall a. Show a => a -> String
show Environment
e2
                  String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
")"
              ),
        fromEnvironment :: HashMap Text Text
fromEnvironment =
          let i :: HashMap Text Text
i = HashMap Text Text -> HashMap Text Text -> HashMap Text Text
forall k v w.
(Eq k, Hashable k) =>
HashMap k v -> HashMap k w -> HashMap k v
HashMap.intersection HashMap Text Text
h1 HashMap Text Text
h2
              h1 :: HashMap Text Text
h1 = Environment -> HashMap Text Text
fromEnvironment Environment
e1
              h2 :: HashMap Text Text
h2 = Environment -> HashMap Text Text
fromEnvironment Environment
e2
           in if HashMap Text Text -> Bool
forall k v. HashMap k v -> Bool
HashMap.null HashMap Text Text
i
                Bool -> Bool -> Bool
|| (Text -> Bool) -> [Text] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all
                  ( \Text
k -> Text -> HashMap Text Text -> Maybe Text
forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
HashMap.lookup Text
k HashMap Text Text
h1 Maybe Text -> Maybe Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text -> HashMap Text Text -> Maybe Text
forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
HashMap.lookup Text
k HashMap Text Text
h2
                  )
                  (HashMap Text Text -> [Text]
forall k v. HashMap k v -> [k]
HashMap.keys HashMap Text Text
i)
                then HashMap Text Text
h1 HashMap Text Text -> HashMap Text Text -> HashMap Text Text
forall a. Semigroup a => a -> a -> a
<> HashMap Text Text
h2
                else
                  String -> HashMap Text Text
forall a. HasCallStack => String -> a
error
                    ( String
"Overlapping entries (<>): ("
                        String -> ShowS
forall a. [a] -> [a] -> [a]
++ Environment -> String
forall a. Show a => a -> String
show Environment
e1
                        String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
") <> ("
                        String -> ShowS
forall a. [a] -> [a] -> [a]
++ Environment -> String
forall a. Show a => a -> String
show Environment
e2
                        String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"): ("
                        String -> ShowS
forall a. [a] -> [a] -> [a]
++ HashMap Text Text -> String
forall a. Show a => a -> String
show HashMap Text Text
i
                        String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
")"
                    )
      }

instance Monoid Environment where
  mempty :: Environment
mempty = Int -> HashMap Text Text -> Environment
MkEnvironment Int
1 HashMap Text Text
forall k v. HashMap k v
HashMap.empty

-- | If environment variables @arg_1 .. arg_n@ are bound
-- and a list of @k@ additional values are passed to this function,
-- store them with keys @arg_(n+1) .. arg_(n+k)@.
--
-- Note that the Environment contains an index of the next position.
--
-- @since 0.5.62
addPositionalArguments :: [Text] -> Environment -> Environment
addPositionalArguments :: [Text] -> Environment -> Environment
addPositionalArguments =
  (Environment -> [Text] -> Environment)
-> [Text] -> Environment -> Environment
forall a b c. (a -> b -> c) -> b -> a -> c
flip
    ( (Environment -> Text -> Environment)
-> Environment -> [Text] -> Environment
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl'
        ( \(MkEnvironment Int
i HashMap Text Text
e) Text
arg ->
            Int -> HashMap Text Text -> Environment
MkEnvironment
              (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1)
              (Text -> Text -> HashMap Text Text -> HashMap Text Text
forall k v.
(Eq k, Hashable k) =>
k -> v -> HashMap k v -> HashMap k v
HashMap.insert (String -> Text
forall a. (Textual a, HasCallStack) => a -> Text
unsafeRenderToText (String
"arg_" String -> ShowS
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show Int
i)) Text
arg HashMap Text Text
e)
        )
    )

-- | Convenient wrapper around 'addPositionalArguments' and 'localEnvironment'.
--
-- @since 0.5.65
addLocalPositionalArguments ::
  Member EnvironmentReader e => [String] -> Eff e a -> Eff e a
addLocalPositionalArguments :: [String] -> Eff e a -> Eff e a
addLocalPositionalArguments [String]
extraPositional = (Environment -> Environment) -> Eff e a -> Eff e a
forall (e :: [* -> *]) a.
Member EnvironmentReader e =>
(Environment -> Environment) -> Eff e a -> Eff e a
localEnvironment Environment -> Environment
appendVars
  where
    appendVars :: Environment -> Environment
appendVars = [Text] -> Environment -> Environment
addPositionalArguments (String -> Text
forall a. (Textual a, HasCallStack) => a -> Text
unsafeRenderToText (String -> Text) -> [String] -> [Text]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [String]
extraPositional)

-- | Create an 'Environment' from a list of pairs ('String's).
-- Duplicated entries are ignored.
--
-- @since 0.5.62
fromStringPairs :: [(String, String)] -> Environment
fromStringPairs :: [(String, String)] -> Environment
fromStringPairs =
  Int -> HashMap Text Text -> Environment
MkEnvironment Int
0 (HashMap Text Text -> Environment)
-> ([(String, String)] -> HashMap Text Text)
-> [(String, String)]
-> Environment
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [(Text, Text)] -> HashMap Text Text
forall k v. (Eq k, Hashable k) => [(k, v)] -> HashMap k v
HashMap.fromList
    ([(Text, Text)] -> HashMap Text Text)
-> ([(String, String)] -> [(Text, Text)])
-> [(String, String)]
-> HashMap Text Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((String, String) -> (Text, Text))
-> [(String, String)] -> [(Text, Text)]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap
      (String -> Text
forall a. (Textual a, HasCallStack) => a -> Text
unsafeRenderToText (String -> Text)
-> (String -> Text) -> (String, String) -> (Text, Text)
forall (a :: * -> * -> *) b c b' c'.
Arrow a =>
a b c -> a b' c' -> a (b, b') (c, c')
*** String -> Text
forall a. (Textual a, HasCallStack) => a -> Text
unsafeRenderToText)

-- | Insert a key value binding to the 'Environment'.
--
-- Throw 'DuplicateKey' if the key already exists, but
-- the value is not equal to the given value.
--
-- @since 0.5.67
addBinding :: Member ExcB9 e => (Text, Text) -> Environment -> Eff e Environment
addBinding :: (Text, Text) -> Environment -> Eff e Environment
addBinding (Text
k, Text
vNew) Environment
env =
  let h :: HashMap Text Text
h = Environment -> HashMap Text Text
fromEnvironment Environment
env
   in case Text -> HashMap Text Text -> Maybe Text
forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
HashMap.lookup Text
k HashMap Text Text
h of
        Just Text
vOld
          | Text
vOld Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
/= Text
vNew ->
            DuplicateKey -> Eff e Environment
forall (e :: [* -> *]) x a.
(Member ExcB9 e, Exception x) =>
x -> Eff e a
throwSomeException (Text -> Text -> Text -> DuplicateKey
MkDuplicateKey Text
k Text
vOld Text
vNew)
        Maybe Text
_ -> Environment -> Eff e Environment
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Int -> HashMap Text Text -> Environment
MkEnvironment (Environment -> Int
nextPosition Environment
env) (Text -> Text -> HashMap Text Text -> HashMap Text Text
forall k v.
(Eq k, Hashable k) =>
k -> v -> HashMap k v -> HashMap k v
HashMap.insert Text
k Text
vNew HashMap Text Text
h))

-- | Insert 'String's into the 'Environment', see 'addBinding'.
--
-- @since 0.5.62
addStringBinding ::
  Member ExcB9 e => (String, String) -> Environment -> Eff e Environment
addStringBinding :: (String, String) -> Environment -> Eff e Environment
addStringBinding = (Text, Text) -> Environment -> Eff e Environment
forall (e :: [* -> *]).
Member ExcB9 e =>
(Text, Text) -> Environment -> Eff e Environment
addBinding ((Text, Text) -> Environment -> Eff e Environment)
-> ((String, String) -> (Text, Text))
-> (String, String)
-> Environment
-> Eff e Environment
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String -> Text
forall a. (Textual a, HasCallStack) => a -> Text
unsafeRenderToText (String -> Text)
-> (String -> Text) -> (String, String) -> (Text, Text)
forall (a :: * -> * -> *) b c b' c'.
Arrow a =>
a b c -> a b' c' -> a (b, b') (c, c')
*** String -> Text
forall a. (Textual a, HasCallStack) => a -> Text
unsafeRenderToText)

-- | Insert a value into an 'Environment' like 'addStringBinding',
-- but add it to the environment of the given effect, as in 'localEnvironment'.
--
-- @since 0.5.65
addLocalStringBinding ::
  (Member EnvironmentReader e, Member ExcB9 e) =>
  (String, String) ->
  Eff e a ->
  Eff e a
addLocalStringBinding :: (String, String) -> Eff e a -> Eff e a
addLocalStringBinding (String, String)
binding Eff e a
action = do
  Environment
e <- Eff e Environment
forall (e :: [* -> *]).
Member EnvironmentReader e =>
Eff e Environment
askEnvironment
  Environment
e' <- (String, String) -> Environment -> Eff e Environment
forall (e :: [* -> *]).
Member ExcB9 e =>
(String, String) -> Environment -> Eff e Environment
addStringBinding (String, String)
binding Environment
e
  (Environment -> Environment) -> Eff e a -> Eff e a
forall (e :: [* -> *]) a.
Member EnvironmentReader e =>
(Environment -> Environment) -> Eff e a -> Eff e a
localEnvironment (Environment -> Environment -> Environment
forall a b. a -> b -> a
const Environment
e') Eff e a
action

-- | A monad transformer providing a 'MonadReader' instance for 'Environment'
--
-- @since 0.5.62
type EnvironmentReader = Reader Environment

-- | Run a 'ReaderT' of 'Environment'.
--
-- @since 0.5.62
runEnvironmentReader :: Environment -> Eff (EnvironmentReader ': e) a -> Eff e a
runEnvironmentReader :: Environment -> Eff (EnvironmentReader : e) a -> Eff e a
runEnvironmentReader = Environment -> Eff (EnvironmentReader : e) a -> Eff e a
forall e (r :: [* -> *]) w. e -> Eff (Reader e : r) w -> Eff r w
runReader

-- | Get the current 'Environment'
--
-- @since 0.5.62
askEnvironment :: Member EnvironmentReader e => Eff e Environment
askEnvironment :: Eff e Environment
askEnvironment = Eff e Environment
forall e (r :: [* -> *]). Member (Reader e) r => Eff r e
ask

-- | Run a computation with a modified 'Environment'
--
-- @since 0.5.62
localEnvironment ::
  Member EnvironmentReader e =>
  (Environment -> Environment) ->
  Eff e a ->
  Eff e a
localEnvironment :: (Environment -> Environment) -> Eff e a -> Eff e a
localEnvironment = (Environment -> Environment) -> Eff e a -> Eff e a
forall e a (r :: [* -> *]).
Member (Reader e) r =>
(e -> e) -> Eff r a -> Eff r a
local

-- | Lookup a key for a value.
--
-- 'throwM' a 'KeyNotFound' 'Exception' if no value with the given key exists
-- in the 'Environment'.
--
-- @Since 0.5.62
lookupOrThrow :: ('[ExcB9, EnvironmentReader] <:: e) => Text -> Eff e Text
lookupOrThrow :: Text -> Eff e Text
lookupOrThrow Text
key = do
  Environment
env <- Eff e Environment
forall (e :: [* -> *]).
Member EnvironmentReader e =>
Eff e Environment
askEnvironment
  Eff e Text -> (Text -> Eff e Text) -> Maybe Text -> Eff e Text
forall b a. b -> (a -> b) -> Maybe a -> b
maybe
    (KeyNotFound -> Eff e Text
forall (e :: [* -> *]) x a.
(Member ExcB9 e, Exception x) =>
x -> Eff e a
throwSomeException (Text -> Environment -> KeyNotFound
MkKeyNotFound Text
key Environment
env))
    Text -> Eff e Text
forall (m :: * -> *) a. Monad m => a -> m a
return
    (Text -> HashMap Text Text -> Maybe Text
forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
HashMap.lookup Text
key (Environment -> HashMap Text Text
fromEnvironment Environment
env))

-- | Lookup a key for a value.
--
-- Return 'Either' 'Left' 'KeyNotFound', if no value with the given key exists
-- in the 'Environment', or 'Right' the value.
--
-- @Since 0.5.62
lookupEither ::
  Member EnvironmentReader e => Text -> Eff e (Either KeyNotFound Text)
lookupEither :: Text -> Eff e (Either KeyNotFound Text)
lookupEither Text
key = do
  Environment
env <- Eff e Environment
forall (e :: [* -> *]).
Member EnvironmentReader e =>
Eff e Environment
askEnvironment
  (Either KeyNotFound Text -> Eff e (Either KeyNotFound Text)
forall (m :: * -> *) a. Monad m => a -> m a
return (Either KeyNotFound Text -> Eff e (Either KeyNotFound Text))
-> (Maybe Text -> Either KeyNotFound Text)
-> Maybe Text
-> Eff e (Either KeyNotFound Text)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Either KeyNotFound Text
-> (Text -> Either KeyNotFound Text)
-> Maybe Text
-> Either KeyNotFound Text
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (KeyNotFound -> Either KeyNotFound Text
forall a b. a -> Either a b
Left (Text -> Environment -> KeyNotFound
MkKeyNotFound Text
key Environment
env)) Text -> Either KeyNotFound Text
forall a b. b -> Either a b
Right)
    (Text -> HashMap Text Text -> Maybe Text
forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
HashMap.lookup Text
key (Environment -> HashMap Text Text
fromEnvironment Environment
env))

-- | An 'Exception' thrown by 'addBinding' indicating that a key already exists.
--
-- @Since 0.5.62
data DuplicateKey
  = MkDuplicateKey
      { DuplicateKey -> Text
duplicateKey :: Text,
        DuplicateKey -> Text
duplicateKeyOldValue :: Text,
        DuplicateKey -> Text
duplicateKeyNewValue :: Text
      }
  deriving (Typeable, Int -> DuplicateKey -> ShowS
[DuplicateKey] -> ShowS
DuplicateKey -> String
(Int -> DuplicateKey -> ShowS)
-> (DuplicateKey -> String)
-> ([DuplicateKey] -> ShowS)
-> Show DuplicateKey
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [DuplicateKey] -> ShowS
$cshowList :: [DuplicateKey] -> ShowS
show :: DuplicateKey -> String
$cshow :: DuplicateKey -> String
showsPrec :: Int -> DuplicateKey -> ShowS
$cshowsPrec :: Int -> DuplicateKey -> ShowS
Show, DuplicateKey -> DuplicateKey -> Bool
(DuplicateKey -> DuplicateKey -> Bool)
-> (DuplicateKey -> DuplicateKey -> Bool) -> Eq DuplicateKey
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: DuplicateKey -> DuplicateKey -> Bool
$c/= :: DuplicateKey -> DuplicateKey -> Bool
== :: DuplicateKey -> DuplicateKey -> Bool
$c== :: DuplicateKey -> DuplicateKey -> Bool
Eq)

instance Exception DuplicateKey

-- | An 'Exception' thrown by 'lookupOrThrow' indicating that a key does not exist.
--
-- @Since 0.5.62
data KeyNotFound
  = MkKeyNotFound
      Text
      Environment
  deriving (Typeable, KeyNotFound -> KeyNotFound -> Bool
(KeyNotFound -> KeyNotFound -> Bool)
-> (KeyNotFound -> KeyNotFound -> Bool) -> Eq KeyNotFound
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: KeyNotFound -> KeyNotFound -> Bool
$c/= :: KeyNotFound -> KeyNotFound -> Bool
== :: KeyNotFound -> KeyNotFound -> Bool
$c== :: KeyNotFound -> KeyNotFound -> Bool
Eq)

instance Exception KeyNotFound

instance Show KeyNotFound where
  showsPrec :: Int -> KeyNotFound -> ShowS
showsPrec Int
_ (MkKeyNotFound Text
key Environment
env) =
    let keys :: String
keys =
          [String] -> String
unlines (Text -> String
forall a. (Textual a, HasCallStack) => Text -> a
unsafeParseFromText (Text -> String) -> [Text] -> [String]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> HashMap Text Text -> [Text]
forall k v. HashMap k v -> [k]
HashMap.keys (Environment -> HashMap Text Text
fromEnvironment Environment
env))
     in String -> ShowS
showString String
"Invalid template parameter: \""
          ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> ShowS
showString (Text -> String
forall a. (Textual a, HasCallStack) => Text -> a
unsafeParseFromText Text
key)
          ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> ShowS
showString String
"\".\nValid variables:\n"
          ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> ShowS
showString String
keys

-- | A predicate that is satisfied when a key exists in the environment.
--
-- @since 0.5.64
hasKey :: Member EnvironmentReader e => Text -> Eff e Bool
hasKey :: Text -> Eff e Bool
hasKey Text
k = Maybe Text -> Bool
forall a. Maybe a -> Bool
isJust (Maybe Text -> Bool)
-> (Environment -> Maybe Text) -> Environment -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> HashMap Text Text -> Maybe Text
forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
HashMap.lookup Text
k (HashMap Text Text -> Maybe Text)
-> (Environment -> HashMap Text Text) -> Environment -> Maybe Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Environment -> HashMap Text Text
fromEnvironment (Environment -> Bool) -> Eff e Environment -> Eff e Bool
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Eff e Environment
forall (e :: [* -> *]).
Member EnvironmentReader e =>
Eff e Environment
askEnvironment