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