diff --git a/Justfile b/Justfile new file mode 100644 index 0000000..8588f0e --- /dev/null +++ b/Justfile @@ -0,0 +1,14 @@ +help: + just -l + +docs: + cd docs && pnpm run dev + +ci test="": + nix-unit --override-input target . --flake github:vic/checkmate#.tests.systems.x86_64-linux.system-agnostic.{{test}} + +check: + nix flake check --override-input target . github:vic/checkmate + +fmt: + nix run github:vic/checkmate#fmt --override-input target . diff --git a/checkmate/modules/formatter.nix b/checkmate/modules/formatter.nix index c5348fb..ee77886 100644 --- a/checkmate/modules/formatter.nix +++ b/checkmate/modules/formatter.nix @@ -1,4 +1,7 @@ { perSystem.treefmt.programs.nixf-diagnose.enable = false; - perSystem.treefmt.settings.global.excludes = [ "docs/*" ]; + perSystem.treefmt.settings.global.excludes = [ + "docs/*" + "Justfile" + ]; } diff --git a/nix/forward.nix b/nix/forward.nix index 7fa8e9a..66459b6 100644 --- a/nix/forward.nix +++ b/nix/forward.nix @@ -1,4 +1,3 @@ -lib: # An utility for creating new aspect configuration classes that # help with separation of concerns. # @@ -39,6 +38,7 @@ lib: # # See checkmate/modules/tests/forward.nix for working example. # +lib: { each, fromClass, @@ -47,6 +47,7 @@ lib: fromAspect, }: let + resolve = import ./resolve.nix lib; include = item: let @@ -54,7 +55,7 @@ let into = intoClass item; path = intoPath item; aspect = fromAspect item; - module = aspect.resolve { class = from; }; + module = resolve from [ ] aspect; config = lib.setAttrByPath path ( { ... }: { diff --git a/nix/lib.nix b/nix/lib.nix index 58b03fc..a598371 100644 --- a/nix/lib.nix +++ b/nix/lib.nix @@ -1,27 +1,15 @@ -# Public API entry point for flake-aspects library -# Exports: types, transpose, aspects, new, new-scope lib: let - # Type system: aspectsType, aspectSubmodule, providerType types = import ./types.nix lib; - - # Generic transposition utility: parameterized by emit function + resolve = import ./resolve.nix lib; transpose = { emit ? lib.singleton, }: import ./default.nix { inherit lib emit; }; - - # Aspect transposition with resolution aspects = import ./aspects.nix lib; - - # Dynamic class forwarding into submodules forward = import ./forward.nix lib; - - # Low-level scope factory: parameterized by callback new = import ./new.nix lib; - - # High-level named scope factory new-scope = import ./new-scope.nix new; in { @@ -32,5 +20,6 @@ in new new-scope forward + resolve ; } diff --git a/nix/resolve.nix b/nix/resolve.nix index cab0660..857d6e1 100644 --- a/nix/resolve.nix +++ b/nix/resolve.nix @@ -1,18 +1,13 @@ -# Core aspect resolution algorithm -# Resolves aspect definitions into nixpkgs modules with dependency resolution - lib: let - # Process a single provider: invoke with context and resolve include = class: aspect-chain: provider: let - provided = provider { inherit aspect-chain class; }; + provided = if lib.isFunction provider then provider { inherit aspect-chain class; } else provider; in - resolve class aspect-chain provided; + inner class aspect-chain provided; - # Main resolution: extract class config and recursively resolve includes - resolve = class: aspect-chain: provided: { + inner = class: aspect-chain: provided: { imports = let config = provided.${class} or { }; @@ -24,5 +19,18 @@ let ]; }; + resolve = + class: aspect-chain: aspect: + let + provided = + if lib.isFunction aspect then + aspect { + inherit class; + aspect-chain = aspect-chain; + } + else + aspect; + in + inner class aspect-chain provided; in resolve diff --git a/nix/types.nix b/nix/types.nix index 454b998..3c2b1a2 100644 --- a/nix/types.nix +++ b/nix/types.nix @@ -1,10 +1,7 @@ -# Core type system for aspect-oriented configuration - lib: let resolve = import ./resolve.nix lib; - # Type for computed values that only exist during evaluation ignoredType = lib.types.mkOptionType { name = "ignored type"; description = "ignored values"; @@ -24,48 +21,39 @@ let apply = fn; }; - # Like lib.types.functionTo, but it does not merges all definitions, and keeps - # just the last one. - functorType = lib.mkOptionType { + functorType = lib.types.mkOptionType { name = "aspectFunctor"; description = "aspect functor function"; check = lib.isFunction; merge = - loc: defs: + _loc: defs: let - # Use only the last definition to avoid duplication from - # functionTo merging all definitions with the same args. - # All definitions receive the same merged `self`, so they - # produce equivalent results - picking one is correct. lastDef = lib.last defs; - innerType = providerType; in { __functionArgs = lib.functionArgs lastDef.value; __functor = _: callerArgs: - (lib.modules.mergeDefinitions (loc ++ [ "" ]) innerType [ - { - inherit (lastDef) file; - value = lastDef.value callerArgs; - } - ]).mergedValue; + let + result = lastDef.value callerArgs; + in + if builtins.isFunction result then result else _: result; }; }; - # Check if function has submodule-style arguments isSubmoduleFn = m: - lib.length ( - lib.intersectLists [ "lib" "config" "options" "aspect" ] (lib.attrNames (lib.functionArgs m)) - ) > 0; + let + args = lib.functionArgs m; + in + args ? lib || args ? config || args ? options || args ? aspect; # Check if function accepts { class } and/or { aspect-chain } isProviderFn = f: let args = lib.functionArgs f; - n = lib.length (lib.attrNames args); + n = builtins.length (builtins.attrNames args); in (args ? class && n == 1) || (args ? aspect-chain && n == 1)