Skip to content

Commit

Permalink
alternate cat def merge function utils.deepmergeCats (#55)
Browse files Browse the repository at this point in the history
This one is capable of merging duplicate lists together. This allows for defining extensions to existing categories, rather than the only option being to completely replace with a new list

`utils.deepmergeCats` is the new merge function, used the same way as `utils.mergeCatDefs`

In addition, module options for categoryDefininions have been slightly changed.

`categoryDefinitions.replace` still uses `utils.mergeCatDefs` and merge now uses `utils.deepmergeCats`

Using `categoryDefinitions.replace` option no longer is what controls if definitions from the package the module was based on are inherited.

Instead, an extra option has been added, `categoryDefinitions.existing` to allow you to control the merge strategy used for merging with the existing definitions.
  • Loading branch information
BirdeeHub committed Oct 29, 2024
1 parent 765810b commit 4f1755b
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 77 deletions.
4 changes: 4 additions & 0 deletions nixCatsHelp/nixCats_format.txt
Original file line number Diff line number Diff line change
Expand Up @@ -887,6 +887,10 @@ For our purposes, we do not consider derivations to be attrsets.
It takes 2 functions that return sets and returns
a function which calls them both and merges the result as desired above.

<deepmergeCats> oldCats: newCats:
same as `utils.mergeCatDefs` except will merge duplicate
category lists together instead of replacing them

<mergeOverlayLists> oldOverlist: newOverlist: self: super:
for merging lists of overlays together properly
to avoid naming conflict issues.
Expand Down
43 changes: 23 additions & 20 deletions nixCatsHelp/nixCats_modules.txt
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ other than the packages themselves in config.${defaultPackageName}.out
A list of overlays to make available to nixCats but not to your system.
Will have access to system overlays regardless of this setting.
'';
example = (literalExpression ''
addOverlays = [ (self: super: { neovimPlugins = { pluginDerivationName = pluginDerivation; }; }) ]
example = (lib.literalExpression ''
addOverlays = [ (self: super: { vimPlugins = { pluginDerivationName = pluginDerivation; }; }) ]
'');
};

Expand Down Expand Up @@ -94,19 +94,25 @@ other than the packages themselves in config.${defaultPackageName}.out
};

categoryDefinitions = {
existing = mkOption {
default = "replace";
type = types.enum [ "replace" "merge" "discard" ];
description = ''
the merge strategy to use for categoryDefinitions inherited from the package this module was based on
choose between "replace", "merge" or "discard"
replace uses utils.mergeCatDefs
merge uses utils.deepmergeCats
discard does not inherit
see :help nixCats.flake.outputs.exports for more info on the merge strategy options
'';
};
replace = mkOption {
default = null;
type = types.nullOr catDef;
type = types.nullOr (catDef "replace");
description = (literalExpression ''
Takes a function that receives the package definition set of this package
and returns a set of categoryDefinitions,
just like :help nixCats.flake.outputs.categories
you should use ${pkgs.system} provided in the packageDef set
to access system specific items.

If any value is found for categoryDefinitions.replace,
any inherited values from the package this module was based on, if any, will be overwritten.
To merge with inherited values, use categoryDefinitions.merge instead, and do not set this value.
see :help nixCats.flake.outputs.categories
uses utils.mergeCatDefs to recursively update old categories with new values
see :help nixCats.flake.outputs.exports for more info on the merge strategy options
'');
example = ''
# see :help nixCats.flake.outputs.categories
Expand All @@ -115,14 +121,11 @@ other than the packages themselves in config.${defaultPackageName}.out
};
merge = mkOption {
default = null;
type = types.nullOr catDef;
type = types.nullOr (catDef "merge");
description = ''
Takes a function that receives the package definition set of this package
and returns a set of categoryDefinitions,
just like :help nixCats.flake.outputs.categories
Will merge the categoryDefinitions of the flake with this value,
recursively updating all non-attrset values,
such as replacing old category lists with ones defined here.
see :help nixCats.flake.outputs.categories
uses utils.deepmergeCats to recursively update and merge category lists if duplicates are defined
see :help nixCats.flake.outputs.exports for more info on the merge strategy options
'';
example = ''
# see :help nixCats.flake.outputs.categories
Expand All @@ -145,7 +148,7 @@ other than the packages themselves in config.${defaultPackageName}.out
see :help nixCats.flake.outputs.settings
and :help nixCats.flake.outputs.categories
'';
type = with types; nullOr (attrsOf catDef);
type = with types; nullOr (attrsOf (catDef "replace"));
example = ''
nixCats.packages = {
nixCats = { pkgs, ... }: {
Expand Down
56 changes: 54 additions & 2 deletions utils/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ with builtins; rec {
mergeCatDefs = oldCats: newCats:
(packageDef: lib.recursiveUpdateUntilDRV (oldCats packageDef) (newCats packageDef));

# allows category list definitions to be merged
deepmergeCats = oldCats: newCats:
(packageDef: lib.recursiveUpdateWithMerge (oldCats packageDef) (newCats packageDef));

# recursiveUpdate each overlay output to avoid issues where
# two overlays output a set of the same name when importing from other nixCats.
# Merges everything into 1 overlay
Expand Down Expand Up @@ -193,6 +197,7 @@ with builtins; rec {
(import ./mkModules.nix {
isHomeManager = false;
oldDependencyOverlays = dependencyOverlays;
my_lib = lib;
inherit nixpkgs luaPath keepLuaBuilder categoryDefinitions
packageDefinitions defaultPackageName extra_pkg_config utils;
});
Expand All @@ -210,6 +215,7 @@ with builtins; rec {
(import ./mkModules.nix {
isHomeManager = true;
oldDependencyOverlays = dependencyOverlays;
my_lib = lib;
inherit nixpkgs luaPath keepLuaBuilder categoryDefinitions
packageDefinitions defaultPackageName extra_pkg_config utils;
});
Expand Down Expand Up @@ -323,12 +329,42 @@ with builtins; rec {
);
in f [] [rhs lhs];

recursiveUpdateUntilDRV = lhs: rhs:
recursiveUpdateUntilDRV = left: right:
lib.recursiveUpdateUntil (path: lhs: rhs:
# I added this check for derivation because a category can be just a derivation.
# otherwise it would squish our single derivation category rather than update.
(!((isAttrs lhs && !lib.isDerivation lhs) && (isAttrs rhs && !lib.isDerivation rhs)))
) lhs rhs;
) left right;

unique = foldl' (acc: e: if elem e acc then acc else acc ++ [ e ]) [];

recUpdateUntilWithMerge = pred: lhs: rhs:
let
mergefunc = left: right:
if isList left && isList right
then lib.unique (left ++ right)
else if isList left && all (lv: typeOf lv == typeOf right) left then
if elem right left then left else left ++ [ right ]
else if isList right && all (rv: typeOf rv == typeOf left) right then
if elem left right then right else [ left ] ++ right
else right;
f = attrPath:
zipAttrsWith (n: values:
let here = attrPath ++ [n]; in
if length values == 1
|| pred here (elemAt values 1) (head values) then
mergefunc (elemAt values 1) (head values)
else
f here values
);
in f [] [rhs lhs];

recursiveUpdateWithMerge = left: right:
lib.recUpdateUntilWithMerge (path: lhs: rhs:
# I added this check for derivation because a category can be just a derivation.
# otherwise it would squish our single derivation category rather than update.
(!((isAttrs lhs && !lib.isDerivation lhs) && (isAttrs rhs && !lib.isDerivation rhs)))
) left right;

genAttrs =
names:
Expand Down Expand Up @@ -398,6 +434,22 @@ with builtins; rec {
else f (path ++ [ name ]) value);
in
recurse [ ] set;

mkCatDefType = mkOptionType: subtype: mkOptionType {
name = "catDef";
description = "a function representing categoryDefinitions or packageDefinitions for nixCats";
descriptionClass = "noun";
check = v: isFunction v;
merge = loc: defs: let
values = map (v: v.value) defs;
mergefunc = if subtype == "replace"
then lib.recursiveUpdateUntilDRV
else if subtype == "merge"
then lib.recursiveUpdateWithMerge
else throw "invalid catDef subtype";
in
arg: foldl' mergefunc {} (map (v: v arg) values);
};
};
}

112 changes: 57 additions & 55 deletions utils/mkModules.nix
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,13 @@
, categoryDefinitions ? (_:{})
, packageDefinitions ? {}
, utils
, my_lib
, nixpkgs ? null
, extra_pkg_config ? {}
, ...
}:
{ config, pkgs, lib, ... }: let
catDef = lib.mkOptionType {
name = "catDef";
description = "a function representing categoryDefinitions or a package definition in the packageDefinitions set for nixCats";
descriptionClass = "noun";
check = v: builtins.isFunction v;
merge = loc: defs: let
recursiveUpdateUntilDRV = lhs: rhs:
lib.recursiveUpdateUntil (path: lhs: rhs:
(!((builtins.isAttrs lhs && !lib.isDerivation lhs) && (builtins.isAttrs rhs && !lib.isDerivation rhs)))
) lhs rhs;
values = map (v: v.value) defs;
in
arg: builtins.foldl' recursiveUpdateUntilDRV {} (map (v: v arg) values);
};
catDef = my_lib.mkCatDefType lib.mkOptionType;
in {

options = with lib; {
Expand Down Expand Up @@ -83,19 +71,25 @@ in {
};

categoryDefinitions = {
existing = mkOption {
default = "replace";
type = types.enum [ "replace" "merge" "discard" ];
description = ''
the merge strategy to use for categoryDefinitions inherited from the package this module was based on
choose between "replace", "merge" or "discard"
replace uses utils.mergeCatDefs
merge uses utils.deepmergeCats
discard does not inherit
see :help nixCats.flake.outputs.exports for more info on the merge strategy options
'';
};
replace = mkOption {
default = null;
type = types.nullOr catDef;
type = types.nullOr (catDef "replace");
description = (literalExpression ''
Takes a function that receives the package definition set of this package
and returns a set of categoryDefinitions,
just like :help nixCats.flake.outputs.categories
you should use ${pkgs.system} provided in the packageDef set
to access system specific items.
If any value is found for categoryDefinitions.replace,
any inherited values from the package this module was based on, if any, will be overwritten.
To merge with inherited values, use categoryDefinitions.merge instead, and do not set this value.
see :help nixCats.flake.outputs.categories
uses utils.mergeCatDefs to recursively update old categories with new values
see :help nixCats.flake.outputs.exports for more info on the merge strategy options
'');
example = ''
# see :help nixCats.flake.outputs.categories
Expand All @@ -104,14 +98,11 @@ in {
};
merge = mkOption {
default = null;
type = types.nullOr catDef;
type = types.nullOr (catDef "merge");
description = ''
Takes a function that receives the package definition set of this package
and returns a set of categoryDefinitions,
just like :help nixCats.flake.outputs.categories
Will merge the categoryDefinitions of the flake with this value,
recursively updating all non-attrset values,
such as replacing old category lists with ones defined here.
see :help nixCats.flake.outputs.categories
uses utils.deepmergeCats to recursively update and merge category lists if duplicates are defined
see :help nixCats.flake.outputs.exports for more info on the merge strategy options
'';
example = ''
# see :help nixCats.flake.outputs.categories
Expand All @@ -134,7 +125,7 @@ in {
see :help nixCats.flake.outputs.settings
and :help nixCats.flake.outputs.categories
'';
type = with types; nullOr (attrsOf catDef);
type = with types; nullOr (attrsOf (catDef "replace"));
example = ''
nixCats.packages = {
nixCats = { pkgs, ... }: {
Expand Down Expand Up @@ -223,19 +214,25 @@ in {
};

categoryDefinitions = {
existing = mkOption {
default = "replace";
type = types.enum [ "replace" "merge" "discard" ];
description = ''
the merge strategy to use for categoryDefinitions inherited from the package this module was based on
choose between "replace", "merge" or "discard"
replace uses utils.mergeCatDefs
merge uses utils.deepmergeCats
discard does not inherit
see :help nixCats.flake.outputs.exports for more info on the merge strategy options
'';
};
replace = mkOption {
default = null;
type = types.nullOr catDef;
type = types.nullOr (catDef "replace");
description = ''
Takes a function that receives the package definition set of this package
and returns a set of categoryDefinitions,
just like :help nixCats.flake.outputs.categories
you should use ''${pkgs.system} provided in the packageDef set
to access system specific items.
If any value is found for categoryDefinitions.replace,
any inherited values from the package this module was based on, if any, will be overwritten.
To merge with inherited values, use categoryDefinitions.merge instead, and do not set this value.
see :help nixCats.flake.outputs.categories
uses utils.mergeCatDefs to recursively update old categories with new values
see :help nixCats.flake.outputs.exports for more info on the merge strategy options
'';
example = ''
# see :help nixCats.flake.outputs.categories
Expand All @@ -244,14 +241,11 @@ in {
};
merge = mkOption {
default = null;
type = types.nullOr catDef;
type = types.nullOr (catDef "merge");
description = ''
Takes a function that receives the package definition set of this package
and returns a set of categoryDefinitions,
just like :help nixCats.flake.outputs.categories
Will merge the categoryDefinitions of the flake with this value,
recursively updating all non-attrset values,
such as replacing old category lists with ones defined here.
see :help nixCats.flake.outputs.categories
uses utils.deepmergeCats to recursively update and merge category lists if duplicates are defined
see :help nixCats.flake.outputs.exports for more info on the merge strategy options
'';
example = ''
# see :help nixCats.flake.outputs.categories
Expand All @@ -274,7 +268,7 @@ in {
see :help nixCats.flake.outputs.settings
and :help nixCats.flake.outputs.categories
'';
type = with types; nullOr (attrsOf catDef);
type = with types; nullOr (attrsOf (catDef "replace"));
example = ''
nixCats.packages = {
nixCats = { pkgs, ... }: {
Expand Down Expand Up @@ -317,11 +311,19 @@ in {
else pkgs.overlays ++ options_set.addOverlays;

mapToPackages = options_set: dependencyOverlays: (let
newCategoryDefinitions = utils.mergeCatDefs (
if options_set.categoryDefinitions.replace != null then options_set.categoryDefinitions.replace else categoryDefinitions
) (
if options_set.categoryDefinitions.merge != null then options_set.categoryDefinitions.merge else (_:{})
);
newCategoryDefinitions = let
stratWithExisting = if options_set.categoryDefinitions.existing == "merge"
then utils.deepmergeCats
else if options_set.categoryDefinitions.existing == "replace"
then utils.mergeCatDefs
else (_: r: r);

moduleCatDefs = utils.deepmergeCats (
if options_set.categoryDefinitions.replace != null then options_set.categoryDefinitions.replace else (_:{})
) (
if options_set.categoryDefinitions.merge != null then options_set.categoryDefinitions.merge else (_:{})
);
in stratWithExisting categoryDefinitions moduleCatDefs;

pkgDefs = if (options_set.packages != null)
then packageDefinitions // options_set.packages else packageDefinitions;
Expand Down

0 comments on commit 4f1755b

Please sign in to comment.