at 17.09-beta 4.2 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 */ 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}