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;
46 type = with lib.types; let
47 valueType = nullOr (oneOf [
56 description = "JSON value";
60 generate = name: value: pkgs.callPackage ({ runCommand, jq }: runCommand name {
61 nativeBuildInputs = [ jq ];
62 value = builtins.toJSON value;
63 passAsFile = [ "value" ];
65 jq . "$valuePath"> $out
72 generate = name: value: pkgs.callPackage ({ runCommand, remarshal }: runCommand name {
73 nativeBuildInputs = [ remarshal ];
74 value = builtins.toJSON value;
75 passAsFile = [ "value" ];
77 json2yaml "$valuePath" "$out"
80 type = with lib.types; let
81 valueType = nullOr (oneOf [
90 description = "YAML value";
97 # Represents lists as duplicate keys
98 listsAsDuplicateKeys ? false,
99 # Alternative to listsAsDuplicateKeys, converts list to non-list
100 # listToValue :: [IniAtom] -> IniAtom
104 assert !listsAsDuplicateKeys || listToValue == null;
107 type = with lib.types; let
109 singleIniAtom = nullOr (oneOf [
115 description = "INI atom (null, bool, int, float or string)";
119 if listsAsDuplicateKeys then
120 coercedTo singleIniAtom lib.singleton (listOf singleIniAtom) // {
121 description = singleIniAtom.description + " or a list of them for duplicate keys";
123 else if listToValue != null then
124 coercedTo singleIniAtom lib.singleton (nonEmptyListOf singleIniAtom) // {
125 description = singleIniAtom.description + " or a non-empty list of them";
130 in attrsOf (attrsOf iniAtom);
132 generate = name: value:
135 if listToValue != null
137 lib.mapAttrs (section: lib.mapAttrs (key: val:
138 if lib.isList val then listToValue val else val
141 in pkgs.writeText name (lib.generators.toINI (removeAttrs args ["listToValue"]) transformedValue);
146 # Represents lists as duplicate keys
147 listsAsDuplicateKeys ? false,
148 # Alternative to listsAsDuplicateKeys, converts list to non-list
149 # listToValue :: [Atom] -> Atom
153 assert !listsAsDuplicateKeys || listToValue == null;
156 type = with lib.types; let
158 singleAtom = nullOr (oneOf [
164 description = "atom (null, bool, int, float or string)";
168 if listsAsDuplicateKeys then
169 coercedTo singleAtom lib.singleton (listOf singleAtom) // {
170 description = singleAtom.description + " or a list of them for duplicate keys";
172 else if listToValue != null then
173 coercedTo singleAtom lib.singleton (nonEmptyListOf singleAtom) // {
174 description = singleAtom.description + " or a non-empty list of them";
181 generate = name: value:
184 if listToValue != null
186 lib.mapAttrs (key: val:
187 if lib.isList val then listToValue val else val
190 in pkgs.writeText name (lib.generators.toKeyValue (removeAttrs args ["listToValue"]) transformedValue);
194 gitIni = { listsAsDuplicateKeys ? false, ... }@args: {
196 type = with lib.types; let
198 iniAtom = (ini args).type/*attrsOf*/.functor.wrapped/*attrsOf*/.functor.wrapped;
200 in attrsOf (attrsOf (either iniAtom (attrsOf iniAtom)));
202 generate = name: value: pkgs.writeText name (lib.generators.toGitINI value);
205 toml = {}: json {} // {
206 type = with lib.types; let
216 description = "TOML value";
220 generate = name: value: pkgs.callPackage ({ runCommand, remarshal }: runCommand name {
221 nativeBuildInputs = [ remarshal ];
222 value = builtins.toJSON value;
223 passAsFile = [ "value" ];
225 json2toml "$valuePath" "$out"
230 /* For configurations of Elixir project, like config.exs or runtime.exs
232 Most Elixir project are configured using the [Config] Elixir DSL
234 Since Elixir has more types than Nix, we need a way to map Nix types to
235 more than 1 Elixir type. To that end, this format provides its own library,
236 and its own set of types.
238 To be more detailed, a Nix attribute set could correspond in Elixir to a
239 [Keyword list] (the more common type), or it could correspond to a [Map].
241 A Nix string could correspond in Elixir to a [String] (also called
242 "binary"), an [Atom], or a list of chars (usually discouraged).
244 A Nix array could correspond in Elixir to a [List] or a [Tuple].
246 Some more types exists, like records, regexes, but since they are less used,
247 we can leave the `mkRaw` function as an escape hatch.
249 For more information on how to use this format in modules, please refer to
250 the Elixir section of the Nixos documentation.
252 TODO: special Elixir values doesn't show up nicely in the documentation
254 [Config]: <https://hexdocs.pm/elixir/Config.html>
255 [Keyword list]: <https://hexdocs.pm/elixir/Keyword.html>
256 [Map]: <https://hexdocs.pm/elixir/Map.html>
257 [String]: <https://hexdocs.pm/elixir/String.html>
258 [Atom]: <https://hexdocs.pm/elixir/Atom.html>
259 [List]: <https://hexdocs.pm/elixir/List.html>
260 [Tuple]: <https://hexdocs.pm/elixir/Tuple.html>
262 elixirConf = { elixir ? pkgs.elixir }:
264 toElixir = value: with builtins;
265 if value == null then "nil" else
266 if value == true then "true" else
267 if value == false then "false" else
268 if isInt value || isFloat value then toString value else
269 if isString value then string value else
270 if isAttrs value then attrs value else
271 if isList value then list value else
272 abort "formats.elixirConf: should never happen (value = ${value})";
274 escapeElixir = escape [ "\\" "#" "\"" ];
275 string = value: "\"${escapeElixir value}\"";
278 if set ? _elixirType then specialType set
281 toKeyword = name: value: "${name}: ${toElixir value}";
282 keywordList = concatStringsSep ", " (mapAttrsToList toKeyword set);
284 "[" + keywordList + "]";
286 listContent = values: concatStringsSep ", " (map toElixir values);
288 list = values: "[" + (listContent values) + "]";
290 specialType = { value, _elixirType }:
291 if _elixirType == "raw" then value else
292 if _elixirType == "atom" then value else
293 if _elixirType == "map" then elixirMap value else
294 if _elixirType == "tuple" then tuple value else
295 abort "formats.elixirConf: should never happen (_elixirType = ${_elixirType})";
299 toEntry = name: value: "${toElixir name} => ${toElixir value}";
300 entries = concatStringsSep ", " (mapAttrsToList toEntry set);
304 tuple = values: "{${listContent values}}";
308 keyConfig = rootKey: key: value:
309 "config ${rootKey}, ${key}, ${toElixir value}";
310 keyConfigs = rootKey: values: mapAttrsToList (keyConfig rootKey) values;
311 rootConfigs = flatten (mapAttrsToList keyConfigs values);
316 ${concatStringsSep "\n" rootConfigs}
320 type = with lib.types; let
330 description = "Elixir value";
333 attrsOf (attrsOf (valueType));
346 /* Fetch an environment variable at runtime, with optional fallback
348 mkGetEnv = { envVariable, fallback ? null }:
349 mkRaw "System.get_env(${toElixir envVariable}, ${toElixir fallback})";
351 /* Make an Elixir atom.
353 Note: lowercase atoms still need to be prefixed by ':'
357 _elixirType = "atom";
360 /* Make an Elixir tuple out of a list.
364 _elixirType = "tuple";
367 /* Make an Elixir map out of an attribute set.
374 /* Contains Elixir types. Every type it exports can also be replaced
375 by raw Elixir code (i.e. every type is `either type rawElixir`).
377 It also reexports standard types, wrapping them so that they can
380 types = with lib.types; let
381 isElixirType = type: x: (x._elixirType or "") == type;
383 rawElixir = mkOptionType {
385 description = "raw elixir";
386 check = isElixirType "raw";
389 elixirOr = other: either other rawElixir;
392 inherit rawElixir elixirOr;
394 atom = elixirOr (mkOptionType {
396 description = "elixir atom";
397 check = isElixirType "atom";
400 tuple = elixirOr (mkOptionType {
401 name = "elixirTuple";
402 description = "elixir tuple";
403 check = isElixirType "tuple";
406 map = elixirOr (mkOptionType {
408 description = "elixir map";
409 check = isElixirType "map";
411 # Wrap standard types, since anything in the Elixir configuration
413 } // lib.mapAttrs (_name: type: elixirOr type) lib.types;
416 generate = name: value: pkgs.runCommand name
418 value = toConf value;
419 passAsFile = [ "value" ];
420 nativeBuildInputs = [ elixir ];
422 cp "$valuePath" "$out"
427 # Outputs a succession of Python variable assignments
428 # Useful for many Django-based services
430 type = with lib.types; let
431 valueType = nullOr(oneOf [
440 description = "Python value";
442 in attrsOf valueType;
443 generate = name: value: pkgs.callPackage ({ runCommand, python3, black }: runCommand name {
444 nativeBuildInputs = [ python3 black ];
445 value = builtins.toJSON value;
450 with open(os.environ["valuePath"], "r") as f:
451 for key, value in json.load(f).items():
452 print(f"{key} = {repr(value)}")
454 passAsFile = [ "value" "pythonGen" ];
457 python3 "$pythonGenPath" > $out