devshell/lib/modules/env.nix
2025-07-31 12:37:19 +02:00

99 lines
2.7 KiB
Nix

{
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));
};
}