lib: pkgs: # Future improvements: # - extra documentation from anyOf types, they can have `enumDescriptions` that are valuable with lib; let packageJson = "${pkgs.rust-analyzer.src}/editors/code/package.json"; options = (trivial.importJSON packageJson).contributes.configuration.properties; packageJsonTxt = strings.splitString "\n" (builtins.readFile packageJson); vscodeStart = lists.findFirstIndex ( x: x == " \"configuration\": {" ) (throw "no configuration") packageJsonTxt; vscodeEnd = lists.findFirstIndex (strings.hasInfix "generated-start") (throw "no generated start") packageJsonTxt; vscodeOptions = lists.sublist vscodeStart (vscodeEnd - vscodeStart) packageJsonTxt; vscodeOptionNames = builtins.map ( x: strings.removeSuffix "\": {" (strings.removePrefix " \"" x) ) (lists.filter (strings.hasPrefix " \"rust-analyzer.") vscodeOptions); rustAnalyzerOptions = builtins.removeAttrs options ( vscodeOptionNames ++ [ "\$generated-start" "\$generated-end" ] ); nullType = mkOptionType { name = "nullType"; description = "null"; descriptionClass = "noun"; merge = mergeEqualOption; check = e: e == null; }; mkRustAnalyzerType = opt: { type, enum ? null, minimum ? null, maximum ? null, items ? null, anyOf ? null, uniqueItems ? null, # unused, but for exhaustivness enumDescriptions ? null, }@def: if enum != null then types.enum enum else if anyOf != null then types.oneOf (builtins.map (mkRustAnalyzerType opt) anyOf) else if builtins.isList type then if builtins.length type == 2 && builtins.head type == "null" then types.nullOr (mkRustAnalyzerType opt (def // { type = builtins.elemAt type 1; })) else types.oneOf (builtins.map (t: mkRustAnalyzerType opt (def // { type = t; })) type) else if type == "boolean" then types.bool else if type == "object" then types.attrsOf types.anything else if type == "string" then types.str else if type == "null" then nullType else if type == "array" then types.listOf ( mkRustAnalyzerType "${opt}-items" ( if items == null then throw "null items with array type (in ${opt})" else items ) ) else if type == "number" then if minimum != null && maximum != null then types.numbers.between minimum maximum else if minimum != null then types.addCheck types.number (x: x >= minimum) else if maximum != null then types.addCheck types.number (x: x <= maximum) else types.number else if type == "integer" then if minimum != null && maximum != null then types.ints.between minimum maximum else if minimum != null then types.addCheck types.int (x: x >= minimum) else if maximum != null then types.addCheck types.int (x: x <= maximum) else types.int else throw "unhandled type `${builtins.toJSON type}` (in ${opt})"; mkRustAnalyzerOption = opt: { default, markdownDescription, type ? null, anyOf ? null, # Enum types enum ? null, enumDescriptions ? null, # Int types minimum ? null, maximum ? null, # List types items ? null, uniqueItems ? false, }: mkOption { type = types.nullOr ( mkRustAnalyzerType opt { inherit type enum minimum maximum items anyOf uniqueItems ; } ); default = null; description = if enum != null then let valueDesc = builtins.map ({ fst, snd }: ''- ${fst}: ${snd}'') ( lists.zipLists enum enumDescriptions ); in '' ${markdownDescription} Values: ${builtins.concatStringsSep "\n" valueDesc} ```nix ${generators.toPretty { } default} ``` '' else '' ${markdownDescription} default: ```nix ${generators.toPretty { } default} ``` ''; }; nixOptions = builtins.mapAttrs mkRustAnalyzerOption rustAnalyzerOptions; nestOpt = opt: value: let parts = strings.splitString "." opt; in builtins.foldl' (current: segment: { ${segment} = current; }) value (lists.reverseList parts); nestedNixOptions = attrsets.mapAttrsToList nestOpt nixOptions; in (builtins.foldl' attrsets.recursiveUpdate { } nestedNixOptions).rust-analyzer