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;
48 inherit (lib) mkOptionType;
49 inherit (lib.types) nullOr oneOf coercedTo listOf nonEmptyListOf attrsOf either;
50 inherit (lib.types) bool int float str path;
55 valueType = nullOr (oneOf [
64 description = "JSON value";
68 generate = name: value: pkgs.callPackage ({ runCommand, jq }: runCommand name {
69 nativeBuildInputs = [ jq ];
70 value = builtins.toJSON value;
71 passAsFile = [ "value" ];
72 preferLocalBuild = true;
74 jq . "$valuePath"> $out
82 generate = name: value: pkgs.callPackage ({ runCommand, remarshal_0_17 }: runCommand name {
83 nativeBuildInputs = [ remarshal_0_17 ];
84 value = builtins.toJSON value;
85 passAsFile = [ "value" ];
86 preferLocalBuild = true;
88 json2yaml "$valuePath" "$out"
92 valueType = nullOr (oneOf [
101 description = "YAML value";
107 # the ini formats share a lot of code
110 singleIniAtom = nullOr (oneOf [ bool int float str ]) // {
111 description = "INI atom (null, bool, int, float or string)";
113 iniAtom = { listsAsDuplicateKeys, listToValue, atomsCoercedToLists }:
115 singleIniAtomOr = if atomsCoercedToLists then coercedTo singleIniAtom lib.singleton else either singleIniAtom;
117 if listsAsDuplicateKeys then
118 singleIniAtomOr (listOf singleIniAtom) // {
119 description = singleIniAtom.description + " or a list of them for duplicate keys";
121 else if listToValue != null then
122 singleIniAtomOr (nonEmptyListOf singleIniAtom) // {
123 description = singleIniAtom.description + " or a non-empty list of them";
127 iniSection = { listsAsDuplicateKeys, listToValue, atomsCoercedToLists }@args:
128 attrsOf (iniAtom args) // {
129 description = "section of an INI file (attrs of " + (iniAtom args).description + ")";
132 maybeToList = listToValue: if listToValue != null then lib.mapAttrs (key: val: if lib.isList val then listToValue val else val) else lib.id;
135 # Represents lists as duplicate keys
136 listsAsDuplicateKeys ? false,
137 # Alternative to listsAsDuplicateKeys, converts list to non-list
138 # listToValue :: [IniAtom] -> IniAtom
140 # Merge multiple instances of the same key into a list
141 atomsCoercedToLists ? null,
144 assert listsAsDuplicateKeys -> listToValue == null;
145 assert atomsCoercedToLists != null -> (listsAsDuplicateKeys || listToValue != null);
147 atomsCoercedToLists' = if atomsCoercedToLists == null then false else atomsCoercedToLists;
151 type = lib.types.attrsOf (
152 iniSection { inherit listsAsDuplicateKeys listToValue; atomsCoercedToLists = atomsCoercedToLists'; }
155 generate = name: value:
158 (lib.mapAttrs (_: maybeToList listToValue))
159 (lib.generators.toINI (removeAttrs args ["listToValue" "atomsCoercedToLists"]))
160 (pkgs.writeText name)
164 iniWithGlobalSection = {
165 # Represents lists as duplicate keys
166 listsAsDuplicateKeys ? false,
167 # Alternative to listsAsDuplicateKeys, converts list to non-list
168 # listToValue :: [IniAtom] -> IniAtom
170 # Merge multiple instances of the same key into a list
171 atomsCoercedToLists ? null,
174 assert listsAsDuplicateKeys -> listToValue == null;
175 assert atomsCoercedToLists != null -> (listsAsDuplicateKeys || listToValue != null);
177 atomsCoercedToLists' = if atomsCoercedToLists == null then false else atomsCoercedToLists;
180 type = lib.types.submodule {
182 sections = lib.mkOption rec {
183 type = lib.types.attrsOf (
184 iniSection { inherit listsAsDuplicateKeys listToValue; atomsCoercedToLists = atomsCoercedToLists'; }
187 description = type.description;
189 globalSection = lib.mkOption rec {
190 type = iniSection { inherit listsAsDuplicateKeys listToValue; atomsCoercedToLists = atomsCoercedToLists'; };
192 description = "global " + type.description;
196 generate = name: { sections ? {}, globalSection ? {}, ... }:
197 pkgs.writeText name (lib.generators.toINIWithGlobalSection (removeAttrs args ["listToValue" "atomsCoercedToLists"])
199 globalSection = maybeToList listToValue globalSection;
200 sections = lib.mapAttrs (_: maybeToList listToValue) sections;
204 gitIni = { listsAsDuplicateKeys ? false, ... }@args: {
207 listsAsDuplicateKeys = listsAsDuplicateKeys;
209 atomsCoercedToLists = false;
211 in attrsOf (attrsOf (either atom (attrsOf atom)));
213 generate = name: value: pkgs.writeText name (lib.generators.toGitINI value);
216 }) ini iniWithGlobalSection gitIni;
218 # As defined by systemd.syntax(7)
220 # null does not set any value, which allows for RFC42 modules to specify
221 # optional config options.
223 mkValueString = lib.generators.mkValueStringDefault {};
225 if v == null then "# ${k} is unset"
226 else "${k} = ${mkValueString v}";
228 listsAsDuplicateKeys = true;
233 # Represents lists as duplicate keys
234 listsAsDuplicateKeys ? false,
235 # Alternative to listsAsDuplicateKeys, converts list to non-list
236 # listToValue :: [Atom] -> Atom
240 assert listsAsDuplicateKeys -> listToValue == null;
245 singleAtom = nullOr (oneOf [
251 description = "atom (null, bool, int, float or string)";
255 if listsAsDuplicateKeys then
256 coercedTo singleAtom lib.singleton (listOf singleAtom) // {
257 description = singleAtom.description + " or a list of them for duplicate keys";
259 else if listToValue != null then
260 coercedTo singleAtom lib.singleton (nonEmptyListOf singleAtom) // {
261 description = singleAtom.description + " or a non-empty list of them";
268 generate = name: value:
271 if listToValue != null
273 lib.mapAttrs (key: val:
274 if lib.isList val then listToValue val else val
277 in pkgs.writeText name (lib.generators.toKeyValue (removeAttrs args ["listToValue"]) transformedValue);
281 toml = {}: json {} // {
292 description = "TOML value";
296 generate = name: value: pkgs.callPackage ({ runCommand, remarshal }: runCommand name {
297 nativeBuildInputs = [ remarshal ];
298 value = builtins.toJSON value;
299 passAsFile = [ "value" ];
300 preferLocalBuild = true;
302 json2toml "$valuePath" "$out"
307 /* For configurations of Elixir project, like config.exs or runtime.exs
309 Most Elixir project are configured using the [Config] Elixir DSL
311 Since Elixir has more types than Nix, we need a way to map Nix types to
312 more than 1 Elixir type. To that end, this format provides its own library,
313 and its own set of types.
315 To be more detailed, a Nix attribute set could correspond in Elixir to a
316 [Keyword list] (the more common type), or it could correspond to a [Map].
318 A Nix string could correspond in Elixir to a [String] (also called
319 "binary"), an [Atom], or a list of chars (usually discouraged).
321 A Nix array could correspond in Elixir to a [List] or a [Tuple].
323 Some more types exists, like records, regexes, but since they are less used,
324 we can leave the `mkRaw` function as an escape hatch.
326 For more information on how to use this format in modules, please refer to
327 the Elixir section of the Nixos documentation.
329 TODO: special Elixir values doesn't show up nicely in the documentation
331 [Config]: <https://hexdocs.pm/elixir/Config.html>
332 [Keyword list]: <https://hexdocs.pm/elixir/Keyword.html>
333 [Map]: <https://hexdocs.pm/elixir/Map.html>
334 [String]: <https://hexdocs.pm/elixir/String.html>
335 [Atom]: <https://hexdocs.pm/elixir/Atom.html>
336 [List]: <https://hexdocs.pm/elixir/List.html>
337 [Tuple]: <https://hexdocs.pm/elixir/Tuple.html>
339 elixirConf = { elixir ? pkgs.elixir }:
342 if value == null then "nil" else
343 if value == true then "true" else
344 if value == false then "false" else
345 if lib.isInt value || lib.isFloat value then toString value else
346 if lib.isString value then string value else
347 if lib.isAttrs value then attrs value else
348 if lib.isList value then list value else
349 abort "formats.elixirConf: should never happen (value = ${value})";
351 escapeElixir = lib.escape [ "\\" "#" "\"" ];
352 string = value: "\"${escapeElixir value}\"";
355 if set ? _elixirType then specialType set
358 toKeyword = name: value: "${name}: ${toElixir value}";
359 keywordList = lib.concatStringsSep ", " (lib.mapAttrsToList toKeyword set);
361 "[" + keywordList + "]";
363 listContent = values: lib.concatStringsSep ", " (map toElixir values);
365 list = values: "[" + (listContent values) + "]";
367 specialType = { value, _elixirType }:
368 if _elixirType == "raw" then value else
369 if _elixirType == "atom" then value else
370 if _elixirType == "map" then elixirMap value else
371 if _elixirType == "tuple" then tuple value else
372 abort "formats.elixirConf: should never happen (_elixirType = ${_elixirType})";
376 toEntry = name: value: "${toElixir name} => ${toElixir value}";
377 entries = lib.concatStringsSep ", " (lib.mapAttrsToList toEntry set);
381 tuple = values: "{${listContent values}}";
385 keyConfig = rootKey: key: value:
386 "config ${rootKey}, ${key}, ${toElixir value}";
387 keyConfigs = rootKey: values: lib.mapAttrsToList (keyConfig rootKey) values;
388 rootConfigs = lib.flatten (lib.mapAttrsToList keyConfigs values);
393 ${lib.concatStringsSep "\n" rootConfigs}
407 description = "Elixir value";
410 attrsOf (attrsOf (valueType));
423 /* Fetch an environment variable at runtime, with optional fallback
425 mkGetEnv = { envVariable, fallback ? null }:
426 mkRaw "System.get_env(${toElixir envVariable}, ${toElixir fallback})";
428 /* Make an Elixir atom.
430 Note: lowercase atoms still need to be prefixed by ':'
434 _elixirType = "atom";
437 /* Make an Elixir tuple out of a list.
441 _elixirType = "tuple";
444 /* Make an Elixir map out of an attribute set.
451 /* Contains Elixir types. Every type it exports can also be replaced
452 by raw Elixir code (i.e. every type is `either type rawElixir`).
454 It also reexports standard types, wrapping them so that they can
458 isElixirType = type: x: (x._elixirType or "") == type;
460 rawElixir = mkOptionType {
462 description = "raw elixir";
463 check = isElixirType "raw";
466 elixirOr = other: either other rawElixir;
469 inherit rawElixir elixirOr;
471 atom = elixirOr (mkOptionType {
473 description = "elixir atom";
474 check = isElixirType "atom";
477 tuple = elixirOr (mkOptionType {
478 name = "elixirTuple";
479 description = "elixir tuple";
480 check = isElixirType "tuple";
483 map = elixirOr (mkOptionType {
485 description = "elixir map";
486 check = isElixirType "map";
488 # Wrap standard types, since anything in the Elixir configuration
490 } // lib.mapAttrs (_name: type: elixirOr type) lib.types;
493 generate = name: value: pkgs.runCommand name
495 value = toConf value;
496 passAsFile = [ "value" ];
497 nativeBuildInputs = [ elixir ];
498 preferLocalBuild = true;
500 cp "$valuePath" "$out"
505 # Outputs a succession of Python variable assignments
506 # Useful for many Django-based services
509 valueType = nullOr(oneOf [
518 description = "Python value";
520 in attrsOf valueType;
521 generate = name: value: pkgs.callPackage ({ runCommand, python3, black }: runCommand name {
522 nativeBuildInputs = [ python3 black ];
523 value = builtins.toJSON value;
528 with open(os.environ["valuePath"], "r") as f:
529 for key, value in json.load(f).items():
530 print(f"{key} = {repr(value)}")
532 passAsFile = [ "value" "pythonGen" ];
533 preferLocalBuild = true;
536 python3 "$pythonGenPath" > $out