Merge pull request #312407 from hsjobeki/doc/lib-generators

doc: init lib.generators reference documentation
This commit is contained in:
Silvan Mosberger 2024-06-26 22:09:05 +02:00 committed by GitHub
commit 54a93d0525
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 399 additions and 194 deletions

View File

@ -23,6 +23,7 @@ let
{ name = "fileset"; description = "file set functions"; } { name = "fileset"; description = "file set functions"; }
{ name = "sources"; description = "source filtering functions"; } { name = "sources"; description = "source filtering functions"; }
{ name = "cli"; description = "command-line serialization functions"; } { name = "cli"; description = "command-line serialization functions"; }
{ name = "generators"; description = "functions that create file formats from nix data structures"; }
{ name = "gvariant"; description = "GVariant formatted string serialization functions"; } { name = "gvariant"; description = "GVariant formatted string serialization functions"; }
{ name = "customisation"; description = "Functions to customise (derivation-related) functions, derivatons, or attribute sets"; } { name = "customisation"; description = "Functions to customise (derivation-related) functions, derivatons, or attribute sets"; }
{ name = "meta"; description = "functions for derivation metadata"; } { name = "meta"; description = "functions for derivation metadata"; }

View File

@ -54,4 +54,4 @@ merge:"diff3"
Nix store paths can be converted to strings by enclosing a derivation attribute like so: `"${drv}"`. Nix store paths can be converted to strings by enclosing a derivation attribute like so: `"${drv}"`.
::: :::
Detailed documentation for each generator can be found in `lib/generators.nix`. Detailed documentation for each generator can be found [here](#sec-functions-library-generators)

View File

@ -1,18 +1,23 @@
/* Functions that generate widespread file /**
* formats from nix data structures. Functions that generate widespread file
* formats from nix data structures.
* They all follow a similar interface:
* generator { config-attrs } data They all follow a similar interface:
*
* `config-attrs` are holes in the generators ```nix
* with sensible default implementations that generator { config-attrs } data
* can be overwritten. The default implementations ```
* are mostly generators themselves, called with
* their respective default values; they can be reused. `config-attrs` are holes in the generators
* with sensible default implementations that
* Tests can be found in ./tests/misc.nix can be overwritten. The default implementations
* Documentation in the manual, #sec-generators are mostly generators themselves, called with
*/ their respective default values; they can be reused.
Tests can be found in ./tests/misc.nix
Further Documentation can be found [here](#sec-generators).
*/
{ lib }: { lib }:
let let
@ -68,11 +73,20 @@ let
; ;
## -- HELPER FUNCTIONS & DEFAULTS -- ## -- HELPER FUNCTIONS & DEFAULTS --
in rec {
/**
Convert a value to a sensible default string representation.
The builtin `toString` function has some strange defaults,
suitable for bash scripts but not much else.
/* Convert a value to a sensible default string representation. # Inputs
* The builtin `toString` function has some strange defaults,
* suitable for bash scripts but not much else. Options
*/ : Empty set, there may be configuration options in the future
`v`
: 2\. Function argument
*/
mkValueStringDefault = {}: v: mkValueStringDefault = {}: v:
let err = t: v: abort let err = t: v: abort
("generators.mkValueStringDefault: " + ("generators.mkValueStringDefault: " +
@ -100,15 +114,36 @@ let
else err "this value is" (toString v); else err "this value is" (toString v);
/* Generate a line of key k and value v, separated by /**
* character sep. If sep appears in k, it is escaped. Generate a line of key k and value v, separated by
* Helper for synaxes with different separators. character sep. If sep appears in k, it is escaped.
* Helper for synaxes with different separators.
* mkValueString specifies how values should be formatted.
* mkValueString specifies how values should be formatted.
* mkKeyValueDefault {} ":" "f:oo" "bar"
* > "f\:oo:bar" ```nix
*/ mkKeyValueDefault {} ":" "f:oo" "bar"
> "f\:oo:bar"
```
# Inputs
Structured function argument
: mkValueString (optional, default: `mkValueStringDefault {}`)
: Function to convert values to strings
`sep`
: 2\. Function argument
`k`
: 3\. Function argument
`v`
: 4\. Function argument
*/
mkKeyValueDefault = { mkKeyValueDefault = {
mkValueString ? mkValueStringDefault {} mkValueString ? mkValueStringDefault {}
}: sep: k: v: }: sep: k: v:
@ -118,10 +153,23 @@ let
## -- FILE FORMAT GENERATORS -- ## -- FILE FORMAT GENERATORS --
/* Generate a key-value-style config file from an attrset. /**
* Generate a key-value-style config file from an attrset.
* mkKeyValue is the same as in toINI.
*/ # Inputs
Structured function argument
: mkKeyValue (optional, default: `mkKeyValueDefault {} "="`)
: format a setting line from key and value
: listsAsDuplicateKeys (optional, default: `false`)
: allow lists as values for duplicate keys
: indent (optional, default: `""`)
: Initial indentation level
*/
toKeyValue = { toKeyValue = {
mkKeyValue ? mkKeyValueDefault {} "=", mkKeyValue ? mkKeyValueDefault {} "=",
listsAsDuplicateKeys ? false, listsAsDuplicateKeys ? false,
@ -134,32 +182,51 @@ let
in attrs: concatStrings (concatLists (mapAttrsToList mkLines attrs)); in attrs: concatStrings (concatLists (mapAttrsToList mkLines attrs));
/* Generate an INI-style config file from an /**
* attrset of sections to an attrset of key-value pairs. 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"; }; # Inputs
* baz = { "also, integers" = 42; };
* } Structured function argument
*
*> [baz] : mkSectionName (optional, default: `(name: escape [ "[" "]" ] name)`)
*> also, integers=42 : apply transformations (e.g. escapes) to section names
*>
*> [foo] : mkKeyValue (optional, default: `{} "="`)
*> ciao=bar : format a setting line from key and value
*> hi=/nix/store/y93qql1p5ggfnaqjjqhxcw0vqw95rlz0-hello-2.10
* : listsAsDuplicateKeys (optional, default: `false`)
* The mk* configuration attributes can generically change : allow lists as values for duplicate keys
* the way sections and key-value strings are generated.
* # Examples
* For more examples see the test cases in ./tests/misc.nix. :::{.example}
*/ ## `lib.generators.toINI` usage example
```nix
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/misc.nix.
:::
*/
toINI = { toINI = {
# apply transformations (e.g. escapes) to section names
mkSectionName ? (name: escape [ "[" "]" ] name), mkSectionName ? (name: escape [ "[" "]" ] name),
# format a setting line from key and value
mkKeyValue ? mkKeyValueDefault {} "=", mkKeyValue ? mkKeyValueDefault {} "=",
# allow lists as values for duplicate keys
listsAsDuplicateKeys ? false listsAsDuplicateKeys ? false
}: attrsOfAttrs: }: attrsOfAttrs:
let let
@ -174,43 +241,70 @@ let
# map input to ini sections # map input to ini sections
mapAttrsToStringsSep "\n" mkSection attrsOfAttrs; mapAttrsToStringsSep "\n" mkSection attrsOfAttrs;
/* Generate an INI-style config file from an attrset /**
* specifying the global section (no header), and an Generate an INI-style config file from an attrset
* attrset of sections to an attrset of key-value pairs. specifying the global section (no header), and an
* attrset of sections to an attrset of key-value pairs.
* generators.toINIWithGlobalSection {} {
* globalSection = { # Inputs
* someGlobalKey = "hi";
* }; 1\. Structured function argument
* sections = {
* foo = { hi = "${pkgs.hello}"; ciao = "bar"; }; : mkSectionName (optional, default: `(name: escape [ "[" "]" ] name)`)
* baz = { "also, integers" = 42; }; : apply transformations (e.g. escapes) to section names
* }
* : mkKeyValue (optional, default: `{} "="`)
*> someGlobalKey=hi : format a setting line from key and value
*>
*> [baz] : listsAsDuplicateKeys (optional, default: `false`)
*> also, integers=42 : allow lists as values for duplicate keys
*>
*> [foo] 2\. Structured function argument
*> ciao=bar
*> hi=/nix/store/y93qql1p5ggfnaqjjqhxcw0vqw95rlz0-hello-2.10 : globalSection (required)
* : global section key-value pairs
* The mk* configuration attributes can generically change
* the way sections and key-value strings are generated. : sections (optional, default: `{}`)
* : attrset of sections to key-value pairs
* For more examples see the test cases in ./tests/misc.nix.
* # Examples
* If you dont need a global section, you can also use :::{.example}
* `generators.toINI` directly, which only takes ## `lib.generators.toINIWithGlobalSection` usage example
* the part in `sections`.
*/ ```nix
generators.toINIWithGlobalSection {} {
globalSection = {
someGlobalKey = "hi";
};
sections = {
foo = { hi = "${pkgs.hello}"; ciao = "bar"; };
baz = { "also, integers" = 42; };
}
> someGlobalKey=hi
>
> [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/misc.nix.
:::
If you dont need a global section, you can also use
`generators.toINI` directly, which only takes
the part in `sections`.
*/
toINIWithGlobalSection = { toINIWithGlobalSection = {
# apply transformations (e.g. escapes) to section names
mkSectionName ? (name: escape [ "[" "]" ] name), mkSectionName ? (name: escape [ "[" "]" ] name),
# format a setting line from key and value
mkKeyValue ? mkKeyValueDefault {} "=", mkKeyValue ? mkKeyValueDefault {} "=",
# allow lists as values for duplicate keys
listsAsDuplicateKeys ? false listsAsDuplicateKeys ? false
}: { globalSection, sections ? {} }: }: { globalSection, sections ? {} }:
( if globalSection == {} ( if globalSection == {}
@ -219,24 +313,43 @@ let
+ "\n") + "\n")
+ (toINI { inherit mkSectionName mkKeyValue listsAsDuplicateKeys; } sections); + (toINI { inherit mkSectionName mkKeyValue listsAsDuplicateKeys; } sections);
/* Generate a git-config file from an attrset. /**
* Generate a git-config file from an attrset.
* It has two major differences from the regular INI format:
* It has two major differences from the regular INI format:
* 1. values are indented with tabs
* 2. sections can have sub-sections 1. values are indented with tabs
* 2. sections can have sub-sections
* generators.toGitINI {
* url."ssh://git@github.com/".insteadOf = "https://github.com"; Further: https://git-scm.com/docs/git-config#EXAMPLES
* user.name = "edolstra";
* } # Examples
* :::{.example}
*> [url "ssh://git@github.com/"] ## `lib.generators.toGitINI` usage example
*> insteadOf = "https://github.com"
*> ```nix
*> [user] generators.toGitINI {
*> name = "edolstra" url."ssh://git@github.com/".insteadOf = "https://github.com";
*/ user.name = "edolstra";
}
> [url "ssh://git@github.com/"]
> insteadOf = "https://github.com"
>
> [user]
> name = "edolstra"
```
:::
# Inputs
`attrs`
: Key-value pairs to be converted to a git-config file.
See: https://git-scm.com/docs/git-config#_variables for possible values.
*/
toGitINI = attrs: toGitINI = attrs:
let let
mkSectionName = name: mkSectionName = name:
@ -280,20 +393,40 @@ let
in in
toINI_ (gitFlattenAttrs attrs); toINI_ (gitFlattenAttrs attrs);
# mkKeyValueDefault wrapper that handles dconf INI quirks. /**
# The main differences of the format is that it requires strings to be quoted. 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 (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. 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; }; toDconfINI = toINI { mkKeyValue = mkDconfKeyValue; };
/**
Recurses through a `Value` limited to a certain depth. (`depthLimit`)
If the depth is exceeded, an error is thrown, unless `throwOnDepthLimit` is set to `false`.
# Inputs
Structured function argument
: depthLimit (required)
: If this option is not null, the given value will stop evaluating at a certain depth
: throwOnDepthLimit (optional, default: `true`)
: If this option is true, an error will be thrown, if a certain given depth is exceeded
Value
: The value to be evaluated recursively
*/
withRecursion = withRecursion =
{ {
/* If this option is not null, the given value will stop evaluating at a certain depth */ depthLimit,
depthLimit throwOnDepthLimit ? true
/* If this option is true, an error will be thrown, if a certain given depth is exceeded */
, throwOnDepthLimit ? true
}: }:
assert isInt depthLimit; assert isInt depthLimit;
let let
@ -323,20 +456,33 @@ let
in in
mapAny 0; mapAny 0;
/* Pretty print a value, akin to `builtins.trace`. /**
* Should probably be a builtin as well. Pretty print a value, akin to `builtins.trace`.
* The pretty-printed string should be suitable for rendering default values
* in the NixOS manual. In particular, it should be as close to a valid Nix expression Should probably be a builtin as well.
* as possible.
*/ The pretty-printed string should be suitable for rendering default values
in the NixOS manual. In particular, it should be as close to a valid Nix expression
as possible.
# Inputs
Structured function argument
: allowPrettyValues
: 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.)
: multiline
: If this option is true, the output is indented with newlines for attribute sets and lists
: indent
: Initial indentation level
Value
: The value to be pretty printed
*/
toPretty = { 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, allowPrettyValues ? false,
/* If this option is true, the output is indented with newlines for attribute sets and lists */
multiline ? true, multiline ? true,
/* Initial indentation level */
indent ? "" indent ? ""
}: }:
let let
@ -397,7 +543,17 @@ let
else abort "generators.toPretty: should never happen (v = ${v})"; else abort "generators.toPretty: should never happen (v = ${v})";
in go indent; in go indent;
# PLIST handling /**
Translate a simple Nix expression to [Plist notation](https://en.wikipedia.org/wiki/Property_list).
# Inputs
Options
: Empty set, there may be configuration options in the future
Value
: The value to be converted to Plist
*/
toPlist = {}: v: let toPlist = {}: v: let
expr = ind: x: expr = ind: x:
if x == null then "" else if x == null then "" else
@ -447,9 +603,21 @@ let
${expr "" v} ${expr "" v}
</plist>''; </plist>'';
/* Translate a simple Nix expression to Dhall notation. /**
* Note that integers are translated to Integer and never Translate a simple Nix expression to Dhall notation.
* the Natural type.
Note that integers are translated to Integer and never
the Natural type.
# Inputs
Options
: Empty set, there may be configuration options in the future
Value
: The value to be converted to Dhall
*/ */
toDhall = { }@args: v: toDhall = { }@args: v:
let concatItems = concatStringsSep ", "; let concatItems = concatStringsSep ", ";
@ -471,46 +639,71 @@ ${expr "" v}
else else
toJSON v; toJSON v;
/* /**
Translate a simple Nix expression to Lua representation with occasional Translate a simple Nix expression to Lua representation with occasional
Lua-inlines that can be constructed by mkLuaInline function. Lua-inlines that can be constructed by mkLuaInline function.
Configuration: Configuration:
* multiline - by default is true which results in indented block-like view.
* indent - initial indent.
* asBindings - by default generate single value, but with this use attrset to set global vars.
Attention: * multiline - by default is true which results in indented block-like view.
Regardless of multiline parameter there is no trailing newline. * indent - initial indent.
* asBindings - by default generate single value, but with this use attrset to set global vars.
Example: Attention:
generators.toLua {}
{ Regardless of multiline parameter there is no trailing newline.
cmd = [ "typescript-language-server" "--stdio" ];
settings.workspace.library = mkLuaInline ''vim.api.nvim_get_runtime_file("", true)'';
} # Inputs
->
Structured function argument
: multiline (optional, default: `true`)
: If this option is true, the output is indented with newlines for attribute sets and lists
: indent (optional, default: `""`)
: Initial indentation level
: asBindings (optional, default: `false`)
: Interpret as variable bindings
Value
: The value to be converted to Lua
# Type
```
toLua :: AttrSet -> Any -> String
```
# Examples
:::{.example}
## `lib.generators.toLua` usage example
```nix
generators.toLua {}
{ {
["cmd"] = { cmd = [ "typescript-language-server" "--stdio" ];
"typescript-language-server", settings.workspace.library = mkLuaInline ''vim.api.nvim_get_runtime_file("", true)'';
"--stdio"
},
["settings"] = {
["workspace"] = {
["library"] = (vim.api.nvim_get_runtime_file("", true))
}
}
} }
->
{
["cmd"] = {
"typescript-language-server",
"--stdio"
},
["settings"] = {
["workspace"] = {
["library"] = (vim.api.nvim_get_runtime_file("", true))
}
}
}
```
Type: :::
toLua :: AttrSet -> Any -> String
*/ */
toLua = { toLua = {
/* If this option is true, the output is indented with newlines for attribute sets and lists */
multiline ? true, multiline ? true,
/* Initial indentation level */
indent ? "", indent ? "",
/* Interpret as variable bindings */
asBindings ? false, asBindings ? false,
}@args: v: }@args: v:
let let
@ -559,44 +752,55 @@ ${expr "" v}
else else
abort "generators.toLua: type ${typeOf v} is unsupported"; abort "generators.toLua: type ${typeOf v} is unsupported";
/* /**
Mark string as Lua expression to be inlined when processed by toLua. Mark string as Lua expression to be inlined when processed by toLua.
Type:
mkLuaInline :: String -> AttrSet # Inputs
`expr`
: 1\. Function argument
# Type
```
mkLuaInline :: String -> AttrSet
```
*/ */
mkLuaInline = expr: { _type = "lua-inline"; inherit expr; }; mkLuaInline = expr: { _type = "lua-inline"; inherit expr; };
} // {
/**
Generates JSON from an arbitrary (non-function) value.
For more information see the documentation of the builtin.
in # Inputs
# Everything in this attrset is the public interface of the file. Options
{
inherit
mkDconfKeyValue
mkKeyValueDefault
mkLuaInline
mkValueStringDefault
toDconfINI
toDhall
toGitINI
toINI
toINIWithGlobalSection
toKeyValue
toLua
toPlist
toPretty
withRecursion
;
/* Generates JSON from an arbitrary (non-function) value. : Empty set, there may be configuration options in the future
* For more information see the documentation of the builtin.
*/
toJSON = {}: toJSON;
/* YAML has been a strict superset of JSON since 1.2, so we Value
* use toJSON. Before it only had a few differences referring
* to implicit typing rules, so it should work with older : The value to be converted to JSON
* parsers as well. */
*/ toJSON = {}: lib.strings.toJSON;
toYAML = {}: 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.
# Inputs
Options
: Empty set, there may be configuration options in the future
Value
: The value to be converted to YAML
*/
toYAML = {}: lib.strings.toJSON;
} }