Merge pull request #293901 from philiptaron/refactor-lib/generators.nix
lib: use explicit name imports in `lib/generators.nix`
This commit is contained in:
commit
c9772eaba0
1 changed files with 132 additions and 75 deletions
|
@ -14,15 +14,58 @@
|
|||
* Documentation in the manual, #sec-generators
|
||||
*/
|
||||
{ lib }:
|
||||
with (lib).trivial;
|
||||
|
||||
let
|
||||
libStr = lib.strings;
|
||||
libAttr = lib.attrsets;
|
||||
inherit (lib)
|
||||
addErrorContext
|
||||
assertMsg
|
||||
attrNames
|
||||
concatLists
|
||||
concatMapStringsSep
|
||||
concatStrings
|
||||
concatStringsSep
|
||||
const
|
||||
elem
|
||||
escape
|
||||
filter
|
||||
flatten
|
||||
foldl
|
||||
functionArgs # Note: not the builtin; considers `__functor` in attrsets.
|
||||
gvariant
|
||||
hasInfix
|
||||
head
|
||||
id
|
||||
init
|
||||
isAttrs
|
||||
isBool
|
||||
isDerivation
|
||||
isFloat
|
||||
isFunction # Note: not the builtin; considers `__functor` in attrsets.
|
||||
isInt
|
||||
isList
|
||||
isPath
|
||||
isString
|
||||
last
|
||||
length
|
||||
mapAttrs
|
||||
mapAttrsToList
|
||||
optionals
|
||||
recursiveUpdate
|
||||
replaceStrings
|
||||
reverseList
|
||||
splitString
|
||||
tail
|
||||
toList
|
||||
;
|
||||
|
||||
inherit (lib) isFunction;
|
||||
in
|
||||
|
||||
rec {
|
||||
inherit (lib.strings)
|
||||
escapeNixIdentifier
|
||||
floatToString
|
||||
match
|
||||
split
|
||||
toJSON
|
||||
typeOf
|
||||
;
|
||||
|
||||
## -- HELPER FUNCTIONS & DEFAULTS --
|
||||
|
||||
|
@ -30,13 +73,13 @@ rec {
|
|||
* The builtin `toString` function has some strange defaults,
|
||||
* suitable for bash scripts but not much else.
|
||||
*/
|
||||
mkValueStringDefault = {}: v: with builtins;
|
||||
mkValueStringDefault = {}: v:
|
||||
let err = t: v: abort
|
||||
("generators.mkValueStringDefault: " +
|
||||
"${t} not supported: ${toPretty {} v}");
|
||||
in if isInt v then toString v
|
||||
# convert derivations to store paths
|
||||
else if lib.isDerivation v then toString v
|
||||
else if isDerivation v then toString v
|
||||
# we default to not quoting strings
|
||||
else if isString v then v
|
||||
# isString returns "1", which is not a good default
|
||||
|
@ -53,7 +96,7 @@ rec {
|
|||
# Floats currently can't be converted to precise strings,
|
||||
# condition warning on nix version once this isn't a problem anymore
|
||||
# See https://github.com/NixOS/nix/pull/3480
|
||||
else if isFloat v then libStr.floatToString v
|
||||
else if isFloat v then floatToString v
|
||||
else err "this value is" (toString v);
|
||||
|
||||
|
||||
|
@ -69,7 +112,7 @@ rec {
|
|||
mkKeyValueDefault = {
|
||||
mkValueString ? mkValueStringDefault {}
|
||||
}: sep: k: v:
|
||||
"${libStr.escape [sep] k}${sep}${mkValueString v}";
|
||||
"${escape [sep] k}${sep}${mkValueString v}";
|
||||
|
||||
|
||||
## -- FILE FORMAT GENERATORS --
|
||||
|
@ -86,9 +129,9 @@ rec {
|
|||
}:
|
||||
let mkLine = k: v: indent + mkKeyValue k v + "\n";
|
||||
mkLines = if listsAsDuplicateKeys
|
||||
then k: v: map (mkLine k) (if lib.isList v then v else [v])
|
||||
then k: v: map (mkLine k) (if isList v then v else [v])
|
||||
else k: v: [ (mkLine k v) ];
|
||||
in attrs: libStr.concatStrings (lib.concatLists (libAttr.mapAttrsToList mkLines attrs));
|
||||
in attrs: concatStrings (concatLists (mapAttrsToList mkLines attrs));
|
||||
|
||||
|
||||
/* Generate an INI-style config file from an
|
||||
|
@ -113,7 +156,7 @@ rec {
|
|||
*/
|
||||
toINI = {
|
||||
# apply transformations (e.g. escapes) to section names
|
||||
mkSectionName ? (name: libStr.escape [ "[" "]" ] name),
|
||||
mkSectionName ? (name: escape [ "[" "]" ] name),
|
||||
# format a setting line from key and value
|
||||
mkKeyValue ? mkKeyValueDefault {} "=",
|
||||
# allow lists as values for duplicate keys
|
||||
|
@ -122,8 +165,8 @@ rec {
|
|||
let
|
||||
# map function to string for each key val
|
||||
mapAttrsToStringsSep = sep: mapFn: attrs:
|
||||
libStr.concatStringsSep sep
|
||||
(libAttr.mapAttrsToList mapFn attrs);
|
||||
concatStringsSep sep
|
||||
(mapAttrsToList mapFn attrs);
|
||||
mkSection = sectName: sectValues: ''
|
||||
[${mkSectionName sectName}]
|
||||
'' + toKeyValue { inherit mkKeyValue listsAsDuplicateKeys; } sectValues;
|
||||
|
@ -164,7 +207,7 @@ rec {
|
|||
*/
|
||||
toINIWithGlobalSection = {
|
||||
# apply transformations (e.g. escapes) to section names
|
||||
mkSectionName ? (name: libStr.escape [ "[" "]" ] name),
|
||||
mkSectionName ? (name: escape [ "[" "]" ] name),
|
||||
# format a setting line from key and value
|
||||
mkKeyValue ? mkKeyValueDefault {} "=",
|
||||
# allow lists as values for duplicate keys
|
||||
|
@ -195,12 +238,11 @@ rec {
|
|||
*> name = "edolstra"
|
||||
*/
|
||||
toGitINI = attrs:
|
||||
with builtins;
|
||||
let
|
||||
mkSectionName = name:
|
||||
let
|
||||
containsQuote = libStr.hasInfix ''"'' name;
|
||||
sections = libStr.splitString "." name;
|
||||
containsQuote = hasInfix ''"'' name;
|
||||
sections = splitString "." name;
|
||||
section = head sections;
|
||||
subsections = tail sections;
|
||||
subsection = concatStringsSep "." subsections;
|
||||
|
@ -220,19 +262,19 @@ rec {
|
|||
# generation for multiple ini values
|
||||
mkKeyValue = k: v:
|
||||
let mkKeyValue = mkKeyValueDefault { inherit mkValueString; } " = " k;
|
||||
in concatStringsSep "\n" (map (kv: "\t" + mkKeyValue kv) (lib.toList v));
|
||||
in concatStringsSep "\n" (map (kv: "\t" + mkKeyValue kv) (toList v));
|
||||
|
||||
# converts { a.b.c = 5; } to { "a.b".c = 5; } for toINI
|
||||
gitFlattenAttrs = let
|
||||
recurse = path: value:
|
||||
if isAttrs value && !lib.isDerivation value then
|
||||
lib.mapAttrsToList (name: value: recurse ([ name ] ++ path) value) value
|
||||
if isAttrs value && !isDerivation value then
|
||||
mapAttrsToList (name: value: recurse ([ name ] ++ path) value) value
|
||||
else if length path > 1 then {
|
||||
${concatStringsSep "." (lib.reverseList (tail path))}.${head path} = value;
|
||||
${concatStringsSep "." (reverseList (tail path))}.${head path} = value;
|
||||
} else {
|
||||
${head path} = value;
|
||||
};
|
||||
in attrs: lib.foldl lib.recursiveUpdate { } (lib.flatten (recurse [ ] attrs));
|
||||
in attrs: foldl recursiveUpdate { } (flatten (recurse [ ] attrs));
|
||||
|
||||
toINI_ = toINI { inherit mkKeyValue mkSectionName; };
|
||||
in
|
||||
|
@ -240,25 +282,12 @@ rec {
|
|||
|
||||
# mkKeyValueDefault wrapper that handles dconf INI quirks.
|
||||
# The main differences of the format is that it requires strings to be quoted.
|
||||
mkDconfKeyValue = mkKeyValueDefault { mkValueString = v: toString (lib.gvariant.mkValue v); } "=";
|
||||
mkDconfKeyValue = mkKeyValueDefault { mkValueString = v: toString (gvariant.mkValue v); } "=";
|
||||
|
||||
# Generates INI in dconf keyfile style. See https://help.gnome.org/admin/system-admin-guide/stable/dconf-keyfiles.html.en
|
||||
# for details.
|
||||
toDconfINI = toINI { mkKeyValue = mkDconfKeyValue; };
|
||||
|
||||
/* 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 = toJSON;
|
||||
|
||||
withRecursion =
|
||||
{
|
||||
/* If this option is not null, the given value will stop evaluating at a certain depth */
|
||||
|
@ -266,7 +295,7 @@ rec {
|
|||
/* If this option is true, an error will be thrown, if a certain given depth is exceeded */
|
||||
, throwOnDepthLimit ? true
|
||||
}:
|
||||
assert builtins.isInt depthLimit;
|
||||
assert isInt depthLimit;
|
||||
let
|
||||
specialAttrs = [
|
||||
"__functor"
|
||||
|
@ -275,7 +304,7 @@ rec {
|
|||
"__pretty"
|
||||
];
|
||||
stepIntoAttr = evalNext: name:
|
||||
if builtins.elem name specialAttrs
|
||||
if elem name specialAttrs
|
||||
then id
|
||||
else evalNext;
|
||||
transform = depth:
|
||||
|
@ -284,7 +313,7 @@ rec {
|
|||
then throw "Exceeded maximum eval-depth limit of ${toString depthLimit} while trying to evaluate with `generators.withRecursion'!"
|
||||
else const "<unevaluated>"
|
||||
else id;
|
||||
mapAny = with builtins; depth: v:
|
||||
mapAny = depth: v:
|
||||
let
|
||||
evalNext = x: mapAny (depth + 1) (transform (depth + 1) x);
|
||||
in
|
||||
|
@ -311,9 +340,8 @@ rec {
|
|||
indent ? ""
|
||||
}:
|
||||
let
|
||||
go = indent: v: with builtins;
|
||||
let isPath = v: typeOf v == "path";
|
||||
introSpace = if multiline then "\n${indent} " else " ";
|
||||
go = indent: v:
|
||||
let introSpace = if multiline then "\n${indent} " else " ";
|
||||
outroSpace = if multiline then "\n${indent}" else " ";
|
||||
in if isInt v then toString v
|
||||
# toString loses precision on floats, so we use toJSON instead. This isn't perfect
|
||||
|
@ -322,16 +350,16 @@ rec {
|
|||
else if isFloat v then builtins.toJSON v
|
||||
else if isString v then
|
||||
let
|
||||
lines = filter (v: ! isList v) (builtins.split "\n" v);
|
||||
escapeSingleline = libStr.escape [ "\\" "\"" "\${" ];
|
||||
escapeMultiline = libStr.replaceStrings [ "\${" "''" ] [ "''\${" "'''" ];
|
||||
lines = filter (v: ! isList v) (split "\n" v);
|
||||
escapeSingleline = escape [ "\\" "\"" "\${" ];
|
||||
escapeMultiline = replaceStrings [ "\${" "''" ] [ "''\${" "'''" ];
|
||||
singlelineResult = "\"" + concatStringsSep "\\n" (map escapeSingleline lines) + "\"";
|
||||
multilineResult = let
|
||||
escapedLines = map escapeMultiline lines;
|
||||
# The last line gets a special treatment: if it's empty, '' is on its own line at the "outer"
|
||||
# indentation level. Otherwise, '' is appended to the last line.
|
||||
lastLine = lib.last escapedLines;
|
||||
in "''" + introSpace + concatStringsSep introSpace (lib.init escapedLines)
|
||||
lastLine = last escapedLines;
|
||||
in "''" + introSpace + concatStringsSep introSpace (init escapedLines)
|
||||
+ (if lastLine == "" then outroSpace else introSpace + lastLine) + "''";
|
||||
in
|
||||
if multiline && length lines > 1 then multilineResult else singlelineResult
|
||||
|
@ -342,11 +370,11 @@ rec {
|
|||
else if isList v then
|
||||
if v == [] then "[ ]"
|
||||
else "[" + introSpace
|
||||
+ libStr.concatMapStringsSep introSpace (go (indent + " ")) v
|
||||
+ concatMapStringsSep introSpace (go (indent + " ")) v
|
||||
+ outroSpace + "]"
|
||||
else if isFunction v then
|
||||
let fna = lib.functionArgs v;
|
||||
showFnas = concatStringsSep ", " (libAttr.mapAttrsToList
|
||||
let fna = functionArgs v;
|
||||
showFnas = concatStringsSep ", " (mapAttrsToList
|
||||
(name: hasDefVal: if hasDefVal then name + "?" else name)
|
||||
fna);
|
||||
in if fna == {} then "<function>"
|
||||
|
@ -359,10 +387,10 @@ rec {
|
|||
else if v ? type && v.type == "derivation" then
|
||||
"<derivation ${v.name or "???"}>"
|
||||
else "{" + introSpace
|
||||
+ libStr.concatStringsSep introSpace (libAttr.mapAttrsToList
|
||||
+ concatStringsSep introSpace (mapAttrsToList
|
||||
(name: value:
|
||||
"${libStr.escapeNixIdentifier name} = ${
|
||||
builtins.addErrorContext "while evaluating an attribute `${name}`"
|
||||
"${escapeNixIdentifier name} = ${
|
||||
addErrorContext "while evaluating an attribute `${name}`"
|
||||
(go (indent + " ") value)
|
||||
};") v)
|
||||
+ outroSpace + "}"
|
||||
|
@ -371,9 +399,7 @@ rec {
|
|||
|
||||
# PLIST handling
|
||||
toPlist = {}: v: let
|
||||
isFloat = builtins.isFloat or (x: false);
|
||||
isPath = x: builtins.typeOf x == "path";
|
||||
expr = ind: x: with builtins;
|
||||
expr = ind: x:
|
||||
if x == null then "" else
|
||||
if isBool x then bool ind x else
|
||||
if isInt x then int ind x else
|
||||
|
@ -394,23 +420,23 @@ rec {
|
|||
|
||||
indent = ind: expr "\t${ind}";
|
||||
|
||||
item = ind: libStr.concatMapStringsSep "\n" (indent ind);
|
||||
item = ind: concatMapStringsSep "\n" (indent ind);
|
||||
|
||||
list = ind: x: libStr.concatStringsSep "\n" [
|
||||
list = ind: x: concatStringsSep "\n" [
|
||||
(literal ind "<array>")
|
||||
(item ind x)
|
||||
(literal ind "</array>")
|
||||
];
|
||||
|
||||
attrs = ind: x: libStr.concatStringsSep "\n" [
|
||||
attrs = ind: x: concatStringsSep "\n" [
|
||||
(literal ind "<dict>")
|
||||
(attr ind x)
|
||||
(literal ind "</dict>")
|
||||
];
|
||||
|
||||
attr = let attrFilter = name: value: name != "_module" && value != null;
|
||||
in ind: x: libStr.concatStringsSep "\n" (lib.flatten (lib.mapAttrsToList
|
||||
(name: value: lib.optionals (attrFilter name value) [
|
||||
in ind: x: concatStringsSep "\n" (flatten (mapAttrsToList
|
||||
(name: value: optionals (attrFilter name value) [
|
||||
(key "\t${ind}" name)
|
||||
(expr "\t${ind}" value)
|
||||
]) x));
|
||||
|
@ -426,11 +452,10 @@ ${expr "" v}
|
|||
* the Natural type.
|
||||
*/
|
||||
toDhall = { }@args: v:
|
||||
with builtins;
|
||||
let concatItems = lib.strings.concatStringsSep ", ";
|
||||
let concatItems = concatStringsSep ", ";
|
||||
in if isAttrs v then
|
||||
"{ ${
|
||||
concatItems (lib.attrsets.mapAttrsToList
|
||||
concatItems (mapAttrsToList
|
||||
(key: value: "${key} = ${toDhall args value}") v)
|
||||
} }"
|
||||
else if isList v then
|
||||
|
@ -444,7 +469,7 @@ ${expr "" v}
|
|||
else if v == null then
|
||||
abort "generators.toDhall: cannot convert a null to Dhall"
|
||||
else
|
||||
builtins.toJSON v;
|
||||
toJSON v;
|
||||
|
||||
/*
|
||||
Translate a simple Nix expression to Lua representation with occasional
|
||||
|
@ -488,7 +513,6 @@ ${expr "" v}
|
|||
/* Interpret as variable bindings */
|
||||
asBindings ? false,
|
||||
}@args: v:
|
||||
with builtins;
|
||||
let
|
||||
innerIndent = "${indent} ";
|
||||
introSpace = if multiline then "\n${innerIndent}" else " ";
|
||||
|
@ -501,9 +525,9 @@ ${expr "" v}
|
|||
isLuaInline = { _type ? null, ... }: _type == "lua-inline";
|
||||
|
||||
generatedBindings =
|
||||
assert lib.assertMsg (badVarNames == []) "Bad Lua var names: ${toPretty {} badVarNames}";
|
||||
libStr.concatStrings (
|
||||
lib.attrsets.mapAttrsToList (key: value: "${indent}${key} = ${toLua innerArgs value}\n") v
|
||||
assert assertMsg (badVarNames == []) "Bad Lua var names: ${toPretty {} badVarNames}";
|
||||
concatStrings (
|
||||
mapAttrsToList (key: value: "${indent}${key} = ${toLua innerArgs value}\n") v
|
||||
);
|
||||
|
||||
# https://en.wikibooks.org/wiki/Lua_Programming/variable#Variable_names
|
||||
|
@ -515,7 +539,7 @@ ${expr "" v}
|
|||
else if v == null then
|
||||
"nil"
|
||||
else if isInt v || isFloat v || isString v || isBool v then
|
||||
builtins.toJSON v
|
||||
toJSON v
|
||||
else if isList v then
|
||||
(if v == [ ] then "{}" else
|
||||
"{${introSpace}${concatItems (map (value: "${toLua innerArgs value}") v)}${outroSpace}}")
|
||||
|
@ -525,11 +549,11 @@ ${expr "" v}
|
|||
"(${v.expr})"
|
||||
else if v == { } then
|
||||
"{}"
|
||||
else if libAttr.isDerivation v then
|
||||
else if isDerivation v then
|
||||
''"${toString v}"''
|
||||
else
|
||||
"{${introSpace}${concatItems (
|
||||
lib.attrsets.mapAttrsToList (key: value: "[${builtins.toJSON key}] = ${toLua innerArgs value}") v
|
||||
mapAttrsToList (key: value: "[${toJSON key}] = ${toLua innerArgs value}") v
|
||||
)}${outroSpace}}"
|
||||
)
|
||||
else
|
||||
|
@ -542,4 +566,37 @@ ${expr "" v}
|
|||
mkLuaInline :: String -> AttrSet
|
||||
*/
|
||||
mkLuaInline = expr: { _type = "lua-inline"; inherit expr; };
|
||||
|
||||
in
|
||||
|
||||
# Everything in this attrset is the public interface of the file.
|
||||
{
|
||||
inherit
|
||||
mkDconfKeyValue
|
||||
mkKeyValueDefault
|
||||
mkLuaInline
|
||||
mkValueStringDefault
|
||||
toDconfINI
|
||||
toDhall
|
||||
toGitINI
|
||||
toINI
|
||||
toINIWithGlobalSection
|
||||
toKeyValue
|
||||
toLua
|
||||
toPlist
|
||||
toPretty
|
||||
withRecursion
|
||||
;
|
||||
|
||||
/* Generates JSON from an arbitrary (non-function) value.
|
||||
* For more information see the documentation of the builtin.
|
||||
*/
|
||||
toJSON = {}: 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 = {}: toJSON;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue