diff --git a/overview/content-types/option-list.nix b/overview/content-types/option-list.nix new file mode 100644 index 000000000..6aeccf53e --- /dev/null +++ b/overview/content-types/option-list.nix @@ -0,0 +1,43 @@ +{ + lib, + name, + config, + pkgs, + ... +}: +let + inherit (lib) + types + mkOption + optionalString + join + concatLines + ; +in +{ + options = { + prefix = mkOption { + type = with types; listOf str; + }; + project-options = mkOption { + type = + with types; + listOf (submodule { + imports = [ ./option.nix ]; + _module.args.pkgs = pkgs; + }); + default = [ ]; + }; + __toString = mkOption { + type = with types; functionTo str; + readOnly = true; + default = + self: + optionalString (self.project-options != [ ]) '' +
${join "." self.prefix}
+ ${concatLines (map toString self.project-options)} +
+ ''; + }; + }; +} diff --git a/overview/content-types/option.nix b/overview/content-types/option.nix new file mode 100644 index 000000000..563f674eb --- /dev/null +++ b/overview/content-types/option.nix @@ -0,0 +1,114 @@ +{ + lib, + pkgs, + config, + ... +}: +let + inherit (lib) + types + mkOption + join + drop + optionalString + take + ; +in +{ + options = { + # TODO: simplify this / make it dynamic + prefix-length = mkOption { + type = types.int; + description = '' + Length to visually separate the fixed and moving parts of an option, making + less specific elements less dominant to reduce cognitive load. + ''; + default = 2; + }; + attrpath = mkOption { + type = with types; listOf str; + description = '' + Attribute path in the NixOS options tree. + ''; + }; + type = mkOption { + type = types.str; + }; + default = mkOption { + type = types.attrs; + default = { }; + }; + description = mkOption { + type = types.str; + }; + readOnly = mkOption { + type = types.bool; + }; + __toString = mkOption { + type = with types; functionTo str; + readOnly = true; + default = + self: + let + option-prefix = + let + prefix-head = take self.prefix-length self.attrpath; + prefix-tail = drop self.prefix-length self.attrpath; + in + '' + ${join "." prefix-head}.${join "." prefix-tail} + ''; + option-type = '' +
Type:
+
${self.type}
+ ''; + option-default = optionalString (self.default ? text) '' +
Default:
+
${self.default.text}
+ ''; + option-description = + let + # This doesn't actually produce a HTML string but a Jinja2 template string + # literal, that is then replaced by it's HTML translation at the last build + # step. + # Also, this avoids IFD (which would make things very slow with a + # growing number of such strings in the website rendering) since + # this way we can do markdown processing in a single step per output file at the end + markdownToHtml = markdown: "{{ markdown_to_html(${builtins.toJSON markdown}) }}"; + in + '' +
+ ${markdownToHtml self.description} +
+ ''; + alert-update-script = + let + isDrv = self.type == "package"; + optionName = lib.removePrefix "pkgs." self.default.text; + in + # TODO: plug a function that recurses into all dependencies, + # so we'd know how much of the build graph is kept up to date automatically + optionalString (isDrv && !pkgs ? ${optionName}.passthru.updateScript) '' +
Notes:
+
Missing update script An update script is required for automatically tracking the latest release.
+ ''; + in + '' +
+ ${option-prefix} + ${optionalString self.readOnly '' + Read-only + ''} +
+
+ ${option-description} +
+ ${option-type} + ${option-default} + ${alert-update-script} +
+
+ ''; + }; + }; +} diff --git a/overview/default.nix b/overview/default.nix index 5f4d7d92f..7139112fa 100644 --- a/overview/default.nix +++ b/overview/default.nix @@ -14,7 +14,6 @@ let isList isInt substring - toJSON toString ; @@ -27,8 +26,6 @@ let filterAttrs mapAttrs' nameValuePair - drop - join ; empty = @@ -81,57 +78,24 @@ let ); }; - # This doesn't actually produce a HTML string but a Jinja2 template string - # literal, that is then replaced by it's HTML translation at the last build - # step. - markdownToHtml = markdown: "{{ markdown_to_html(${toJSON markdown}) }}"; - render = { - options = rec { - one = - prefix: option: - let - maybeDefault = optionalString (option ? default.text) '' -
Default:
-
${option.default.text}
- ''; - maybeReadonly = optionalString option.readOnly '' - Read-only - ''; - updateScriptStatus = - let - optionName = lib.removePrefix "pkgs." option.default.text; - in - optionalString (option.type == "package" && !pkgs ? ${optionName}.passthru.updateScript) '' -
Notes:
-
Missing update script An update script is required for automatically tracking the latest release.
- ''; - in - '' -
- ${join "." prefix}.${join "." (drop (lib.length prefix) option.loc)} - ${maybeReadonly} -
-
-
- ${markdownToHtml option.description} -
-
-
Type:
-
${option.type}
- ${maybeDefault} - ${updateScriptStatus} -
-
- ''; - many = - prefix: projectOptions: - optionalString (!empty projectOptions) '' -
${join "." prefix}
- ${concatLines (map (one prefix) projectOptions)} -
- ''; - }; + options = + prefix: projectOptions: + eval { + imports = [ ./content-types/option-list.nix ]; + _module.args.pkgs = pkgs; + + inherit prefix; + project-options = map (option: { + inherit (option) + type + description + readOnly + ; + attrpath = option.loc; + default = option.default or { }; + }) projectOptions; + }; examples = rec { one = example: '' @@ -193,14 +157,17 @@ let type: lib.concatMapAttrsStringSep "\n" ( name: val: - optionalString (val.module != null) ( - render.options.many [ type name ] ( - pick.options [ - type - name - ] - ) - ) + let + project-options = pick.options [ + type + name + ]; + attrpath-prefix = [ + type + name + ]; + in + render.options attrpath-prefix project-options ) project.nixos.modules.${type} ) [