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
81 generate = name: value: pkgs.callPackage ({ runCommand, remarshal }: runCommand name {
82 nativeBuildInputs = [ remarshal ];
83 value = builtins.toJSON value;
84 passAsFile = [ "value" ];
85 preferLocalBuild = true;
87 json2yaml "$valuePath" "$out"
91 valueType = nullOr (oneOf [
100 description = "YAML value";
106 # the ini formats share a lot of code
109 singleIniAtom = nullOr (oneOf [ bool int float str ]) // {
110 description = "INI atom (null, bool, int, float or string)";
112 iniAtom = { listsAsDuplicateKeys, listToValue }:
113 if listsAsDuplicateKeys then
114 coercedTo singleIniAtom lib.singleton (listOf singleIniAtom) // {
115 description = singleIniAtom.description + " or a list of them for duplicate keys";
117 else if listToValue != null then
118 coercedTo singleIniAtom lib.singleton (nonEmptyListOf singleIniAtom) // {
119 description = singleIniAtom.description + " or a non-empty list of them";
123 iniSection = { listsAsDuplicateKeys, listToValue }@args:
124 attrsOf (iniAtom args) // {
125 description = "section of an INI file (attrs of " + (iniAtom args).description + ")";
128 maybeToList = listToValue: if listToValue != null then lib.mapAttrs (key: val: if lib.isList val then listToValue val else val) else lib.id;
131 # Represents lists as duplicate keys
132 listsAsDuplicateKeys ? false,
133 # Alternative to listsAsDuplicateKeys, converts list to non-list
134 # listToValue :: [IniAtom] -> IniAtom
138 assert listsAsDuplicateKeys -> listToValue == null;
141 type = lib.types.attrsOf (iniSection { listsAsDuplicateKeys = listsAsDuplicateKeys; listToValue = listToValue; });
143 generate = name: value:
146 (lib.mapAttrs (_: maybeToList listToValue))
147 (lib.generators.toINI (removeAttrs args ["listToValue"]))
148 (pkgs.writeText name)
152 iniWithGlobalSection = {
153 # Represents lists as duplicate keys
154 listsAsDuplicateKeys ? false,
155 # Alternative to listsAsDuplicateKeys, converts list to non-list
156 # listToValue :: [IniAtom] -> IniAtom
160 assert listsAsDuplicateKeys -> listToValue == null;
162 type = lib.types.submodule {
164 sections = lib.mkOption rec {
165 type = lib.types.attrsOf (iniSection { listsAsDuplicateKeys = listsAsDuplicateKeys; listToValue = listToValue; });
167 description = type.description;
169 globalSection = lib.mkOption rec {
170 type = iniSection { listsAsDuplicateKeys = listsAsDuplicateKeys; listToValue = listToValue; };
172 description = "global " + type.description;
176 generate = name: { sections ? {}, globalSection ? {}, ... }:
177 pkgs.writeText name (lib.generators.toINIWithGlobalSection (removeAttrs args ["listToValue"])
179 globalSection = maybeToList listToValue globalSection;
180 sections = lib.mapAttrs (_: maybeToList listToValue) sections;
184 gitIni = { listsAsDuplicateKeys ? false, ... }@args: {
187 listsAsDuplicateKeys = listsAsDuplicateKeys;
190 in attrsOf (attrsOf (either atom (attrsOf atom)));
192 generate = name: value: pkgs.writeText name (lib.generators.toGitINI value);
195 }) ini iniWithGlobalSection gitIni;
197 # As defined by systemd.syntax(7)
199 # null does not set any value, which allows for RFC42 modules to specify
200 # optional config options.
202 mkValueString = lib.generators.mkValueStringDefault {};
204 if v == null then "# ${k} is unset"
205 else "${k} = ${mkValueString v}";
207 listsAsDuplicateKeys = true;
212 # Represents lists as duplicate keys
213 listsAsDuplicateKeys ? false,
214 # Alternative to listsAsDuplicateKeys, converts list to non-list
215 # listToValue :: [Atom] -> Atom
219 assert listsAsDuplicateKeys -> listToValue == null;
224 singleAtom = nullOr (oneOf [
230 description = "atom (null, bool, int, float or string)";
234 if listsAsDuplicateKeys then
235 coercedTo singleAtom lib.singleton (listOf singleAtom) // {
236 description = singleAtom.description + " or a list of them for duplicate keys";
238 else if listToValue != null then
239 coercedTo singleAtom lib.singleton (nonEmptyListOf singleAtom) // {
240 description = singleAtom.description + " or a non-empty list of them";
247 generate = name: value:
250 if listToValue != null
252 lib.mapAttrs (key: val:
253 if lib.isList val then listToValue val else val
256 in pkgs.writeText name (lib.generators.toKeyValue (removeAttrs args ["listToValue"]) transformedValue);
260 toml = {}: json {} // {
271 description = "TOML value";
275 generate = name: value: pkgs.callPackage ({ runCommand, remarshal }: runCommand name {
276 nativeBuildInputs = [ remarshal ];
277 value = builtins.toJSON value;
278 passAsFile = [ "value" ];
279 preferLocalBuild = true;
281 json2toml "$valuePath" "$out"
286 /* For configurations of Elixir project, like config.exs or runtime.exs
288 Most Elixir project are configured using the [Config] Elixir DSL
290 Since Elixir has more types than Nix, we need a way to map Nix types to
291 more than 1 Elixir type. To that end, this format provides its own library,
292 and its own set of types.
294 To be more detailed, a Nix attribute set could correspond in Elixir to a
295 [Keyword list] (the more common type), or it could correspond to a [Map].
297 A Nix string could correspond in Elixir to a [String] (also called
298 "binary"), an [Atom], or a list of chars (usually discouraged).
300 A Nix array could correspond in Elixir to a [List] or a [Tuple].
302 Some more types exists, like records, regexes, but since they are less used,
303 we can leave the `mkRaw` function as an escape hatch.
305 For more information on how to use this format in modules, please refer to
306 the Elixir section of the Nixos documentation.
308 TODO: special Elixir values doesn't show up nicely in the documentation
310 [Config]: <https://hexdocs.pm/elixir/Config.html>
311 [Keyword list]: <https://hexdocs.pm/elixir/Keyword.html>
312 [Map]: <https://hexdocs.pm/elixir/Map.html>
313 [String]: <https://hexdocs.pm/elixir/String.html>
314 [Atom]: <https://hexdocs.pm/elixir/Atom.html>
315 [List]: <https://hexdocs.pm/elixir/List.html>
316 [Tuple]: <https://hexdocs.pm/elixir/Tuple.html>
318 elixirConf = { elixir ? pkgs.elixir }:
321 if value == null then "nil" else
322 if value == true then "true" else
323 if value == false then "false" else
324 if lib.isInt value || lib.isFloat value then toString value else
325 if lib.isString value then string value else
326 if lib.isAttrs value then attrs value else
327 if lib.isList value then list value else
328 abort "formats.elixirConf: should never happen (value = ${value})";
330 escapeElixir = lib.escape [ "\\" "#" "\"" ];
331 string = value: "\"${escapeElixir value}\"";
334 if set ? _elixirType then specialType set
337 toKeyword = name: value: "${name}: ${toElixir value}";
338 keywordList = lib.concatStringsSep ", " (lib.mapAttrsToList toKeyword set);
340 "[" + keywordList + "]";
342 listContent = values: lib.concatStringsSep ", " (map toElixir values);
344 list = values: "[" + (listContent values) + "]";
346 specialType = { value, _elixirType }:
347 if _elixirType == "raw" then value else
348 if _elixirType == "atom" then value else
349 if _elixirType == "map" then elixirMap value else
350 if _elixirType == "tuple" then tuple value else
351 abort "formats.elixirConf: should never happen (_elixirType = ${_elixirType})";
355 toEntry = name: value: "${toElixir name} => ${toElixir value}";
356 entries = lib.concatStringsSep ", " (lib.mapAttrsToList toEntry set);
360 tuple = values: "{${listContent values}}";
364 keyConfig = rootKey: key: value:
365 "config ${rootKey}, ${key}, ${toElixir value}";
366 keyConfigs = rootKey: values: lib.mapAttrsToList (keyConfig rootKey) values;
367 rootConfigs = lib.flatten (lib.mapAttrsToList keyConfigs values);
372 ${lib.concatStringsSep "\n" rootConfigs}
386 description = "Elixir value";
389 attrsOf (attrsOf (valueType));
402 /* Fetch an environment variable at runtime, with optional fallback
404 mkGetEnv = { envVariable, fallback ? null }:
405 mkRaw "System.get_env(${toElixir envVariable}, ${toElixir fallback})";
407 /* Make an Elixir atom.
409 Note: lowercase atoms still need to be prefixed by ':'
413 _elixirType = "atom";
416 /* Make an Elixir tuple out of a list.
420 _elixirType = "tuple";
423 /* Make an Elixir map out of an attribute set.
430 /* Contains Elixir types. Every type it exports can also be replaced
431 by raw Elixir code (i.e. every type is `either type rawElixir`).
433 It also reexports standard types, wrapping them so that they can
437 isElixirType = type: x: (x._elixirType or "") == type;
439 rawElixir = mkOptionType {
441 description = "raw elixir";
442 check = isElixirType "raw";
445 elixirOr = other: either other rawElixir;
448 inherit rawElixir elixirOr;
450 atom = elixirOr (mkOptionType {
452 description = "elixir atom";
453 check = isElixirType "atom";
456 tuple = elixirOr (mkOptionType {
457 name = "elixirTuple";
458 description = "elixir tuple";
459 check = isElixirType "tuple";
462 map = elixirOr (mkOptionType {
464 description = "elixir map";
465 check = isElixirType "map";
467 # Wrap standard types, since anything in the Elixir configuration
469 } // lib.mapAttrs (_name: type: elixirOr type) lib.types;
472 generate = name: value: pkgs.runCommand name
474 value = toConf value;
475 passAsFile = [ "value" ];
476 nativeBuildInputs = [ elixir ];
477 preferLocalBuild = true;
479 cp "$valuePath" "$out"
484 # Outputs a succession of Python variable assignments
485 # Useful for many Django-based services
488 valueType = nullOr(oneOf [
497 description = "Python value";
499 in attrsOf valueType;
500 generate = name: value: pkgs.callPackage ({ runCommand, python3, black }: runCommand name {
501 nativeBuildInputs = [ python3 black ];
502 value = builtins.toJSON value;
507 with open(os.environ["valuePath"], "r") as f:
508 for key, value in json.load(f).items():
509 print(f"{key} = {repr(value)}")
511 passAsFile = [ "value" "pythonGen" ];
512 preferLocalBuild = true;
515 python3 "$pythonGenPath" > $out