From feb9987300c9a0239e7825152b6ef60363aab53f Mon Sep 17 00:00:00 2001 From: Austreelis Date: Sat, 22 Jun 2024 15:47:39 +0200 Subject: [PATCH] Use include object syntax instead of shorthand --- lib/src/modules/default.nix | 80 ++++++++++++++++++++++---------- lib/src/modules/default.test.nix | 52 ++++++++++++--------- 2 files changed, 86 insertions(+), 46 deletions(-) diff --git a/lib/src/modules/default.nix b/lib/src/modules/default.nix index 487424b..131175e 100644 --- a/lib/src/modules/default.nix +++ b/lib/src/modules/default.nix @@ -142,6 +142,18 @@ lib: { invalid = builtins.removeAttrs module lib.modules.VALID_KEYS; in invalid == { }; + + ## Check that an include object has the required attributes and otherwise + ## only specifies supported attributes. + ## + ## @type (Attrs | String | Path | Function) -> Bool + include = + include: + let + invalidKeys = builtins.removeAttrs include lib.modules.VALID_INCLUDE_KEYS; + namespaceIsString = include ? namespace -> builtins.isString include.namespace; + in + builtins.isAttrs include -> (include ? module && namespaceIsString && invalidKeys == { }); }; ## Modules only support certain keys at the root level. This list determines @@ -159,6 +171,15 @@ lib: { "meta" ]; + ## Include objects only support certain keys. This list determines the + ## valid attributes that users can supply. + ## + ## @type List String + VALID_INCLUDE_KEYS = [ + "module" + "namespace" + ]; + ## Normalize a module to a standard structure. All other information will be ## lost in the conversion. ## @@ -168,48 +189,59 @@ lib: { let invalid = builtins.removeAttrs module lib.modules.VALID_KEYS; invalidKeys = builtins.concatStringsSep ", " (builtins.attrNames invalid); + throwError = msg: builtins.throw ("Module ${key} (${file}) " + msg); __key__ = builtins.toString module.__key__ or key; normalizeIncludes = includes: let - flattened = builtins.concatMap ( - include: - if - builtins.isAttrs include - && include != { } - && !builtins.any (n: builtins.elem n lib.modules.VALID_KEYS) (builtins.attrNames include) - then - lib.attrs.mapToList (namespace: module: { - inherit namespace; - include = module; - }) include - else - [ + normalized = lib.lists.mapWithIndex1 ( + n: include: + let + invalid = builtins.removeAttrs module lib.modules.VALID_INCLUDE_KEYS; + invalidKeys = builtins.concatStringsSep ", " (builtins.attrNames invalid); + hasInvalidKeys = invalid != { }; + throwError' = msg: throwError ("has invalid include at position ${builtins.toString n}" + msg); + in + if lib.modules.validate.include include then + if builtins.isAttrs include then { - inherit include; + inherit (include) module; + namespace = include.namespace or null; + } + else + { + module = include; namespace = null; } - ] - ) (lib.lists.when (includes != { }) includes); + else if !include ? module then + { + module = include; + namespace = null; + } + else if hasInvalidKeys then + throwError' " with unsupported attribute(s): ${invalidKeys}" + else + throwError' ": namespace is not a string" + ) includes; throwOnConflict = let filter = namespaces: - { namespace, include }: + { namespace, ... }: if namespace != null && builtins.elem namespace namespaces then - builtins.throw "Module ${key} declares several includes under the same namespace '${namespace}'" + throwError "declares several includes under the same namespace '${namespace}'" else namespaces ++ [ namespace ]; in - builtins.foldl' filter [ ] flattened; + builtins.foldl' filter [ ] normalized; createNamespacedModule = - { namespace, include }: + { namespace, module, ... }: if namespace == null then - include + module else { __key__ = "${__key__}:include-${namespace}"; @@ -217,13 +249,13 @@ lib: { description = "options and configuration included from ${namespace}"; default.value = { }; type = lib.types.submodules.of { - modules = [ include ]; + modules = [ module ]; description = "include ${namespace}"; }; }; }; - namespacedModules = builtins.map createNamespacedModule flattened; + namespacedModules = builtins.map createNamespacedModule normalized; in builtins.seq throwOnConflict namespacedModules; in @@ -259,7 +291,7 @@ lib: { withFreeform (withMeta base); } else - builtins.throw "Module `${key}` (${file}) has unsupported attribute(s): ${invalidKeys}"; + throwError "has unsupported attribute(s): ${invalidKeys}"; ## Convert a module that is either a function or an attribute set into ## a resolved attribute set. If the module was a function then it will diff --git a/lib/src/modules/default.test.nix b/lib/src/modules/default.test.nix index bbe82db..d05d4b7 100644 --- a/lib/src/modules/default.test.nix +++ b/lib/src/modules/default.test.nix @@ -246,28 +246,31 @@ in in expected == actual; - "handles an empty set" = - let - expected = [ ]; - actual = (lib.modules.normalize "/aux/example.nix" "example" { includes = { }; }).includes; - in - expected == actual; - "handles a mixed list" = let expected = [ - { } + null { a = null; } ]; actual = # Because includes leverage submodules, we can't match the actual # included namespaced submodule under "a". So we just assert the # namespace was gotten right and do not evaluate the included value. - builtins.map (include: builtins.mapAttrs (_: _: null) include.options or include) + builtins.map + ( + include: + if builtins.isAttrs include then + builtins.mapAttrs (_: _: null) include.options or include + else + include + ) (lib.modules.normalize "/aux/example.nix" "example" { includes = [ - { } - { a = null; } + null + { + module = null; + namespace = "a"; + } ]; }).includes; in @@ -277,8 +280,14 @@ in let normalized = lib.modules.normalize "/aux/example.nix" "example" { includes = [ - { a = { }; } - { a = { }; } + { + module = null; + namespace = "a"; + } + { + module = null; + namespace = "a"; + } ]; }; in @@ -288,8 +297,8 @@ in let normalized = lib.modules.normalize "/aux/example.nix" "example" { includes = [ - { } - { } + null + null ]; }; in @@ -298,14 +307,14 @@ in "handles multiple without namespace" = let expected = [ - { } - { } + null + null ]; actual = (lib.modules.normalize "/aux/example.nix" "example" { includes = [ - { } - { } + null + null ]; }).includes; in @@ -786,9 +795,8 @@ in { includes = [ { - myinclude = { - options.msg = lib.options.create { default.value = "hello"; }; - }; + namespace = "myinclude"; + module.options.msg = lib.options.create { default.value = "hello"; }; } { options.msg = lib.options.create { default.value = "hello"; }; } ];