1/* Functions that generate widespread file
2 * formats from nix data structures.
3 *
4 * They all follow a similar interface:
5 * generator { config-attrs } data
6 *
7 * Tests can be found in ./tests.nix
8 * Documentation in the manual, #sec-generators
9 */
10{ lib }:
11with (lib).trivial;
12let
13 libStr = lib.strings;
14 libAttr = lib.attrsets;
15
16 flipMapAttrs = flip libAttr.mapAttrs;
17
18 inherit (lib) isFunction;
19in
20
21rec {
22
23 /* Generate a line of key k and value v, separated by
24 * character sep. If sep appears in k, it is escaped.
25 * Helper for synaxes with different separators.
26 *
27 * mkValueString specifies how values should be formatted.
28 *
29 * mkKeyValueDefault {} ":" "f:oo" "bar"
30 * > "f\:oo:bar"
31 */
32 mkKeyValueDefault = {
33 mkValueString ? toString
34 }: sep: k: v:
35 "${libStr.escape [sep] k}${sep}${mkValueString v}";
36
37
38 /* Generate a key-value-style config file from an attrset.
39 *
40 * mkKeyValue is the same as in toINI.
41 */
42 toKeyValue = {
43 mkKeyValue ? mkKeyValueDefault {} "="
44 }: attrs:
45 let mkLine = k: v: mkKeyValue k v + "\n";
46 in libStr.concatStrings (libAttr.mapAttrsToList mkLine attrs);
47
48
49 /* Generate an INI-style config file from an
50 * attrset of sections to an attrset of key-value pairs.
51 *
52 * generators.toINI {} {
53 * foo = { hi = "${pkgs.hello}"; ciao = "bar"; };
54 * baz = { "also, integers" = 42; };
55 * }
56 *
57 *> [baz]
58 *> also, integers=42
59 *>
60 *> [foo]
61 *> ciao=bar
62 *> hi=/nix/store/y93qql1p5ggfnaqjjqhxcw0vqw95rlz0-hello-2.10
63 *
64 * The mk* configuration attributes can generically change
65 * the way sections and key-value strings are generated.
66 *
67 * For more examples see the test cases in ./tests.nix.
68 */
69 toINI = {
70 # apply transformations (e.g. escapes) to section names
71 mkSectionName ? (name: libStr.escape [ "[" "]" ] name),
72 # format a setting line from key and value
73 mkKeyValue ? mkKeyValueDefault {} "="
74 }: attrsOfAttrs:
75 let
76 # map function to string for each key val
77 mapAttrsToStringsSep = sep: mapFn: attrs:
78 libStr.concatStringsSep sep
79 (libAttr.mapAttrsToList mapFn attrs);
80 mkSection = sectName: sectValues: ''
81 [${mkSectionName sectName}]
82 '' + toKeyValue { inherit mkKeyValue; } sectValues;
83 in
84 # map input to ini sections
85 mapAttrsToStringsSep "\n" mkSection attrsOfAttrs;
86
87
88 /* Generates JSON from an arbitrary (non-function) value.
89 * For more information see the documentation of the builtin.
90 */
91 toJSON = {}: builtins.toJSON;
92
93
94 /* YAML has been a strict superset of JSON since 1.2, so we
95 * use toJSON. Before it only had a few differences referring
96 * to implicit typing rules, so it should work with older
97 * parsers as well.
98 */
99 toYAML = {}@args: toJSON args;
100
101 /* Pretty print a value, akin to `builtins.trace`.
102 * Should probably be a builtin as well.
103 */
104 toPretty = {
105 /* If this option is true, attrsets like { __pretty = fn; val = …; }
106 will use fn to convert val to a pretty printed representation.
107 (This means fn is type Val -> String.) */
108 allowPrettyValues ? false
109 }@args: v: with builtins;
110 if isInt v then toString v
111 else if isBool v then (if v == true then "true" else "false")
112 else if isString v then "\"" + v + "\""
113 else if null == v then "null"
114 else if isFunction v then
115 let fna = lib.functionArgs v;
116 showFnas = concatStringsSep "," (libAttr.mapAttrsToList
117 (name: hasDefVal: if hasDefVal then "(${name})" else name)
118 fna);
119 in if fna == {} then "<λ>"
120 else "<λ:{${showFnas}}>"
121 else if isList v then "[ "
122 + libStr.concatMapStringsSep " " (toPretty args) v
123 + " ]"
124 else if isAttrs v then
125 # apply pretty values if allowed
126 if attrNames v == [ "__pretty" "val" ] && allowPrettyValues
127 then v.__pretty v.val
128 # TODO: there is probably a better representation?
129 else if v ? type && v.type == "derivation" then "<δ>"
130 else "{ "
131 + libStr.concatStringsSep " " (libAttr.mapAttrsToList
132 (name: value:
133 "${toPretty args name} = ${toPretty args value};") v)
134 + " }"
135 else abort "toPretty: should never happen (v = ${v})";
136
137}