From 8ee634f5a65b8bda919f12e95ed4a391fac6e171 Mon Sep 17 00:00:00 2001 From: Johannes Kirschbauer Date: Tue, 21 May 2024 21:54:47 +0200 Subject: [PATCH 1/2] doc: init lib.generators reference documentation --- doc/default.nix | 1 + doc/functions/generators.section.md | 2 +- lib/generators.nix | 578 ++++++++++++++++++---------- 3 files changed, 387 insertions(+), 194 deletions(-) diff --git a/doc/default.nix b/doc/default.nix index ca4091dc222c..9cc06983445a 100644 --- a/doc/default.nix +++ b/doc/default.nix @@ -22,6 +22,7 @@ let { name = "fileset"; description = "file set functions"; } { name = "sources"; description = "source filtering 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 = "customisation"; description = "Functions to customise (derivation-related) functions, derivatons, or attribute sets"; } { name = "meta"; description = "functions for derivation metadata"; } diff --git a/doc/functions/generators.section.md b/doc/functions/generators.section.md index dbfc302a3abf..9d71a0240108 100644 --- a/doc/functions/generators.section.md +++ b/doc/functions/generators.section.md @@ -54,4 +54,4 @@ merge:"diff3" 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) diff --git a/lib/generators.nix b/lib/generators.nix index 5f42a98de709..36891f9caedf 100644 --- a/lib/generators.nix +++ b/lib/generators.nix @@ -1,18 +1,23 @@ -/* Functions that generate widespread file - * formats from nix data structures. - * - * They all follow a similar interface: - * generator { config-attrs } data - * - * `config-attrs` are “holes” in the generators - * with sensible default implementations that - * can be overwritten. The default implementations - * are mostly generators themselves, called with - * their respective default values; they can be reused. - * - * Tests can be found in ./tests/misc.nix - * Documentation in the manual, #sec-generators - */ +/** + Functions that generate widespread file + formats from nix data structures. + + They all follow a similar interface: + + ```nix + generator { config-attrs } data + ``` + + `config-attrs` are “holes” in the generators + with sensible default implementations that + can be overwritten. The default implementations + 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 }: let @@ -68,11 +73,20 @@ let ; ## -- 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. - * The builtin `toString` function has some strange defaults, - * suitable for bash scripts but not much else. - */ + # Inputs + + Options + : Empty set, there may be configuration options in the future + + `v` + : 2\. Function argument + */ mkValueStringDefault = {}: v: let err = t: v: abort ("generators.mkValueStringDefault: " + @@ -100,15 +114,36 @@ let 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. - * Helper for synaxes with different separators. - * - * mkValueString specifies how values should be formatted. - * - * mkKeyValueDefault {} ":" "f:oo" "bar" - * > "f\:oo:bar" - */ + /** + 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. + + mkValueString specifies how values should be formatted. + + ```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 = { mkValueString ? mkValueStringDefault {} }: sep: k: v: @@ -118,10 +153,11 @@ let ## -- FILE FORMAT GENERATORS -- - /* Generate a key-value-style config file from an attrset. - * - * mkKeyValue is the same as in toINI. - */ + /** + Generate a key-value-style config file from an attrset. + * + * mkKeyValue is the same as in toINI. + */ toKeyValue = { mkKeyValue ? mkKeyValueDefault {} "=", listsAsDuplicateKeys ? false, @@ -134,32 +170,51 @@ let 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. - * - * 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. - */ + /** + Generate an INI-style config file from an + attrset of sections to an attrset of key-value pairs. + + # Inputs + + Structured function argument + + : mkSectionName (optional, default: `(name: escape [ "[" "]" ] name)`) + : apply transformations (e.g. escapes) to section names + + : mkKeyValue (optional, default: `{} "="`) + : format a setting line from key and value + + : listsAsDuplicateKeys (optional, default: `false`) + : allow lists as values for duplicate keys + + # Examples + :::{.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 = { - # apply transformations (e.g. escapes) to section names mkSectionName ? (name: escape [ "[" "]" ] name), - # format a setting line from key and value mkKeyValue ? mkKeyValueDefault {} "=", - # allow lists as values for duplicate keys listsAsDuplicateKeys ? false }: attrsOfAttrs: let @@ -174,43 +229,70 @@ let # map input to ini sections mapAttrsToStringsSep "\n" mkSection attrsOfAttrs; - /* Generate an INI-style config file from an attrset - * specifying the global section (no header), and an - * attrset of sections to an attrset of key-value pairs. - * - * 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 don’t need a global section, you can also use - * `generators.toINI` directly, which only takes - * the part in `sections`. - */ + /** + Generate an INI-style config file from an attrset + specifying the global section (no header), and an + attrset of sections to an attrset of key-value pairs. + + # Inputs + + 1\. Structured function argument + + : mkSectionName (optional, default: `(name: escape [ "[" "]" ] name)`) + : apply transformations (e.g. escapes) to section names + + : mkKeyValue (optional, default: `{} "="`) + : format a setting line from key and value + + : listsAsDuplicateKeys (optional, default: `false`) + : allow lists as values for duplicate keys + + 2\. Structured function argument + + : globalSection (required) + : global section key-value pairs + + : sections (optional, default: `{}`) + : attrset of sections to key-value pairs + + # Examples + :::{.example} + ## `lib.generators.toINIWithGlobalSection` usage example + + ```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 don’t need a global section, you can also use + `generators.toINI` directly, which only takes + the part in `sections`. + */ toINIWithGlobalSection = { - # apply transformations (e.g. escapes) to section names mkSectionName ? (name: escape [ "[" "]" ] name), - # format a setting line from key and value mkKeyValue ? mkKeyValueDefault {} "=", - # allow lists as values for duplicate keys listsAsDuplicateKeys ? false }: { globalSection, sections ? {} }: ( if globalSection == {} @@ -219,24 +301,43 @@ let + "\n") + (toINI { inherit mkSectionName mkKeyValue listsAsDuplicateKeys; } sections); - /* Generate a git-config file from an attrset. - * - * It has two major differences from the regular INI format: - * - * 1. values are indented with tabs - * 2. sections can have sub-sections - * - * generators.toGitINI { - * url."ssh://git@github.com/".insteadOf = "https://github.com"; - * user.name = "edolstra"; - * } - * - *> [url "ssh://git@github.com/"] - *> insteadOf = "https://github.com" - *> - *> [user] - *> name = "edolstra" - */ + /** + Generate a git-config file from an attrset. + + It has two major differences from the regular INI format: + + 1. values are indented with tabs + 2. sections can have sub-sections + + Further: https://git-scm.com/docs/git-config#EXAMPLES + + # Examples + :::{.example} + ## `lib.generators.toGitINI` usage example + + ```nix + generators.toGitINI { + 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: let mkSectionName = name: @@ -280,20 +381,40 @@ let in 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); } "="; - # 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; }; + /** + 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 = { - /* If this option is not null, the given value will stop evaluating at a certain depth */ - depthLimit - /* If this option is true, an error will be thrown, if a certain given depth is exceeded */ - , throwOnDepthLimit ? true + depthLimit, + throwOnDepthLimit ? true }: assert isInt depthLimit; let @@ -323,20 +444,33 @@ let in mapAny 0; - /* Pretty print a value, akin to `builtins.trace`. - * Should probably be a builtin as well. - * 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. - */ + /** + Pretty print a value, akin to `builtins.trace`. + + Should probably be a builtin as well. + + 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 = { - /* 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, - /* If this option is true, the output is indented with newlines for attribute sets and lists */ multiline ? true, - /* Initial indentation level */ indent ? "" }: let @@ -397,7 +531,17 @@ let else abort "generators.toPretty: should never happen (v = ${v})"; 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 expr = ind: x: if x == null then "" else @@ -447,9 +591,21 @@ let ${expr "" v} ''; - /* Translate a simple Nix expression to Dhall notation. - * Note that integers are translated to Integer and never - * the Natural type. + /** + Translate a simple Nix expression to Dhall notation. + + 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: let concatItems = concatStringsSep ", "; @@ -471,46 +627,71 @@ ${expr "" v} else toJSON v; - /* - Translate a simple Nix expression to Lua representation with occasional - Lua-inlines that can be constructed by mkLuaInline function. + /** + Translate a simple Nix expression to Lua representation with occasional + Lua-inlines that can be constructed by mkLuaInline function. - 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. + Configuration: - Attention: - Regardless of multiline parameter there is no trailing newline. + * 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. - Example: - generators.toLua {} - { - cmd = [ "typescript-language-server" "--stdio" ]; - settings.workspace.library = mkLuaInline ''vim.api.nvim_get_runtime_file("", true)''; - } - -> + Attention: + + Regardless of multiline parameter there is no trailing newline. + + + # 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"] = { - "typescript-language-server", - "--stdio" - }, - ["settings"] = { - ["workspace"] = { - ["library"] = (vim.api.nvim_get_runtime_file("", true)) - } - } + cmd = [ "typescript-language-server" "--stdio" ]; + settings.workspace.library = mkLuaInline ''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 = { - /* If this option is true, the output is indented with newlines for attribute sets and lists */ multiline ? true, - /* Initial indentation level */ indent ? "", - /* Interpret as variable bindings */ asBindings ? false, }@args: v: let @@ -559,44 +740,55 @@ ${expr "" v} else 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; }; +} // { + /** + 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. -{ - inherit - mkDconfKeyValue - mkKeyValueDefault - mkLuaInline - mkValueStringDefault - toDconfINI - toDhall - toGitINI - toINI - toINIWithGlobalSection - toKeyValue - toLua - toPlist - toPretty - withRecursion - ; + Options - /* Generates JSON from an arbitrary (non-function) value. - * For more information see the documentation of the builtin. - */ - toJSON = {}: toJSON; + : Empty set, there may be configuration options in the future - /* 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; + Value + + : The value to be converted to JSON + */ + toJSON = {}: lib.strings.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; } From 793ed729f2cfe0e387e1237d6aacd8770ce16509 Mon Sep 17 00:00:00 2001 From: Johannes Kirschbauer Date: Tue, 21 May 2024 22:05:18 +0200 Subject: [PATCH 2/2] doc: add arguments for toKeyValue --- lib/generators.nix | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/generators.nix b/lib/generators.nix index 36891f9caedf..4317e49c2538 100644 --- a/lib/generators.nix +++ b/lib/generators.nix @@ -155,8 +155,20 @@ in rec { /** 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 = { mkKeyValue ? mkKeyValueDefault {} "=",