at 18.03-beta 4.4 kB view raw
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}