150 lines
3.6 KiB
Nix
150 lines
3.6 KiB
Nix
{
|
|
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;
|
|
};
|
|
}
|