This does break the API of being able to import any lib file and get its libs, however I'm not sure people did this. I made this while exploring being able to swap out docFn with a stub in #2305, to avoid functor performance problems. I don't know if that is going to move forward (or if it is a problem or not,) but after doing all this work figured I'd put it up anyway :) Two notable advantages to this approach: 1. when a lib inherits another lib's functions, it doesn't automatically get put in to the scope of lib 2. when a lib implements a new obscure functions, it doesn't automatically get put in to the scope of lib Using the test script (later in this commit) I got the following diff on the API: + diff master fixed-lib 11764a11765,11766 > .types.defaultFunctor > .types.defaultTypeMerge 11774a11777,11778 > .types.isOptionType > .types.isType 11781a11786 > .types.mkOptionType 11788a11794 > .types.setType 11795a11802 > .types.types This means that this commit _adds_ to the API, however I can't find a way to fix these last remaining discrepancies. At least none are _removed_. Test script (run with nix-repl in the PATH): #!/bin/sh set -eux repl() { suff=${1:-} echo "(import ./lib)$suff" \ | nix-repl 2>&1 } attrs_to_check() { repl "${1:-}" \ | tr ';' $'\n' \ | grep "\.\.\." \ | cut -d' ' -f2 \ | sed -e "s/^/${1:-}./" \ | sort } summ() { repl "${1:-}" \ | tr ' ' $'\n' \ | sort \ | uniq } deep_summ() { suff="${1:-}" depth="${2:-4}" depth=$((depth - 1)) summ "$suff" for attr in $(attrs_to_check "$suff" | grep -v "types.types"); do if [ $depth -eq 0 ]; then summ "$attr" | sed -e "s/^/$attr./" else deep_summ "$attr" "$depth" | sed -e "s/^/$attr./" fi done } ( cd nixpkgs #git add . #git commit -m "Auto-commit, sorry" || true git checkout fixed-lib deep_summ > ../fixed-lib git checkout master deep_summ > ../master ) if diff master fixed-lib; then echo "SHALLOW MATCH!" fi ( cd nixpkgs git checkout fixed-lib repl .types )
132 lines
4.1 KiB
Nix
132 lines
4.1 KiB
Nix
/* Functions that generate widespread file
|
|
* formats from nix data structures.
|
|
*
|
|
* They all follow a similar interface:
|
|
* generator { config-attrs } data
|
|
*
|
|
* Tests can be found in ./tests.nix
|
|
* Documentation in the manual, #sec-generators
|
|
*/
|
|
{ lib }:
|
|
with (lib).trivial;
|
|
let
|
|
libStr = lib.strings;
|
|
libAttr = lib.attrsets;
|
|
|
|
flipMapAttrs = flip libAttr.mapAttrs;
|
|
in
|
|
|
|
rec {
|
|
|
|
/* Generate a line of key k and value v, separated by
|
|
* character sep. If sep appears in k, it is escaped.
|
|
* Helper for synaxes with different separators.
|
|
*
|
|
* mkKeyValueDefault ":" "f:oo" "bar"
|
|
* > "f\:oo:bar"
|
|
*/
|
|
mkKeyValueDefault = sep: k: v:
|
|
"${libStr.escape [sep] k}${sep}${toString v}";
|
|
|
|
|
|
/* Generate a key-value-style config file from an attrset.
|
|
*
|
|
* mkKeyValue is the same as in toINI.
|
|
*/
|
|
toKeyValue = {
|
|
mkKeyValue ? mkKeyValueDefault "="
|
|
}: attrs:
|
|
let mkLine = k: v: mkKeyValue k v + "\n";
|
|
in libStr.concatStrings (libAttr.mapAttrsToList mkLine attrs);
|
|
|
|
|
|
/* Generate an INI-style config file from an
|
|
* attrset of sections to an attrset of key-value pairs.
|
|
*
|
|
* generators.toINI {} {
|
|
* foo = { hi = "${pkgs.hello}"; ciao = "bar"; };
|
|
* baz = { "also, integers" = 42; };
|
|
* }
|
|
*
|
|
*> [baz]
|
|
*> also, integers=42
|
|
*>
|
|
*> [foo]
|
|
*> ciao=bar
|
|
*> hi=/nix/store/y93qql1p5ggfnaqjjqhxcw0vqw95rlz0-hello-2.10
|
|
*
|
|
* The mk* configuration attributes can generically change
|
|
* the way sections and key-value strings are generated.
|
|
*
|
|
* For more examples see the test cases in ./tests.nix.
|
|
*/
|
|
toINI = {
|
|
# apply transformations (e.g. escapes) to section names
|
|
mkSectionName ? (name: libStr.escape [ "[" "]" ] name),
|
|
# format a setting line from key and value
|
|
mkKeyValue ? mkKeyValueDefault "="
|
|
}: attrsOfAttrs:
|
|
let
|
|
# map function to string for each key val
|
|
mapAttrsToStringsSep = sep: mapFn: attrs:
|
|
libStr.concatStringsSep sep
|
|
(libAttr.mapAttrsToList mapFn attrs);
|
|
mkSection = sectName: sectValues: ''
|
|
[${mkSectionName sectName}]
|
|
'' + toKeyValue { inherit mkKeyValue; } sectValues;
|
|
in
|
|
# map input to ini sections
|
|
mapAttrsToStringsSep "\n" mkSection attrsOfAttrs;
|
|
|
|
|
|
/* Generates JSON from an arbitrary (non-function) value.
|
|
* For more information see the documentation of the builtin.
|
|
*/
|
|
toJSON = {}: builtins.toJSON;
|
|
|
|
|
|
/* YAML has been a strict superset of JSON since 1.2, so we
|
|
* use toJSON. Before it only had a few differences referring
|
|
* to implicit typing rules, so it should work with older
|
|
* parsers as well.
|
|
*/
|
|
toYAML = {}@args: toJSON args;
|
|
|
|
/* Pretty print a value, akin to `builtins.trace`.
|
|
* Should probably be a builtin as well.
|
|
*/
|
|
toPretty = {
|
|
/* If this option is true, attrsets like { __pretty = fn; val = …; }
|
|
will use fn to convert val to a pretty printed representation.
|
|
(This means fn is type Val -> String.) */
|
|
allowPrettyValues ? false
|
|
}@args: v: with builtins;
|
|
if isInt v then toString v
|
|
else if isBool v then (if v == true then "true" else "false")
|
|
else if isString v then "\"" + v + "\""
|
|
else if null == v then "null"
|
|
else if isFunction v then
|
|
let fna = functionArgs v;
|
|
showFnas = concatStringsSep "," (libAttr.mapAttrsToList
|
|
(name: hasDefVal: if hasDefVal then "(${name})" else name)
|
|
fna);
|
|
in if fna == {} then "<λ>"
|
|
else "<λ:{${showFnas}}>"
|
|
else if isList v then "[ "
|
|
+ libStr.concatMapStringsSep " " (toPretty args) v
|
|
+ " ]"
|
|
else if isAttrs v then
|
|
# apply pretty values if allowed
|
|
if attrNames v == [ "__pretty" "val" ] && allowPrettyValues
|
|
then v.__pretty v.val
|
|
# TODO: there is probably a better representation?
|
|
else if v ? type && v.type == "derivation" then "<δ>"
|
|
else "{ "
|
|
+ libStr.concatStringsSep " " (libAttr.mapAttrsToList
|
|
(name: value:
|
|
"${toPretty args name} = ${toPretty args value};") v)
|
|
+ " }"
|
|
else "toPretty: should never happen (v = ${v})";
|
|
|
|
}
|