6 Every following entry represents a format for program configuration files
7 used for `settings`-style options (see https://github.com/NixOS/rfcs/pull/42).
8 Each entry should look as follows:
10 <format> = <parameters>: {
11 # ^^ Parameters for controlling the format
13 # The module system type most suitable for representing such a format
14 # The description needs to be overwritten for recursive types
17 # Utility functions for convenience, or special interactions with the
21 # Types specific to the format (optional)
26 # generate :: Name -> Value -> Path
27 # A function for generating a file with a value of such a type
32 Please note that `pkgs` may not always be available for use due to the split
33 options doc build introduced in fc614c37c653, so lazy evaluation of only the
34 'type' field is required.
39 inherit (import ./formats/java-properties/default.nix { inherit lib pkgs; })
42 libconfig = (import ./formats/libconfig/default.nix { inherit lib pkgs; }).format;
44 hocon = (import ./formats/hocon/default.nix { inherit lib pkgs; }).format;
46 php = (import ./formats/php/default.nix { inherit lib pkgs; }).format;
50 type = with lib.types; let
51 valueType = nullOr (oneOf [
60 description = "JSON value";
64 generate = name: value: pkgs.callPackage ({ runCommand, jq }: runCommand name {
65 nativeBuildInputs = [ jq ];
66 value = builtins.toJSON value;
67 passAsFile = [ "value" ];
68 preferLocalBuild = true;
70 jq . "$valuePath"> $out
77 generate = name: value: pkgs.callPackage ({ runCommand, remarshal }: runCommand name {
78 nativeBuildInputs = [ remarshal ];
79 value = builtins.toJSON value;
80 passAsFile = [ "value" ];
81 preferLocalBuild = true;
83 json2yaml "$valuePath" "$out"
86 type = with lib.types; let
87 valueType = nullOr (oneOf [
96 description = "YAML value";
102 # the ini formats share a lot of code
105 singleIniAtom = with lib.types; nullOr (oneOf [ bool int float str ]) // {
106 description = "INI atom (null, bool, int, float or string)";
108 iniAtom = with lib.types; { listsAsDuplicateKeys, listToValue }:
109 if listsAsDuplicateKeys then
110 coercedTo singleIniAtom lib.singleton (listOf singleIniAtom) // {
111 description = singleIniAtom.description + " or a list of them for duplicate keys";
113 else if listToValue != null then
114 coercedTo singleIniAtom lib.singleton (nonEmptyListOf singleIniAtom) // {
115 description = singleIniAtom.description + " or a non-empty list of them";
119 iniSection = with lib.types; { listsAsDuplicateKeys, listToValue }@args:
120 attrsOf (iniAtom args) // {
121 description = "section of an INI file (attrs of " + (iniAtom args).description + ")";
124 maybeToList = listToValue: if listToValue != null then lib.mapAttrs (key: val: if lib.isList val then listToValue val else val) else lib.id;
127 # Represents lists as duplicate keys
128 listsAsDuplicateKeys ? false,
129 # Alternative to listsAsDuplicateKeys, converts list to non-list
130 # listToValue :: [IniAtom] -> IniAtom
134 assert listsAsDuplicateKeys -> listToValue == null;
137 type = lib.types.attrsOf (iniSection { listsAsDuplicateKeys = listsAsDuplicateKeys; listToValue = listToValue; });
139 generate = name: value:
142 (lib.mapAttrs (_: maybeToList listToValue))
143 (lib.generators.toINI (removeAttrs args ["listToValue"]))
144 (pkgs.writeText name)
148 iniWithGlobalSection = {
149 # Represents lists as duplicate keys
150 listsAsDuplicateKeys ? false,
151 # Alternative to listsAsDuplicateKeys, converts list to non-list
152 # listToValue :: [IniAtom] -> IniAtom
156 assert listsAsDuplicateKeys -> listToValue == null;
158 type = lib.types.submodule {
160 sections = lib.mkOption rec {
161 type = lib.types.attrsOf (iniSection { listsAsDuplicateKeys = listsAsDuplicateKeys; listToValue = listToValue; });
163 description = type.description;
165 globalSection = lib.mkOption rec {
166 type = iniSection { listsAsDuplicateKeys = listsAsDuplicateKeys; listToValue = listToValue; };
168 description = "global " + type.description;
172 generate = name: { sections ? {}, globalSection ? {}, ... }:
173 pkgs.writeText name (lib.generators.toINIWithGlobalSection (removeAttrs args ["listToValue"])
175 globalSection = maybeToList listToValue globalSection;
176 sections = lib.mapAttrs (_: maybeToList listToValue) sections;
180 gitIni = { listsAsDuplicateKeys ? false, ... }@args: {
183 listsAsDuplicateKeys = listsAsDuplicateKeys;
186 in with lib.types; attrsOf (attrsOf (either atom (attrsOf atom)));
188 generate = name: value: pkgs.writeText name (lib.generators.toGitINI value);
191 }) ini iniWithGlobalSection gitIni;
193 # As defined by systemd.syntax(7)
195 # null does not set any value, which allows for RFC42 modules to specify
196 # optional config options.
198 mkValueString = lib.generators.mkValueStringDefault {};
200 if v == null then "# ${k} is unset"
201 else "${k} = ${mkValueString v}";
203 listsAsDuplicateKeys = true;
208 # Represents lists as duplicate keys
209 listsAsDuplicateKeys ? false,
210 # Alternative to listsAsDuplicateKeys, converts list to non-list
211 # listToValue :: [Atom] -> Atom
215 assert listsAsDuplicateKeys -> listToValue == null;
218 type = with lib.types; let
220 singleAtom = nullOr (oneOf [
226 description = "atom (null, bool, int, float or string)";
230 if listsAsDuplicateKeys then
231 coercedTo singleAtom lib.singleton (listOf singleAtom) // {
232 description = singleAtom.description + " or a list of them for duplicate keys";
234 else if listToValue != null then
235 coercedTo singleAtom lib.singleton (nonEmptyListOf singleAtom) // {
236 description = singleAtom.description + " or a non-empty list of them";
243 generate = name: value:
246 if listToValue != null
248 lib.mapAttrs (key: val:
249 if lib.isList val then listToValue val else val
252 in pkgs.writeText name (lib.generators.toKeyValue (removeAttrs args ["listToValue"]) transformedValue);
256 toml = {}: json {} // {
257 type = with lib.types; let
267 description = "TOML value";
271 generate = name: value: pkgs.callPackage ({ runCommand, remarshal }: runCommand name {
272 nativeBuildInputs = [ remarshal ];
273 value = builtins.toJSON value;
274 passAsFile = [ "value" ];
275 preferLocalBuild = true;
277 json2toml "$valuePath" "$out"
282 /* For configurations of Elixir project, like config.exs or runtime.exs
284 Most Elixir project are configured using the [Config] Elixir DSL
286 Since Elixir has more types than Nix, we need a way to map Nix types to
287 more than 1 Elixir type. To that end, this format provides its own library,
288 and its own set of types.
290 To be more detailed, a Nix attribute set could correspond in Elixir to a
291 [Keyword list] (the more common type), or it could correspond to a [Map].
293 A Nix string could correspond in Elixir to a [String] (also called
294 "binary"), an [Atom], or a list of chars (usually discouraged).
296 A Nix array could correspond in Elixir to a [List] or a [Tuple].
298 Some more types exists, like records, regexes, but since they are less used,
299 we can leave the `mkRaw` function as an escape hatch.
301 For more information on how to use this format in modules, please refer to
302 the Elixir section of the Nixos documentation.
304 TODO: special Elixir values doesn't show up nicely in the documentation
306 [Config]: <https://hexdocs.pm/elixir/Config.html>
307 [Keyword list]: <https://hexdocs.pm/elixir/Keyword.html>
308 [Map]: <https://hexdocs.pm/elixir/Map.html>
309 [String]: <https://hexdocs.pm/elixir/String.html>
310 [Atom]: <https://hexdocs.pm/elixir/Atom.html>
311 [List]: <https://hexdocs.pm/elixir/List.html>
312 [Tuple]: <https://hexdocs.pm/elixir/Tuple.html>
314 elixirConf = { elixir ? pkgs.elixir }:
316 toElixir = value: with builtins;
317 if value == null then "nil" else
318 if value == true then "true" else
319 if value == false then "false" else
320 if isInt value || isFloat value then toString value else
321 if isString value then string value else
322 if isAttrs value then attrs value else
323 if isList value then list value else
324 abort "formats.elixirConf: should never happen (value = ${value})";
326 escapeElixir = escape [ "\\" "#" "\"" ];
327 string = value: "\"${escapeElixir value}\"";
330 if set ? _elixirType then specialType set
333 toKeyword = name: value: "${name}: ${toElixir value}";
334 keywordList = concatStringsSep ", " (mapAttrsToList toKeyword set);
336 "[" + keywordList + "]";
338 listContent = values: concatStringsSep ", " (map toElixir values);
340 list = values: "[" + (listContent values) + "]";
342 specialType = { value, _elixirType }:
343 if _elixirType == "raw" then value else
344 if _elixirType == "atom" then value else
345 if _elixirType == "map" then elixirMap value else
346 if _elixirType == "tuple" then tuple value else
347 abort "formats.elixirConf: should never happen (_elixirType = ${_elixirType})";
351 toEntry = name: value: "${toElixir name} => ${toElixir value}";
352 entries = concatStringsSep ", " (mapAttrsToList toEntry set);
356 tuple = values: "{${listContent values}}";
360 keyConfig = rootKey: key: value:
361 "config ${rootKey}, ${key}, ${toElixir value}";
362 keyConfigs = rootKey: values: mapAttrsToList (keyConfig rootKey) values;
363 rootConfigs = flatten (mapAttrsToList keyConfigs values);
368 ${concatStringsSep "\n" rootConfigs}
372 type = with lib.types; let
382 description = "Elixir value";
385 attrsOf (attrsOf (valueType));
398 /* Fetch an environment variable at runtime, with optional fallback
400 mkGetEnv = { envVariable, fallback ? null }:
401 mkRaw "System.get_env(${toElixir envVariable}, ${toElixir fallback})";
403 /* Make an Elixir atom.
405 Note: lowercase atoms still need to be prefixed by ':'
409 _elixirType = "atom";
412 /* Make an Elixir tuple out of a list.
416 _elixirType = "tuple";
419 /* Make an Elixir map out of an attribute set.
426 /* Contains Elixir types. Every type it exports can also be replaced
427 by raw Elixir code (i.e. every type is `either type rawElixir`).
429 It also reexports standard types, wrapping them so that they can
432 types = with lib.types; let
433 isElixirType = type: x: (x._elixirType or "") == type;
435 rawElixir = mkOptionType {
437 description = "raw elixir";
438 check = isElixirType "raw";
441 elixirOr = other: either other rawElixir;
444 inherit rawElixir elixirOr;
446 atom = elixirOr (mkOptionType {
448 description = "elixir atom";
449 check = isElixirType "atom";
452 tuple = elixirOr (mkOptionType {
453 name = "elixirTuple";
454 description = "elixir tuple";
455 check = isElixirType "tuple";
458 map = elixirOr (mkOptionType {
460 description = "elixir map";
461 check = isElixirType "map";
463 # Wrap standard types, since anything in the Elixir configuration
465 } // lib.mapAttrs (_name: type: elixirOr type) lib.types;
468 generate = name: value: pkgs.runCommand name
470 value = toConf value;
471 passAsFile = [ "value" ];
472 nativeBuildInputs = [ elixir ];
473 preferLocalBuild = true;
475 cp "$valuePath" "$out"
480 # Outputs a succession of Python variable assignments
481 # Useful for many Django-based services
483 type = with lib.types; let
484 valueType = nullOr(oneOf [
493 description = "Python value";
495 in attrsOf valueType;
496 generate = name: value: pkgs.callPackage ({ runCommand, python3, black }: runCommand name {
497 nativeBuildInputs = [ python3 black ];
498 value = builtins.toJSON value;
503 with open(os.environ["valuePath"], "r") as f:
504 for key, value in json.load(f).items():
505 print(f"{key} = {repr(value)}")
507 passAsFile = [ "value" "pythonGen" ];
508 preferLocalBuild = true;
511 python3 "$pythonGenPath" > $out