chromium,chromedriver: 129.0.6668.91 -> 129.0.6668.100
[NixPkgs.git] / pkgs / pkgs-lib / formats.nix
blobc99144d935190323d8225dd3d6b76b17285c54f1
1 { lib, pkgs }:
2 rec {
4   /*
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
15       type = ...;
17       # Utility functions for convenience, or special interactions with the
18       # format (optional)
19       lib = {
20         exampleFunction = ...
21         # Types specific to the format (optional)
22         types = { ... };
23         ...
24       };
26       # generate :: Name -> Value -> Path
27       # A function for generating a file with a value of such a type
28       generate = ...;
30     });
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.
36   */
39   inherit (import ./formats/java-properties/default.nix { inherit lib pkgs; })
40     javaProperties;
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;
52   json = {}: {
54     type = let
55       valueType = nullOr (oneOf [
56         bool
57         int
58         float
59         str
60         path
61         (attrsOf valueType)
62         (listOf valueType)
63       ]) // {
64         description = "JSON value";
65       };
66     in valueType;
68     generate = name: value: pkgs.callPackage ({ runCommand, jq }: runCommand name {
69       nativeBuildInputs = [ jq ];
70       value = builtins.toJSON value;
71       passAsFile = [ "value" ];
72       preferLocalBuild = true;
73     } ''
74       jq . "$valuePath"> $out
75     '') {};
77   };
79   yaml = {}: {
81     generate = name: value: pkgs.callPackage ({ runCommand, remarshal }: runCommand name {
82       nativeBuildInputs = [ remarshal ];
83       value = builtins.toJSON value;
84       passAsFile = [ "value" ];
85       preferLocalBuild = true;
86     } ''
87       json2yaml "$valuePath" "$out"
88     '') {};
90     type = let
91       valueType = nullOr (oneOf [
92         bool
93         int
94         float
95         str
96         path
97         (attrsOf valueType)
98         (listOf valueType)
99       ]) // {
100         description = "YAML value";
101       };
102     in valueType;
104   };
106   # the ini formats share a lot of code
107   inherit (
108     let
109       singleIniAtom = nullOr (oneOf [ bool int float str ]) // {
110         description = "INI atom (null, bool, int, float or string)";
111       };
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";
116           }
117         else if listToValue != null then
118           coercedTo singleIniAtom lib.singleton (nonEmptyListOf singleIniAtom) // {
119             description = singleIniAtom.description + " or a non-empty list of them";
120           }
121         else
122           singleIniAtom;
123       iniSection = { listsAsDuplicateKeys, listToValue }@args:
124         attrsOf (iniAtom args) // {
125           description = "section of an INI file (attrs of " + (iniAtom args).description + ")";
126         };
128       maybeToList = listToValue: if listToValue != null then lib.mapAttrs (key: val: if lib.isList val then listToValue val else val) else lib.id;
129     in {
130       ini = {
131         # Represents lists as duplicate keys
132         listsAsDuplicateKeys ? false,
133         # Alternative to listsAsDuplicateKeys, converts list to non-list
134         # listToValue :: [IniAtom] -> IniAtom
135         listToValue ? null,
136         ...
137         }@args:
138         assert listsAsDuplicateKeys -> listToValue == null;
139         {
141         type = lib.types.attrsOf (iniSection { listsAsDuplicateKeys = listsAsDuplicateKeys; listToValue = listToValue; });
143         generate = name: value:
144           lib.pipe value
145           [
146             (lib.mapAttrs (_: maybeToList listToValue))
147             (lib.generators.toINI (removeAttrs args ["listToValue"]))
148             (pkgs.writeText name)
149           ];
150       };
152       iniWithGlobalSection = {
153         # Represents lists as duplicate keys
154         listsAsDuplicateKeys ? false,
155         # Alternative to listsAsDuplicateKeys, converts list to non-list
156         # listToValue :: [IniAtom] -> IniAtom
157         listToValue ? null,
158         ...
159         }@args:
160         assert listsAsDuplicateKeys -> listToValue == null;
161         {
162           type = lib.types.submodule {
163             options = {
164               sections = lib.mkOption rec {
165                 type = lib.types.attrsOf (iniSection { listsAsDuplicateKeys = listsAsDuplicateKeys; listToValue = listToValue; });
166                 default = {};
167                 description = type.description;
168               };
169               globalSection = lib.mkOption rec {
170                 type = iniSection { listsAsDuplicateKeys = listsAsDuplicateKeys; listToValue = listToValue; };
171                 default = {};
172                 description = "global " + type.description;
173               };
174             };
175           };
176           generate = name: { sections ? {}, globalSection ? {}, ... }:
177             pkgs.writeText name (lib.generators.toINIWithGlobalSection (removeAttrs args ["listToValue"])
178             {
179               globalSection = maybeToList listToValue globalSection;
180               sections = lib.mapAttrs (_: maybeToList listToValue) sections;
181             });
182         };
184       gitIni = { listsAsDuplicateKeys ? false, ... }@args: {
185         type = let
186           atom = iniAtom {
187             listsAsDuplicateKeys = listsAsDuplicateKeys;
188             listToValue = null;
189           };
190         in attrsOf (attrsOf (either atom (attrsOf atom)));
192         generate = name: value: pkgs.writeText name (lib.generators.toGitINI value);
193       };
195     }) ini iniWithGlobalSection gitIni;
197   # As defined by systemd.syntax(7)
198   #
199   # null does not set any value, which allows for RFC42 modules to specify
200   # optional config options.
201   systemd = let
202     mkValueString = lib.generators.mkValueStringDefault {};
203     mkKeyValue = k: v:
204       if v == null then "# ${k} is unset"
205       else "${k} = ${mkValueString v}";
206   in ini {
207     listsAsDuplicateKeys = true;
208     inherit mkKeyValue;
209   };
211   keyValue = {
212     # Represents lists as duplicate keys
213     listsAsDuplicateKeys ? false,
214     # Alternative to listsAsDuplicateKeys, converts list to non-list
215     # listToValue :: [Atom] -> Atom
216     listToValue ? null,
217     ...
218     }@args:
219     assert listsAsDuplicateKeys -> listToValue == null;
220     {
222     type = let
224       singleAtom = nullOr (oneOf [
225         bool
226         int
227         float
228         str
229       ]) // {
230         description = "atom (null, bool, int, float or string)";
231       };
233       atom =
234         if listsAsDuplicateKeys then
235           coercedTo singleAtom lib.singleton (listOf singleAtom) // {
236             description = singleAtom.description + " or a list of them for duplicate keys";
237           }
238         else if listToValue != null then
239           coercedTo singleAtom lib.singleton (nonEmptyListOf singleAtom) // {
240             description = singleAtom.description + " or a non-empty list of them";
241           }
242         else
243           singleAtom;
245     in attrsOf atom;
247     generate = name: value:
248       let
249         transformedValue =
250           if listToValue != null
251           then
252             lib.mapAttrs (key: val:
253               if lib.isList val then listToValue val else val
254             ) value
255           else value;
256       in pkgs.writeText name (lib.generators.toKeyValue (removeAttrs args ["listToValue"]) transformedValue);
258   };
260   toml = {}: json {} // {
261     type = let
262       valueType = oneOf [
263         bool
264         int
265         float
266         str
267         path
268         (attrsOf valueType)
269         (listOf valueType)
270       ] // {
271         description = "TOML value";
272       };
273     in valueType;
275     generate = name: value: pkgs.callPackage ({ runCommand, remarshal }: runCommand name {
276       nativeBuildInputs = [ remarshal ];
277       value = builtins.toJSON value;
278       passAsFile = [ "value" ];
279       preferLocalBuild = true;
280     } ''
281       json2toml "$valuePath" "$out"
282     '') {};
284   };
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>
317   */
318   elixirConf = { elixir ? pkgs.elixir }:
319     let
320       toElixir = value:
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}\"";
333       attrs = set:
334         if set ? _elixirType then specialType set
335         else
336           let
337             toKeyword = name: value: "${name}: ${toElixir value}";
338             keywordList = lib.concatStringsSep ", " (lib.mapAttrsToList toKeyword set);
339           in
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})";
353       elixirMap = set:
354         let
355           toEntry = name: value: "${toElixir name} => ${toElixir value}";
356           entries = lib.concatStringsSep ", " (lib.mapAttrsToList toEntry set);
357         in
358         "%{${entries}}";
360       tuple = values: "{${listContent values}}";
362       toConf = values:
363         let
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);
368         in
369         ''
370           import Config
372           ${lib.concatStringsSep "\n" rootConfigs}
373         '';
374     in
375     {
376       type = let
377         valueType = nullOr
378           (oneOf [
379             bool
380             int
381             float
382             str
383             (attrsOf valueType)
384             (listOf valueType)
385           ]) // {
386           description = "Elixir value";
387         };
388       in
389       attrsOf (attrsOf (valueType));
391       lib =
392         let
393           mkRaw = value: {
394             inherit value;
395             _elixirType = "raw";
396           };
398         in
399         {
400           inherit mkRaw;
402           /* Fetch an environment variable at runtime, with optional fallback
403           */
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 ':'
410           */
411           mkAtom = value: {
412             inherit value;
413             _elixirType = "atom";
414           };
416           /* Make an Elixir tuple out of a list.
417           */
418           mkTuple = value: {
419             inherit value;
420             _elixirType = "tuple";
421           };
423           /* Make an Elixir map out of an attribute set.
424           */
425           mkMap = value: {
426             inherit value;
427             _elixirType = "map";
428           };
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
434              also be raw Elixir.
435           */
436           types = let
437             isElixirType = type: x: (x._elixirType or "") == type;
439             rawElixir = mkOptionType {
440               name = "rawElixir";
441               description = "raw elixir";
442               check = isElixirType "raw";
443             };
445             elixirOr = other: either other rawElixir;
446           in
447           {
448             inherit rawElixir elixirOr;
450             atom = elixirOr (mkOptionType {
451               name = "elixirAtom";
452               description = "elixir atom";
453               check = isElixirType "atom";
454             });
456             tuple = elixirOr (mkOptionType {
457               name = "elixirTuple";
458               description = "elixir tuple";
459               check = isElixirType "tuple";
460             });
462             map = elixirOr (mkOptionType {
463               name = "elixirMap";
464               description = "elixir map";
465               check = isElixirType "map";
466             });
467             # Wrap standard types, since anything in the Elixir configuration
468             # can be raw Elixir
469           } // lib.mapAttrs (_name: type: elixirOr type) lib.types;
470         };
472       generate = name: value: pkgs.runCommand name
473         {
474           value = toConf value;
475           passAsFile = [ "value" ];
476           nativeBuildInputs = [ elixir ];
477           preferLocalBuild = true;
478         } ''
479         cp "$valuePath" "$out"
480         mix format "$out"
481       '';
482     };
484   # Outputs a succession of Python variable assignments
485   # Useful for many Django-based services
486   pythonVars = {}: {
487     type = let
488       valueType = nullOr(oneOf [
489         bool
490         float
491         int
492         path
493         str
494         (attrsOf valueType)
495         (listOf valueType)
496       ]) // {
497         description = "Python value";
498       };
499     in attrsOf valueType;
500     generate = name: value: pkgs.callPackage ({ runCommand, python3, black }: runCommand name {
501       nativeBuildInputs = [ python3 black ];
502       value = builtins.toJSON value;
503       pythonGen = ''
504         import json
505         import os
507         with open(os.environ["valuePath"], "r") as f:
508             for key, value in json.load(f).items():
509                 print(f"{key} = {repr(value)}")
510       '';
511       passAsFile = [ "value" "pythonGen" ];
512       preferLocalBuild = true;
513     } ''
514       cat "$valuePath"
515       python3 "$pythonGenPath" > $out
516       black $out
517     '') {};
518   };