Merge pull request #110787 from tfc/cartesian-product
lib/attrsets: add cartesianProductOfSets function
This commit is contained in:
commit
aa48e205a2
|
@ -1711,4 +1711,43 @@ recursiveUpdate
|
||||||
</example>
|
</example>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section xml:id="function-library-lib.attrsets.cartesianProductOfSets">
|
||||||
|
<title><function>lib.attrsets.cartesianProductOfSets</function></title>
|
||||||
|
|
||||||
|
<subtitle><literal>cartesianProductOfSets :: AttrSet -> [ AttrSet ]</literal>
|
||||||
|
</subtitle>
|
||||||
|
|
||||||
|
<xi:include href="./locations.xml" xpointer="lib.attrsets.cartesianProductOfSets" />
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Return the cartesian product of attribute set value combinations.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<variablelist>
|
||||||
|
<varlistentry>
|
||||||
|
<term>
|
||||||
|
<varname>set</varname>
|
||||||
|
</term>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
An attribute set with attributes that carry lists of values.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
</variablelist>
|
||||||
|
|
||||||
|
<example xml:id="function-library-lib.attrsets.cartesianProductOfSets-example">
|
||||||
|
<title>Creating the cartesian product of a list of attribute values</title>
|
||||||
|
<programlisting><![CDATA[
|
||||||
|
cartesianProductOfSets { a = [ 1 2 ]; b = [ 10 20 ]; }
|
||||||
|
=> [
|
||||||
|
{ a = 1; b = 10; }
|
||||||
|
{ a = 1; b = 20; }
|
||||||
|
{ a = 2; b = 10; }
|
||||||
|
{ a = 2; b = 20; }
|
||||||
|
]
|
||||||
|
]]></programlisting>
|
||||||
|
</example>
|
||||||
|
</section>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -183,6 +183,24 @@ rec {
|
||||||
else
|
else
|
||||||
[];
|
[];
|
||||||
|
|
||||||
|
/* Return the cartesian product of attribute set value combinations.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
cartesianProductOfSets { a = [ 1 2 ]; b = [ 10 20 ]; }
|
||||||
|
=> [
|
||||||
|
{ a = 1; b = 10; }
|
||||||
|
{ a = 1; b = 20; }
|
||||||
|
{ a = 2; b = 10; }
|
||||||
|
{ a = 2; b = 20; }
|
||||||
|
]
|
||||||
|
*/
|
||||||
|
cartesianProductOfSets = attrsOfLists:
|
||||||
|
lib.foldl' (listOfAttrs: attrName:
|
||||||
|
concatMap (attrs:
|
||||||
|
map (listValue: attrs // { ${attrName} = listValue; }) attrsOfLists.${attrName}
|
||||||
|
) listOfAttrs
|
||||||
|
) [{}] (attrNames attrsOfLists);
|
||||||
|
|
||||||
|
|
||||||
/* Utility function that creates a {name, value} pair as expected by
|
/* Utility function that creates a {name, value} pair as expected by
|
||||||
builtins.listToAttrs.
|
builtins.listToAttrs.
|
||||||
|
@ -493,5 +511,4 @@ rec {
|
||||||
zipWithNames = zipAttrsWithNames;
|
zipWithNames = zipAttrsWithNames;
|
||||||
zip = builtins.trace
|
zip = builtins.trace
|
||||||
"lib.zip is deprecated, use lib.zipAttrsWith instead" zipAttrsWith;
|
"lib.zip is deprecated, use lib.zipAttrsWith instead" zipAttrsWith;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -78,7 +78,7 @@ let
|
||||||
zipAttrsWithNames zipAttrsWith zipAttrs recursiveUpdateUntil
|
zipAttrsWithNames zipAttrsWith zipAttrs recursiveUpdateUntil
|
||||||
recursiveUpdate matchAttrs overrideExisting getOutput getBin
|
recursiveUpdate matchAttrs overrideExisting getOutput getBin
|
||||||
getLib getDev getMan chooseDevOutputs zipWithNames zip
|
getLib getDev getMan chooseDevOutputs zipWithNames zip
|
||||||
recurseIntoAttrs dontRecurseIntoAttrs;
|
recurseIntoAttrs dontRecurseIntoAttrs cartesianProductOfSets;
|
||||||
inherit (self.lists) singleton forEach foldr fold foldl foldl' imap0 imap1
|
inherit (self.lists) singleton forEach foldr fold foldl foldl' imap0 imap1
|
||||||
concatMap flatten remove findSingle findFirst any all count
|
concatMap flatten remove findSingle findFirst any all count
|
||||||
optional optionals toList range partition zipListsWith zipLists
|
optional optionals toList range partition zipListsWith zipLists
|
||||||
|
|
|
@ -629,7 +629,9 @@ rec {
|
||||||
crossLists (x:y: "${toString x}${toString y}") [[1 2] [3 4]]
|
crossLists (x:y: "${toString x}${toString y}") [[1 2] [3 4]]
|
||||||
=> [ "13" "14" "23" "24" ]
|
=> [ "13" "14" "23" "24" ]
|
||||||
*/
|
*/
|
||||||
crossLists = f: foldl (fs: args: concatMap (f: map f args) fs) [f];
|
crossLists = builtins.trace
|
||||||
|
"lib.crossLists is deprecated, use lib.cartesianProductOfSets instead"
|
||||||
|
(f: foldl (fs: args: concatMap (f: map f args) fs) [f]);
|
||||||
|
|
||||||
|
|
||||||
/* Remove duplicate elements from the list. O(n^2) complexity.
|
/* Remove duplicate elements from the list. O(n^2) complexity.
|
||||||
|
|
|
@ -660,4 +660,71 @@ runTests {
|
||||||
expected = [ [ "foo" ] [ "foo" "<name>" "bar" ] [ "foo" "bar" ] ];
|
expected = [ [ "foo" ] [ "foo" "<name>" "bar" ] [ "foo" "bar" ] ];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
testCartesianProductOfEmptySet = {
|
||||||
|
expr = cartesianProductOfSets {};
|
||||||
|
expected = [ {} ];
|
||||||
|
};
|
||||||
|
|
||||||
|
testCartesianProductOfOneSet = {
|
||||||
|
expr = cartesianProductOfSets { a = [ 1 2 3 ]; };
|
||||||
|
expected = [ { a = 1; } { a = 2; } { a = 3; } ];
|
||||||
|
};
|
||||||
|
|
||||||
|
testCartesianProductOfTwoSets = {
|
||||||
|
expr = cartesianProductOfSets { a = [ 1 ]; b = [ 10 20 ]; };
|
||||||
|
expected = [
|
||||||
|
{ a = 1; b = 10; }
|
||||||
|
{ a = 1; b = 20; }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
testCartesianProductOfTwoSetsWithOneEmpty = {
|
||||||
|
expr = cartesianProductOfSets { a = [ ]; b = [ 10 20 ]; };
|
||||||
|
expected = [ ];
|
||||||
|
};
|
||||||
|
|
||||||
|
testCartesianProductOfThreeSets = {
|
||||||
|
expr = cartesianProductOfSets {
|
||||||
|
a = [ 1 2 3 ];
|
||||||
|
b = [ 10 20 30 ];
|
||||||
|
c = [ 100 200 300 ];
|
||||||
|
};
|
||||||
|
expected = [
|
||||||
|
{ a = 1; b = 10; c = 100; }
|
||||||
|
{ a = 1; b = 10; c = 200; }
|
||||||
|
{ a = 1; b = 10; c = 300; }
|
||||||
|
|
||||||
|
{ a = 1; b = 20; c = 100; }
|
||||||
|
{ a = 1; b = 20; c = 200; }
|
||||||
|
{ a = 1; b = 20; c = 300; }
|
||||||
|
|
||||||
|
{ a = 1; b = 30; c = 100; }
|
||||||
|
{ a = 1; b = 30; c = 200; }
|
||||||
|
{ a = 1; b = 30; c = 300; }
|
||||||
|
|
||||||
|
{ a = 2; b = 10; c = 100; }
|
||||||
|
{ a = 2; b = 10; c = 200; }
|
||||||
|
{ a = 2; b = 10; c = 300; }
|
||||||
|
|
||||||
|
{ a = 2; b = 20; c = 100; }
|
||||||
|
{ a = 2; b = 20; c = 200; }
|
||||||
|
{ a = 2; b = 20; c = 300; }
|
||||||
|
|
||||||
|
{ a = 2; b = 30; c = 100; }
|
||||||
|
{ a = 2; b = 30; c = 200; }
|
||||||
|
{ a = 2; b = 30; c = 300; }
|
||||||
|
|
||||||
|
{ a = 3; b = 10; c = 100; }
|
||||||
|
{ a = 3; b = 10; c = 200; }
|
||||||
|
{ a = 3; b = 10; c = 300; }
|
||||||
|
|
||||||
|
{ a = 3; b = 20; c = 100; }
|
||||||
|
{ a = 3; b = 20; c = 200; }
|
||||||
|
{ a = 3; b = 20; c = 300; }
|
||||||
|
|
||||||
|
{ a = 3; b = 30; c = 100; }
|
||||||
|
{ a = 3; b = 30; c = 200; }
|
||||||
|
{ a = 3; b = 30; c = 300; }
|
||||||
|
];
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -444,8 +444,8 @@ in
|
||||||
in
|
in
|
||||||
# We will generate every possible pair of WM and DM.
|
# We will generate every possible pair of WM and DM.
|
||||||
concatLists (
|
concatLists (
|
||||||
crossLists
|
builtins.map
|
||||||
(dm: wm: let
|
({dm, wm}: let
|
||||||
sessionName = "${dm.name}${optionalString (wm.name != "none") ("+" + wm.name)}";
|
sessionName = "${dm.name}${optionalString (wm.name != "none") ("+" + wm.name)}";
|
||||||
script = xsession dm wm;
|
script = xsession dm wm;
|
||||||
desktopNames = if dm ? desktopNames
|
desktopNames = if dm ? desktopNames
|
||||||
|
@ -472,7 +472,7 @@ in
|
||||||
providedSessions = [ sessionName ];
|
providedSessions = [ sessionName ];
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
[dms wms]
|
(cartesianProductOfSets { dm = dms; wm = wms; })
|
||||||
);
|
);
|
||||||
|
|
||||||
# Make xsessions and wayland sessions available in XDG_DATA_DIRS
|
# Make xsessions and wayland sessions available in XDG_DATA_DIRS
|
||||||
|
|
|
@ -5,7 +5,11 @@
|
||||||
|
|
||||||
let
|
let
|
||||||
inherit (import ../lib/testing-python.nix { inherit system pkgs; }) makeTest;
|
inherit (import ../lib/testing-python.nix { inherit system pkgs; }) makeTest;
|
||||||
in pkgs.lib.listToAttrs (pkgs.lib.crossLists (predictable: withNetworkd: {
|
testCombinations = pkgs.lib.cartesianProductOfSets {
|
||||||
|
predictable = [true false];
|
||||||
|
withNetworkd = [true false];
|
||||||
|
};
|
||||||
|
in pkgs.lib.listToAttrs (builtins.map ({ predictable, withNetworkd }: {
|
||||||
name = pkgs.lib.optionalString (!predictable) "un" + "predictable"
|
name = pkgs.lib.optionalString (!predictable) "un" + "predictable"
|
||||||
+ pkgs.lib.optionalString withNetworkd "Networkd";
|
+ pkgs.lib.optionalString withNetworkd "Networkd";
|
||||||
value = makeTest {
|
value = makeTest {
|
||||||
|
@ -30,4 +34,4 @@ in pkgs.lib.listToAttrs (pkgs.lib.crossLists (predictable: withNetworkd: {
|
||||||
machine.${if predictable then "fail" else "succeed"}("ip link show eth0")
|
machine.${if predictable then "fail" else "succeed"}("ip link show eth0")
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
}) [[true false] [true false]])
|
}) testCombinations)
|
||||||
|
|
|
@ -50,10 +50,11 @@ in stdenv.mkDerivation {
|
||||||
homepage = "https://github.com/solo5/solo5";
|
homepage = "https://github.com/solo5/solo5";
|
||||||
license = licenses.isc;
|
license = licenses.isc;
|
||||||
maintainers = [ maintainers.ehmry ];
|
maintainers = [ maintainers.ehmry ];
|
||||||
platforms = lib.crossLists (arch: os: "${arch}-${os}") [
|
platforms = builtins.map ({arch, os}: "${arch}-${os}")
|
||||||
[ "aarch64" "x86_64" ]
|
(cartesianProductOfSets {
|
||||||
[ "freebsd" "genode" "linux" "openbsd" ]
|
arch = [ "aarch64" "x86_64" ];
|
||||||
];
|
os = [ "freebsd" "genode" "linux" "openbsd" ];
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue