feat: add portable submodule type #11

Open
jakehamilton wants to merge 1 commit from feat/portable-submodules into main
2 changed files with 159 additions and 1 deletions

View file

@ -916,6 +916,117 @@ lib: {
}; };
}; };
}; };
## Create a type from a submodule which can be used in multiple parts of the configuration.
##
## @type { module :: Module, name? :: String, description? :: String } -> Attrs
portable =
{ name ? "PortableSubmodule"
, description ? "portable submodule"
, module
,
}:
let
normalize =
value:
if builtins.isFunction value || builtins.isList value then
value
else if value ? __modules__ then
value.__modules__
else
{ config = value; };
initial = lib.types.create {
name = "PortableSubmoduleInitial";
description = "initial portable submodule value";
check = value: builtins.isFunction value || builtins.isAttrs value || builtins.isList value;
merge =
location: definitions:
let
normalized = builtins.map (definition: lib.lists.from.any (normalize definition.value)) definitions;
in
builtins.concatLists normalized;
};
base = { config }: {
options = {
__modules__ = lib.options.create {
description = "User specified modules to be evaluated.";
type = lib.types.list.of (initial // { merge = lib.options.merge.one; });
internal = true;
default.value = [ ];
};
extend = lib.options.create {
description = "Extend the submodule configuration.";
type = lib.types.function lib.types.raw;
internal = true;
writable = false;
default.value = module:
let
normalized =
if builtins.isList module then module
else if builtins.isFunction module || module ? config then
[ module ]
else [{ config = module; }];
modules = config.__modules__ ++ normalized;
result = lib.modules.run {
modules = [
module
base
{ config.__modules__ = modules; }
] ++ modules;
};
in
result.config;
};
};
};
transform = location: value:
let
modules = lib.lists.from.any (normalize value);
result = lib.modules.run {
prefix = location;
modules = [
module
base
{ config.__modules__ = modules; }
] ++ modules;
};
in
result.config;
final = lib.types.raw // {
merge = location: definitions:
let
modules = lib.lists.flatten (
builtins.map (definition: normalize definition.value) definitions
);
result = lib.modules.run {
prefix = location;
modules = [
module
base
{ config.__modules__ = modules; }
] ++ modules;
};
in
result.config;
};
type = lib.types.coerceWithLocation initial transform final;
in
type // {
inherit name description;
children = type.children // {
inherit module base;
};
};
}; };
deferred = { deferred = {

View file

@ -1,4 +1,51 @@
let let
lib = import ./../default.nix; lib = import ./../default.nix;
in in
{ } {
"PortableSubmodule" = {
"is portable" =
let
submodule = { config }: {
options = {
primitive = lib.options.create {
type = lib.types.string;
default.value = "<default>";
};
computed = lib.options.create {
type = lib.types.string;
default.value = "computed: ${config.primitive}";
};
};
};
option = lib.options.create {
default.value = { };
type = lib.types.submodules.portable {
module = submodule;
};
};
moduleA = {
options.a = option;
};
moduleB = { config }: {
options.b = option;
config.b = config.a;
};
moduleC = {
config.b.primitive = "custom";
};
result = lib.modules.run {
modules = [
moduleA
moduleB
moduleC
];
};
in
result.config.b.computed == "computed: custom";
};
}