{ pkgs, lib, schema, ... }: with lib; let json = pkgs.formats.json { }; submoduleOptions = { spec, depth, extraRequires ? [ ], ... }: let isRequired = n: any (x: x == n) (extraRequires ++ (optionals (spec ? required) spec.required)); in if spec ? "$ref" then submoduleOptions (getRef x."$ref") else mapAttrs ( n: v: buildOption { inherit depth; spec = v; required = isRequired n; } ) (if spec ? properties then spec.properties else { }); getRef = x: let path = splitString "/" (traceVal x); result = attrByPath (tail path) (throw "ref ${x} not found") schema; in result; deref = x: if x ? "$ref" then getRef x."$ref" else x; fileSubmod = types.submodule { options.path = mkOption { type = types.pathWith { inStore = false; absolute = true; }; }; }; buildOptionType = { spec, depth ? 0, ... }: let strType = if spec ? enum then types.enum spec.enum else (types.either types.str fileSubmod); objType = types.submodule { freeformType = json.type; options = submoduleOptions { inherit spec depth; }; }; arrType = types.listOf ( if spec ? items then buildOptionType { inherit depth; spec = spec.items; } else types.anything ); allOfType = let resolve = x: if x ? "if" then x."then" else x; # just ignore conditionals for now resolved = map (x: deref (resolve x)) spec.allOf; # mergedDesc = concatStringsSep "\n" ( # map (x: if x ? markdownDescription then x.markdownDescription else "") resolved # ); combined = foldl (x: c: recursiveUpdate c x) { } resolved; # options = map ( # x: # submoduleOptions { # spec = x; # extraRequires = if spec ? required then spec.required else [ ]; # } # ) (traceValSeqN 4 resolved); in buildOptionType { depth = depth + 1; spec = combined; }; type = if depth > 3 then types.deferredModule else if spec ? "$ref" then buildOptionType { depth = depth + 1; spec = getRef spec."$ref"; } else if spec ? allOf then allOfType else if !spec ? type then json.type else if isList spec.type then types.oneOf (map (x: buildOptionType x) spec.type) else if spec.type == "string" then strType else if spec.type == "boolean" then types.bool else if spec.type == "number" then types.number else if spec.type == "array" then arrType else if spec.type == "object" then objType else (throw "unknown json schema type: ${spec.type}"); in type; buildOption = { spec, depth, required ? false, ... }: let type = buildOptionType { inherit spec depth; }; in mkOption { type = if required then type else types.nullOr type; description = if spec ? markdownDescription then spec.markdownDescription else "no description qwq"; default = if required then if spec.type == "object" then { } else if spec.type == "array" then [ ] else null else null; }; in { generate = json.generate; type = buildOptionType { depth = 0; spec = schema; }; }