richenv: Rich environment variable setup for Haskell

This is a package candidate release! Here you can preview how this package release will appear once published to the main package index (which can be accomplished via the 'maintain' link below). Please note that once a package has been published to the main package index it cannot be undone! Please consult the package uploading documentation for more information.

[maintain] [Publish]

This package exposes a type that captures a set of rules to modify an existing environment variable set, be it a provided list of key-value pairs (list of duples) or the system's environment variable set itself. Each rule can be either a prefix, a mapping or a value. See README.md for more details.


[Skip to Readme]

Properties

Versions 0.1.0.0, 0.1.0.1, 0.1.0.2, 0.1.0.2
Change log CHANGELOG.md
Dependencies aeson (>=2.1.2.1 && <2.3), base (>=4.17 && <5), text (>=2.0 && <3), unordered-containers (>=0.2.20 && <0.3) [details]
License MIT
Copyright 2024 David Sánchez
Author David Sánchez
Maintainer davidslt+git@pm.me
Category Configuration
Home page https://github.com/DavSanchez/richenv
Source repo head: git clone https://github.com/DavSanchez/richenv.git
Uploaded by DavSanchez at 2024-05-20T22:04:15Z

Modules

[Index] [Quick Jump]

Downloads

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees


Readme for richenv-0.1.0.2

[back to package description]

RichEnv

Tests

Hackage Version richenv on Stackage Nightly richenv on Stackage LTS

nixpkgs unstable nixpkgs stable

Rich environment variable setup for Haskell

built with nix

This package exposes a type that captures a set of rules that modify an existing environment variable set, be it a provided list of key-value pairs (list of tuples) or the system's environment variable set.

Each rule can be either a prefix, a mapping or a value. Prefixes take environment variable names and prepend a prefix to them, replacing existing prefixes (i.e. fist removing old prefix, then adding the new one) if desired. Mappings replace the name of the environment variable with a different one, and values create the environment variable with the provided value.

Getting started

The idea behind this library is that you can find a set of rules for setting environment variables that may or may not use the current environment as starting stage, to replace the ones in the current process or pass a custom env to System.Process.CreateProcess to spawn some sub-process.

If your application uses a configuration file, for example in YAML format, you could add a new field to your config like this:

# example.yaml
env:
  values:
    VERBOSE: "true"
  mappings:
    NEWNAME: OLDNAME
  prefixes:
    NEW_PREFIX_:
      - PREFIXED_
      - OTHER_PREFIXED_
    OTHER_NEW_PREFIX_: [OTHER_PREFIXED_]
# More configs ...

When parsing this new env field as the RichEnv type (it provides FromJSON/ToJSON instances), this defines a set of rules:

Thus, after parsing, you will end up with a set of environment variables that you can:

You can either provide a list of environment variables (normally of type [(Text, Text)]) to apply RichEnv rules or use the environment variables from the current process.

Code example

Assuming you are using the previous YAML example (and a controlled set of environment variables for the current process, see steps 1 and 2 below), you could use RichEnv modify the environment variables like this:

{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}

module Main where

import Data.Aeson (FromJSON, ToJSON)
import Data.Yaml (decodeFileEither)
import Data.Yaml.Aeson (ParseException)
import GHC.Generics (Generic)
import RichEnv (RichEnv (..), clearEnvironment, setRichEnvFromCurrent)
import System.Environment (getEnvironment, setEnv)

newtype SomeConfig = SomeConfig {env :: RichEnv} deriving (Show, Eq, Generic, FromJSON, ToJSON)

main :: IO ()
main = do
  decodedYaml <- decodeFileEither "./example.yaml" :: IO (Either ParseException SomeConfig)
  case decodedYaml of
    Left err -> error $ show err
    Right rEnv -> do
      -- Successfully parsed. Now we can use the RichEnv
      -- 1. clear the environment of the current process
      getEnvironment >>= clearEnvironment
      -- 2. Set an example environment for the current process
      mapM_ (uncurry setEnv) [("FOO", "bar"), ("OLDNAME", "qux"), ("PREFIXED_VAR", "content"), ("OTHER_PREFIXED_VAR2", "content2")]
      -- 3. modify the current environment with the RichEnv
      setRichEnvFromCurrent (env rEnv)
      -- 4. check the environment again
      getEnvironment >>= print

-- printedOutput =
--   [ ("OTHER_NEW_PREFIX_VAR2", "content2"),
--     ("VERBOSE", "true"),
--     ("NEWNAME", "qux"),
--     ("NEW_PREFIX_VAR", "content"),
--     ("NEW_PREFIX_VAR2", "content2")
--   ]
  -- ...

As mentioned before, instead of modifying the current process, you use RichEnv to spawn processes with custom environments (for example with System.Process' proc and CreateProcess' env field) defined with your rules, effectively controlling how the environment is forwarded from the current process to the spawned ones.

See the Hackage documentation and the tests for more details and examples.