{ lib, pkgs, config, ... }: let inherit (lib) filter assertMsg head length escapeShellArg types mkOption concatStringsSep; envToBash = { name, value, eval, prefix, ... } @ args: let vals = filter (key: args.${key} != null && args.${key} != false) [ "eval" "prefix" "unset" "value" ]; valType = head vals; in assert assertMsg ( (length vals) > 0 ) "[devshell/env]: ${name} expected one of (value|eval|prefix|unset) to be set."; assert assertMsg ((length vals) < 2) "[devshell/env]: ${name} expected only one of (value|eval|prefix|unset) to be set. Not ${toString vals}"; assert assertMsg (!(name == "PATH" && valType == "value")) "[devshell/env]: ${name} should not override the value. Use 'prefix' instead."; if valType == "value" then "export ${name}=${escapeShellArg (toString value)}" else if valType == "eval" then "export ${name}=${eval}" else if valType == "prefix" then ''export ${name}=$(${pkgs.coreutils}/bin/realpath --canonicalize-missing "${prefix}")''${${name}+:''${${name}}}'' else if valType == "unset" then ''unset ${name}'' else throw "[devshell] BUG in the env module. This should never be reached."; envType = types.submodule ({name, ...}: { options = { name = mkOption { type = types.str; default = name; }; value = mkOption { type = with types; nullOr (oneOf [ str int bool package ]); default = null; }; eval = mkOption { type = types.nullOr types.str; default = null; description = '' Like value but not evaluated by Bash. This allows to inject other variable names or even commands using the `$()` notation. ''; example = "$OTHER_VAR"; }; prefix = mkOption { type = types.nullOr types.str; default = null; description = '' Prepend to PATH-like environment variables. For example name = "PATH"; prefix = "bin"; will expand the path of ./bin and prepend it to the PATH, separated by ':'. ''; example = "bin"; }; unset = mkOption { type = types.bool; default = false; description = ''Unset the env variable''; }; }; }); in { options.env = mkOption { type = types.attrsOf envType; default = {}; description = ''''; }; config = { env = { "XDG_DATA_DIRS".eval = "$DEVSHELL_DIR/share:\${XDG_DATA_DIRS-}"; }; enterShellCommands."env".text = concatStringsSep "\n" (map envToBash (builtins.attrValues config.env)); }; }