Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions modules/aspects/dependencies.nix
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,6 @@ let
(statics den.default)
(owned HM)
(statics HM)
(owned OS)
(statics OS)
(parametric.fixedTo context OS)
];
};
Expand Down
3 changes: 2 additions & 1 deletion nix/fn-can-take.nix
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ let
check =
params: func:
let
givenFn = builtins.isFunction func || (builtins.isAttrs func && func ? __functor);
givenArgs = builtins.isAttrs params;
fargs = lib.functionArgs func;
provided = builtins.attrNames params;
args = lib.mapAttrsToList (name: optional: { inherit name optional; }) fargs;
required = map (x: x.name) (lib.filter (x: !x.optional) args);
intersection = lib.intersectLists required provided;
satisfied = givenArgs && lib.length required == lib.length intersection;
satisfied = givenFn && givenArgs && lib.length required == lib.length intersection;
noExtras = lib.length required == lib.length provided;
exactly = satisfied && noExtras;
in
Expand Down
58 changes: 28 additions & 30 deletions nix/lib.nix
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ let
};
};

isFn = f: (builtins.isFunction f) || (f ? __functor);
isFn = f: (builtins.isFunction f) || (builtins.isAttrs f && f ? __functor);
canTake = import ./fn-can-take.nix lib;

# an aspect producing only owned configs
Expand All @@ -33,9 +33,10 @@ let
// {
__functor =
self:
# deadnix: skip
{ class, aspect-chain }@ctx:
funk applyStatics self ctx;
{ class, aspect-chain }:
{
includes = map (applyStatics { inherit class aspect-chain; }) self.includes;
};
};

applyStatics =
Expand All @@ -51,10 +52,7 @@ let
class = "";
aspect-chain = [ ];
};
isCtxStatic = (lib.flip canTake.exactly) (
# deadnix: skip
{ class, aspect-chain }: true
);
isCtxStatic = (lib.flip canTake.exactly) ({ class, aspect-chain }: class aspect-chain);

take.unused = _unused: used: used;
take.exactly = take canTake.exactly;
Expand All @@ -66,39 +64,39 @@ let
parametric.atLeast = funk (lib.flip take.atLeast);
parametric.exactly = funk (lib.flip take.exactly);
parametric.expands =
attrs: aspect: ctx:
parametric.fixedTo (ctx // attrs) aspect;
attrs: parametric.withOwn (aspect: ctx: parametric.atLeast aspect (ctx // attrs));
parametric.fixedTo =
ctx: aspect:
{ class, aspect-chain }:
{
includes = [
(parametric.atLeast aspect ctx)
(parametricStatics aspect { inherit class aspect-chain; })
];
attrs: aspect:
aspect
// {
__functor =
self:
{ class, aspect-chain }:
{
includes = [
(owned self)
(statics self { inherit class aspect-chain; })
(parametric.atLeast self attrs)
];
};
};
parametric.withOwn =
functor: aspect:
aspect
// {
__functor = self: ctx: {
includes = [
(functor self ctx)
(parametricStatics self ctx)
];
includes =
if isCtxStatic ctx then
[
(owned self)
(statics self ctx)
]
else
[ (functor self ctx) ];
};
};
parametric.__functor = _: parametric.withOwn parametric.atLeast;

parametricStatics = self: ctx: {
includes = lib.optionals (isCtxStatic ctx) [
(owned self)
{
includes = map (applyStatics ctx) self.includes;
}
];
};

aspects = inputs.flake-aspects.lib lib;

__findFile = import ./den-brackets.nix { inherit lib config; };
Expand Down
72 changes: 64 additions & 8 deletions templates/examples/modules/_example/ci/parametric-with-owned.nix
Original file line number Diff line number Diff line change
@@ -1,28 +1,36 @@
{ den, lib, ... }:
{
den,
lib,
...
}:
let
# a test module to check context was forwarded
fwdModule.nixos.options.fwd = {
fwdModule.options.fwd = {
a = strOpt;
b = strOpt;
c = strOpt;
d = strOpt;
e = strOpt;
f = strOpt;
# unlike strings, pkgs cannot be duplicated, we use this to
# unlike strings, pkgs cannot be duplicated/merged, we use this to
# ensure no-dups are created from parametric owned modules.
pkg = lib.mkOption { type = lib.types.package; };
pkg = pkgOpt;
pkg2 = pkgOpt;
pkg3 = pkgOpt;
};
strOpt = lib.mkOption { type = lib.types.str; };
pkgOpt = lib.mkOption { type = lib.types.package; };

inherit (den.lib) parametric;
in
{

den.aspects.rockhopper.includes = [
fwdModule
{ nixos.imports = [ fwdModule ]; }
{ homeManager.imports = [ fwdModule ]; }
den.aspects.fwd._.first
];
den.aspects.rockhopper.nixos.fwd.c = "host owned C";
den.aspects.rockhopper.homeManager.fwd.a = "host home-managed A";

# this aspect will take any context and also forward it
# into any includes function that can take same context.
Expand All @@ -33,6 +41,11 @@ in
fwd.a = "First owned A";
fwd.pkg = pkgs.hello;
};
homeManager =
{ pkgs, ... }:
{
fwd.pkg = builtins.break pkgs.vim;
Copy link

Copilot AI Nov 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The builtins.break function is being used here, which will cause evaluation to stop. This appears to be a debugging artifact that should be removed. The line should be fwd.pkg = pkgs.vim; instead.

Suggested change
fwd.pkg = builtins.break pkgs.vim;
fwd.pkg = pkgs.vim;

Copilot uses AI. Check for mistakes.
};
includes = [
den.aspects.fwd._.second
{ nixos.fwd.d = "First static includes D"; }
Expand All @@ -46,8 +59,18 @@ in
den.aspects.fwd._.second =
{ host, ... }:
parametric.fixedTo { third = "Impact"; } {
nixos.fwd.b = "Second owned B for ${host.name}";
includes = [ den.aspects.fwd._.third ];
nixos =
{ pkgs, ... }:
{
fwd.b = "Second owned B for ${host.name}";
fwd.pkg2 = pkgs.bat;
};
homeManager =
{ pkgs, ... }:
{
fwd.pkg2 = pkgs.helix;
};
};

den.aspects.fwd._.third =
Expand All @@ -58,6 +81,16 @@ in

den.aspects.fwd._.fourth = parametric.expands { planet = "Earth"; } {
includes = [ den.aspects.fwd._.fifth ];
nixos =
{ pkgs, ... }:
{
fwd.pkg3 = pkgs.emacs-nox;
};
homeManager =
{ pkgs, ... }:
{
fwd.pkg3 = pkgs.emacs-nox;
};
};

den.aspects.fwd._.fifth =
Expand All @@ -73,7 +106,12 @@ in
};

perSystem =
{ checkCond, rockhopper, ... }:
{
checkCond,
rockhopper,
alice-at-rockhopper,
...
}:
{
checks.parametric-fwd-a = checkCond "fwd-a" (rockhopper.config.fwd.a == "First owned A");
checks.parametric-fwd-b = checkCond "fwd-b" (
Expand All @@ -83,7 +121,25 @@ in
checks.parametric-fwd-d = checkCond "fwd-d" (rockhopper.config.fwd.d == "First static includes D");
checks.parametric-fwd-e = checkCond "fwd-e" (rockhopper.config.fwd.e == "Third Impact");
checks.parametric-fwd-f = checkCond "fwd-f" (rockhopper.config.fwd.f == "Fifth Earth rockhopper");

checks.parametric-fwd-pkg = checkCond "fwd-pkg" (lib.getName rockhopper.config.fwd.pkg == "hello");
checks.parametric-fwd-pkg2 = checkCond "fwd-pkg2" (lib.getName rockhopper.config.fwd.pkg2 == "bat");
checks.parametric-fwd-pkg3 = checkCond "fwd-pkg3" (
lib.getName rockhopper.config.fwd.pkg3 == "emacs-nox"
);

checks.parametric-fwd-hm-a = checkCond "fwd-hm-a" (
alice-at-rockhopper.fwd.a == "host home-managed A"
);
checks.parametric-fwd-hm-pkg = checkCond "fwd-hm-pkg" (
lib.getName alice-at-rockhopper.fwd.pkg == "vim"
);
checks.parametric-fwd-hm-pkg2 = checkCond "fwd-hm-pkg2" (
lib.getName alice-at-rockhopper.fwd.pkg2 == "helix"
);
checks.parametric-fwd-hm-pkg3 = checkCond "fwd-hm-pkg3" (
lib.getName alice-at-rockhopper.fwd.pkg3 == "emacs-nox"
);
};

}