re-format with nixfmt

This commit is contained in:
Jörg Thalheim
2024-03-17 19:48:49 +01:00
parent f2a0f84bc2
commit ede4d3f6e5
87 changed files with 2122 additions and 1650 deletions

View File

@@ -1,243 +1,290 @@
{ lib ? import <nixpkgs/lib>
, excludedTypes ? [
{
lib ? import <nixpkgs/lib>,
excludedTypes ? [
"functionTo"
"package"
]
],
}:
let
# remove _module attribute from options
clean = opts: builtins.removeAttrs opts [ "_module" ];
# throw error if option type is not supported
notSupported = option: lib.trace option throw ''
option type '${option.type.name}' ('${option.type.description}') not supported by jsonschema converter
location: ${lib.concatStringsSep "." option.loc}
'';
notSupported =
option:
lib.trace option throw ''
option type '${option.type.name}' ('${option.type.description}') not supported by jsonschema converter
location: ${lib.concatStringsSep "." option.loc}
'';
isExcludedOption = option: (lib.elem (option.type.name or null) excludedTypes);
filterExcluded = lib.filter (opt: ! isExcludedOption opt);
filterExcluded = lib.filter (opt: !isExcludedOption opt);
filterExcludedAttrs = lib.filterAttrs (_name: opt: ! isExcludedOption opt);
allBasicTypes =
[ "boolean" "integer" "number" "string" "array" "object" "null" ];
filterExcludedAttrs = lib.filterAttrs (_name: opt: !isExcludedOption opt);
allBasicTypes = [
"boolean"
"integer"
"number"
"string"
"array"
"object"
"null"
];
in
rec {
# parses a nixos module to a jsonschema
parseModule = module:
parseModule =
module:
let
evaled = lib.evalModules {
modules = [ module ];
};
evaled = lib.evalModules { modules = [ module ]; };
in
parseOptions evaled.options;
# parses a set of evaluated nixos options to a jsonschema
parseOptions = options':
parseOptions =
options':
let
options = filterExcludedAttrs (clean options');
# parse options to jsonschema properties
properties = lib.mapAttrs (_name: option: parseOption option) options;
# TODO: figure out how to handle if prop.anyOf is used
isRequired = prop: ! (prop ? default || prop.type or null == "object");
isRequired = prop: !(prop ? default || prop.type or null == "object");
requiredProps = lib.filterAttrs (_: prop: isRequired prop) properties;
required = lib.optionalAttrs (requiredProps != { }) {
required = lib.attrNames requiredProps;
};
required = lib.optionalAttrs (requiredProps != { }) { required = lib.attrNames requiredProps; };
in
# return jsonschema
required // {
required
// {
type = "object";
inherit properties;
};
# parses and evaluated nixos option to a jsonschema property definition
parseOption = option:
parseOption =
option:
let
default = lib.optionalAttrs (option ? default) {
inherit (option) default;
};
default = lib.optionalAttrs (option ? default) { inherit (option) default; };
description = lib.optionalAttrs (option ? description) {
description = option.description.text or option.description;
};
in
# either type
# TODO: if all nested optiosn are excluded, the parent sould be excluded too
if option.type.name or null == "either"
# TODO: if all nested optiosn are excluded, the parent sould be excluded too
if
option.type.name or null == "either"
# return jsonschema property definition for either
then
let
optionsList' = [
{ type = option.type.nestedTypes.left; _type = "option"; loc = option.loc; }
{ type = option.type.nestedTypes.right; _type = "option"; loc = option.loc; }
{
type = option.type.nestedTypes.left;
_type = "option";
loc = option.loc;
}
{
type = option.type.nestedTypes.right;
_type = "option";
loc = option.loc;
}
];
optionsList = filterExcluded optionsList';
in
default // description // {
anyOf = map parseOption optionsList;
}
default // description // { anyOf = map parseOption optionsList; }
# handle nested options (not a submodule)
else if ! option ? _type
then parseOptions option
else if !option ? _type then
parseOptions option
# throw if not an option
else if option._type != "option" && option._type != "option-type"
then throw "parseOption: not an option"
else if option._type != "option" && option._type != "option-type" then
throw "parseOption: not an option"
# parse nullOr
else if option.type.name == "nullOr"
else if
option.type.name == "nullOr"
# return jsonschema property definition for nullOr
then
let
nestedOption =
{ type = option.type.nestedTypes.elemType; _type = "option"; loc = option.loc; };
nestedOption = {
type = option.type.nestedTypes.elemType;
_type = "option";
loc = option.loc;
};
in
default // description // {
anyOf =
[{ type = "null"; }]
++ (
lib.optional (! isExcludedOption nestedOption)
(parseOption nestedOption)
);
default
// description
// {
anyOf = [
{ type = "null"; }
] ++ (lib.optional (!isExcludedOption nestedOption) (parseOption nestedOption));
}
# parse bool
else if option.type.name == "bool"
else if
option.type.name == "bool"
# return jsonschema property definition for bool
then default // description // {
type = "boolean";
}
then
default // description // { type = "boolean"; }
# parse float
else if option.type.name == "float"
else if
option.type.name == "float"
# return jsonschema property definition for float
then default // description // {
type = "number";
}
then
default // description // { type = "number"; }
# parse int
else if (option.type.name == "int" || option.type.name == "positiveInt")
else if
(option.type.name == "int" || option.type.name == "positiveInt")
# return jsonschema property definition for int
then default // description // {
type = "integer";
}
then
default // description // { type = "integer"; }
# parse string
else if option.type.name == "str"
else if
option.type.name == "str"
# return jsonschema property definition for string
then default // description // {
type = "string";
}
then
default // description // { type = "string"; }
# parse string
else if option.type.name == "path"
else if
option.type.name == "path"
# return jsonschema property definition for path
then default // description // {
type = "string";
}
then
default // description // { type = "string"; }
# parse anything
else if option.type.name == "anything"
else if
option.type.name == "anything"
# return jsonschema property definition for anything
then default // description // {
type = allBasicTypes;
}
then
default // description // { type = allBasicTypes; }
# parse unspecified
else if option.type.name == "unspecified"
else if
option.type.name == "unspecified"
# return jsonschema property definition for unspecified
then default // description // {
type = allBasicTypes;
}
then
default // description // { type = allBasicTypes; }
# parse raw
else if option.type.name == "raw"
else if
option.type.name == "raw"
# return jsonschema property definition for raw
then default // description // {
type = allBasicTypes;
}
then
default // description // { type = allBasicTypes; }
# parse enum
else if option.type.name == "enum"
else if
option.type.name == "enum"
# return jsonschema property definition for enum
then default // description // {
enum = option.type.functor.payload;
}
then
default // description // { enum = option.type.functor.payload; }
# parse listOf submodule
else if option.type.name == "listOf" && option.type.functor.wrapped.name == "submodule"
else if
option.type.name == "listOf" && option.type.functor.wrapped.name == "submodule"
# return jsonschema property definition for listOf submodule
then default // description // {
type = "array";
items = parseOptions (option.type.functor.wrapped.getSubOptions option.loc);
}
then
default
// description
// {
type = "array";
items = parseOptions (option.type.functor.wrapped.getSubOptions option.loc);
}
# parse list
else if (option.type.name == "listOf")
else if
(option.type.name == "listOf")
# return jsonschema property definition for list
then
let
nestedOption = { type = option.type.functor.wrapped; _type = "option"; loc = option.loc; };
nestedOption = {
type = option.type.functor.wrapped;
_type = "option";
loc = option.loc;
};
in
default // description // {
default
// description
// {
type = "array";
}
// (lib.optionalAttrs (! isExcludedOption nestedOption) {
items = parseOption nestedOption;
})
// (lib.optionalAttrs (!isExcludedOption nestedOption) { items = parseOption nestedOption; })
# parse list of unspecified
else if
(option.type.name == "listOf")
&& (option.type.functor.wrapped.name == "unspecified")
(option.type.name == "listOf") && (option.type.functor.wrapped.name == "unspecified")
# return jsonschema property definition for list
then default // description // {
type = "array";
}
then
default // description // { type = "array"; }
# parse attrsOf submodule
else if option.type.name == "attrsOf" && option.type.nestedTypes.elemType.name == "submodule"
else if
option.type.name == "attrsOf" && option.type.nestedTypes.elemType.name == "submodule"
# return jsonschema property definition for attrsOf submodule
then default // description // {
type = "object";
additionalProperties = parseOptions (option.type.nestedTypes.elemType.getSubOptions option.loc);
}
then
default
// description
// {
type = "object";
additionalProperties = parseOptions (option.type.nestedTypes.elemType.getSubOptions option.loc);
}
# parse attrs
else if option.type.name == "attrs"
else if
option.type.name == "attrs"
# return jsonschema property definition for attrs
then default // description // {
type = "object";
additionalProperties = true;
}
then
default
// description
// {
type = "object";
additionalProperties = true;
}
# parse attrsOf
# TODO: if nested option is excluded, the parent sould be excluded too
else if option.type.name == "attrsOf" || option.type.name == "lazyAttrsOf"
else if
option.type.name == "attrsOf" || option.type.name == "lazyAttrsOf"
# return jsonschema property definition for attrs
then
let
nestedOption = { type = option.type.nestedTypes.elemType; _type = "option"; loc = option.loc; };
nestedOption = {
type = option.type.nestedTypes.elemType;
_type = "option";
loc = option.loc;
};
in
default // description // {
default
// description
// {
type = "object";
additionalProperties =
if ! isExcludedOption nestedOption
then parseOption { type = option.type.nestedTypes.elemType; _type = "option"; loc = option.loc; }
else false;
if !isExcludedOption nestedOption then
parseOption {
type = option.type.nestedTypes.elemType;
_type = "option";
loc = option.loc;
}
else
false;
}
# parse submodule
else if option.type.name == "submodule"
else if
option.type.name == "submodule"
# return jsonschema property definition for submodule
# then (lib.attrNames (option.type.getSubOptions option.loc).opt)
then parseOptions (option.type.getSubOptions option.loc)
then
parseOptions (option.type.getSubOptions option.loc)
# throw error if option type is not supported
else notSupported option;
else
notSupported option;
}

View File

@@ -1,7 +1,6 @@
/*
An example nixos module declaring an interface.
*/
{ lib, ... }: {
# An example nixos module declaring an interface.
{ lib, ... }:
{
options = {
# str
name = lib.mkOption {
@@ -44,7 +43,11 @@
# list of str
kernelModules = lib.mkOption {
type = lib.types.listOf lib.types.str;
default = [ "nvme" "xhci_pci" "ahci" ];
default = [
"nvme"
"xhci_pci"
"ahci"
];
description = "A list of enabled kernel modules";
};
};

View File

@@ -1,29 +1,31 @@
{
perSystem = { pkgs, ... }: {
checks = {
perSystem =
{ pkgs, ... }:
{
checks = {
# check if the `clan config` example jsonschema and data is valid
lib-jsonschema-example-valid = pkgs.runCommand "lib-jsonschema-example-valid" { } ''
echo "Checking that example-schema.json is valid"
${pkgs.check-jsonschema}/bin/check-jsonschema \
--check-metaschema ${./.}/example-schema.json
# check if the `clan config` example jsonschema and data is valid
lib-jsonschema-example-valid = pkgs.runCommand "lib-jsonschema-example-valid" { } ''
echo "Checking that example-schema.json is valid"
${pkgs.check-jsonschema}/bin/check-jsonschema \
--check-metaschema ${./.}/example-schema.json
echo "Checking that example-data.json is valid according to example-schema.json"
${pkgs.check-jsonschema}/bin/check-jsonschema \
--schemafile ${./.}/example-schema.json \
${./.}/example-data.json
echo "Checking that example-data.json is valid according to example-schema.json"
${pkgs.check-jsonschema}/bin/check-jsonschema \
--schemafile ${./.}/example-schema.json \
${./.}/example-data.json
touch $out
'';
touch $out
'';
# check if the `clan config` nix jsonschema converter unit tests succeed
lib-jsonschema-nix-unit-tests = pkgs.runCommand "lib-jsonschema-nix-unit-tests" { } ''
export NIX_PATH=nixpkgs=${pkgs.path}
${pkgs.nix-unit}/bin/nix-unit \
${./.}/test.nix \
--eval-store $(realpath .)
touch $out
'';
# check if the `clan config` nix jsonschema converter unit tests succeed
lib-jsonschema-nix-unit-tests = pkgs.runCommand "lib-jsonschema-nix-unit-tests" { } ''
export NIX_PATH=nixpkgs=${pkgs.path}
${pkgs.nix-unit}/bin/nix-unit \
${./.}/test.nix \
--eval-store $(realpath .)
touch $out
'';
};
};
};
}

View File

@@ -1,6 +1,7 @@
# run these tests via `nix-unit ./test.nix`
{ lib ? (import <nixpkgs> { }).lib
, slib ? import ./. { inherit lib; }
{
lib ? (import <nixpkgs> { }).lib,
slib ? import ./. { inherit lib; },
}:
{
parseOption = import ./test_parseOption.nix { inherit lib slib; };

View File

@@ -1,21 +1,25 @@
# tests for the nixos options to jsonschema converter
# run these tests via `nix-unit ./test.nix`
{ lib ? (import <nixpkgs> { }).lib
, slib ? import ./. { inherit lib; }
{
lib ? (import <nixpkgs> { }).lib,
slib ? import ./. { inherit lib; },
}:
let
description = "Test Description";
evalType = type: default:
evalType =
type: default:
let
evaledConfig = lib.evalModules {
modules = [{
options.opt = lib.mkOption {
inherit type;
inherit default;
inherit description;
};
}];
modules = [
{
options.opt = lib.mkOption {
inherit type;
inherit default;
inherit description;
};
}
];
};
in
evaledConfig.options.opt;
@@ -25,11 +29,7 @@ in
testNoDefaultNoDescription =
let
evaledConfig = lib.evalModules {
modules = [{
options.opt = lib.mkOption {
type = lib.types.bool;
};
}];
modules = [ { options.opt = lib.mkOption { type = lib.types.bool; }; } ];
};
in
{
@@ -42,15 +42,17 @@ in
testDescriptionIsAttrs =
let
evaledConfig = lib.evalModules {
modules = [{
options.opt = lib.mkOption {
type = lib.types.bool;
description = {
_type = "mdDoc";
text = description;
modules = [
{
options.opt = lib.mkOption {
type = lib.types.bool;
description = {
_type = "mdDoc";
text = description;
};
};
};
}];
}
];
};
in
{
@@ -112,7 +114,11 @@ in
testEnum =
let
default = "foo";
values = [ "foo" "bar" "baz" ];
values = [
"foo"
"bar"
"baz"
];
in
{
expr = slib.parseOption (evalType (lib.types.enum values) default);
@@ -124,7 +130,11 @@ in
testListOfInt =
let
default = [ 1 2 3 ];
default = [
1
2
3
];
in
{
expr = slib.parseOption (evalType (lib.types.listOf lib.types.int) default);
@@ -139,14 +149,26 @@ in
testListOfUnspecified =
let
default = [ 1 2 3 ];
default = [
1
2
3
];
in
{
expr = slib.parseOption (evalType (lib.types.listOf lib.types.unspecified) default);
expected = {
type = "array";
items = {
type = [ "boolean" "integer" "number" "string" "array" "object" "null" ];
type = [
"boolean"
"integer"
"number"
"string"
"array"
"object"
"null"
];
};
inherit default description;
};
@@ -154,7 +176,11 @@ in
testAttrs =
let
default = { foo = 1; bar = 2; baz = 3; };
default = {
foo = 1;
bar = 2;
baz = 3;
};
in
{
expr = slib.parseOption (evalType (lib.types.attrs) default);
@@ -167,7 +193,11 @@ in
testAttrsOfInt =
let
default = { foo = 1; bar = 2; baz = 3; };
default = {
foo = 1;
bar = 2;
baz = 3;
};
in
{
expr = slib.parseOption (evalType (lib.types.attrsOf lib.types.int) default);
@@ -182,7 +212,11 @@ in
testLazyAttrsOfInt =
let
default = { foo = 1; bar = 2; baz = 3; };
default = {
foo = 1;
bar = 2;
baz = 3;
};
in
{
expr = slib.parseOption (evalType (lib.types.lazyAttrsOf lib.types.int) default);
@@ -286,7 +320,10 @@ in
inherit description;
};
};
default = { foo.opt = false; bar.opt = true; };
default = {
foo.opt = false;
bar.opt = true;
};
in
{
expr = slib.parseOption (evalType (lib.types.attrsOf (lib.types.submodule subModule)) default);
@@ -315,7 +352,10 @@ in
inherit description;
};
};
default = [{ opt = false; } { opt = true; }];
default = [
{ opt = false; }
{ opt = true; }
];
in
{
expr = slib.parseOption (evalType (lib.types.listOf (lib.types.submodule subModule)) default);
@@ -358,7 +398,15 @@ in
expr = slib.parseOption (evalType lib.types.anything default);
expected = {
inherit default description;
type = [ "boolean" "integer" "number" "string" "array" "object" "null" ];
type = [
"boolean"
"integer"
"number"
"string"
"array"
"object"
"null"
];
};
};
@@ -370,7 +418,15 @@ in
expr = slib.parseOption (evalType lib.types.unspecified default);
expected = {
inherit default description;
type = [ "boolean" "integer" "number" "string" "array" "object" "null" ];
type = [
"boolean"
"integer"
"number"
"string"
"array"
"object"
"null"
];
};
};
@@ -382,7 +438,15 @@ in
expr = slib.parseOption (evalType lib.types.raw default);
expected = {
inherit default description;
type = [ "boolean" "integer" "number" "string" "array" "object" "null" ];
type = [
"boolean"
"integer"
"number"
"string"
"array"
"object"
"null"
];
};
};
}

View File

@@ -1,14 +1,13 @@
# tests for the nixos options to jsonschema converter
# run these tests via `nix-unit ./test.nix`
{ lib ? (import <nixpkgs> { }).lib
, slib ? import ./. { inherit lib; }
{
lib ? (import <nixpkgs> { }).lib,
slib ? import ./. { inherit lib; },
}:
let
evaledOptions =
let
evaledConfig = lib.evalModules {
modules = [ ./example-interface.nix ];
};
evaledConfig = lib.evalModules { modules = [ ./example-interface.nix ]; };
in
evaledConfig.options;
in
@@ -21,11 +20,7 @@ in
testParseNestedOptions =
let
evaled = lib.evalModules {
modules = [{
options.foo.bar = lib.mkOption {
type = lib.types.bool;
};
}];
modules = [ { options.foo.bar = lib.mkOption { type = lib.types.bool; }; } ];
};
in
{
@@ -34,7 +29,9 @@ in
properties = {
foo = {
properties = {
bar = { type = "boolean"; };
bar = {
type = "boolean";
};
};
required = [ "bar" ];
type = "object";