forked from auxolotl/labs
chore: initial commit
This commit is contained in:
parent
b7456195bd
commit
f7d6c846f6
|
@ -75,12 +75,40 @@ in
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Successful tests will return `true` while failing test will resolve with `false`.
|
Successful tests will return `true` while failing test will resolve with `false`. You can run
|
||||||
|
all tests with the following command:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
./test.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to run a specific test suite, you can run the command, specifying the directory
|
||||||
|
to the tests file:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
./test.sh $namespace
|
||||||
|
```
|
||||||
|
|
||||||
|
For example, to run the tests for only `attrs`, use the following command:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
./test.sh attrs
|
||||||
|
```
|
||||||
|
|
||||||
### Formatting
|
### Formatting
|
||||||
|
|
||||||
|
> **Note:** To keep this flake light and keep its inputs empty we do not include a package
|
||||||
|
> set which would provide a formatter. Instead please run `nix run nixpkgs#nixfmt-rfc-style`
|
||||||
|
> until an improved solution is available.
|
||||||
|
|
||||||
All code in this project must be formatted using the provided formatter in the `flake.nix`
|
All code in this project must be formatted using the provided formatter in the `flake.nix`
|
||||||
file. You can run this formatter using the command `nix fmt`.
|
file. You can run this formatter using the command `nix fmt` (not currently available).
|
||||||
|
|
||||||
|
### Code Quality
|
||||||
|
|
||||||
|
In order to keep the project approachable and easy to maintain, certain patterns are not allowed.
|
||||||
|
In particular, the use of `with` and `rec` are not allowed. Additionally, you should prefer the
|
||||||
|
fully qualified name of a variable rather than creating intermediate ones using `inherit`.
|
||||||
|
|
||||||
### Adding Functionality
|
### Adding Functionality
|
||||||
|
|
||||||
|
|
107
lib/default.test.nix
Normal file
107
lib/default.test.nix
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
let
|
||||||
|
lib = import ./default.nix;
|
||||||
|
|
||||||
|
root = ./.;
|
||||||
|
|
||||||
|
files = [
|
||||||
|
./src/attrs/default.test.nix
|
||||||
|
./src/bools/default.test.nix
|
||||||
|
./src/errors/default.test.nix
|
||||||
|
./src/fp/default.test.nix
|
||||||
|
./src/generators/default.test.nix
|
||||||
|
./src/importers/default.test.nix
|
||||||
|
./src/lists/default.test.nix
|
||||||
|
./src/math/default.test.nix
|
||||||
|
./src/modules/default.test.nix
|
||||||
|
./src/numbers/default.test.nix
|
||||||
|
./src/options/default.test.nix
|
||||||
|
./src/packages/default.test.nix
|
||||||
|
./src/paths/default.test.nix
|
||||||
|
./src/points/default.test.nix
|
||||||
|
./src/strings/default.test.nix
|
||||||
|
./src/types/default.test.nix
|
||||||
|
./src/versions/default.test.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
resolve = file: let
|
||||||
|
imported = import file;
|
||||||
|
value =
|
||||||
|
if builtins.isFunction imported
|
||||||
|
then imported {inherit lib;}
|
||||||
|
else imported;
|
||||||
|
relative = lib.strings.removePrefix (builtins.toString root) (builtins.toString file);
|
||||||
|
in {
|
||||||
|
inherit file value;
|
||||||
|
relative =
|
||||||
|
if lib.strings.hasPrefix "/" relative
|
||||||
|
then "." + relative
|
||||||
|
else relative;
|
||||||
|
namespace = getNamespace file;
|
||||||
|
};
|
||||||
|
|
||||||
|
resolved = builtins.map resolve files;
|
||||||
|
|
||||||
|
getNamespace = path: let
|
||||||
|
relative = lib.strings.removePrefix (builtins.toString root) (builtins.toString path);
|
||||||
|
parts = lib.strings.split "/" relative;
|
||||||
|
in
|
||||||
|
if builtins.length parts > 2
|
||||||
|
then builtins.elemAt parts 2
|
||||||
|
else relative;
|
||||||
|
|
||||||
|
results = let
|
||||||
|
getTests = file: prefix: suite: let
|
||||||
|
nested = lib.attrs.mapToList (name: value: getTests file (prefix ++ [name]) value) suite;
|
||||||
|
relative = lib.strings.removePrefix (builtins.toString root) (builtins.toString file);
|
||||||
|
in
|
||||||
|
if builtins.isAttrs suite
|
||||||
|
then builtins.concatLists nested
|
||||||
|
else [
|
||||||
|
{
|
||||||
|
inherit prefix file;
|
||||||
|
name = builtins.concatStringsSep " > " prefix;
|
||||||
|
value = suite;
|
||||||
|
relative =
|
||||||
|
if lib.strings.hasPrefix "/" relative
|
||||||
|
then "." + relative
|
||||||
|
else relative;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
base =
|
||||||
|
builtins.map (entry: getTests entry.file [entry.namespace] entry.value) resolved;
|
||||||
|
in
|
||||||
|
builtins.concatLists base;
|
||||||
|
|
||||||
|
successes = builtins.filter (test: test.value) results;
|
||||||
|
failures = builtins.filter (test: !test.value) results;
|
||||||
|
|
||||||
|
total = "${builtins.toString (builtins.length successes)} / ${builtins.toString (builtins.length results)}";
|
||||||
|
in
|
||||||
|
if failures == []
|
||||||
|
then let
|
||||||
|
message =
|
||||||
|
lib.strings.concatMapSep "\n"
|
||||||
|
(test: "✅ ${test.name}")
|
||||||
|
successes;
|
||||||
|
in ''
|
||||||
|
SUCCESS (${total})
|
||||||
|
|
||||||
|
${message}
|
||||||
|
''
|
||||||
|
else let
|
||||||
|
successMessage =
|
||||||
|
lib.strings.concatMapSep "\n"
|
||||||
|
(test: "✅ ${test.name}")
|
||||||
|
successes;
|
||||||
|
failureMessage =
|
||||||
|
lib.strings.concatMapSep "\n\n"
|
||||||
|
(test:
|
||||||
|
"❎ ${test.name}\n"
|
||||||
|
+ " -> ${test.relative}")
|
||||||
|
failures;
|
||||||
|
in ''
|
||||||
|
FAILURE (${total})
|
||||||
|
|
||||||
|
${failureMessage}
|
||||||
|
''
|
|
@ -13,16 +13,18 @@ lib: {
|
||||||
mergeRecursiveUntil = predicate: x: y: let
|
mergeRecursiveUntil = predicate: x: y: let
|
||||||
process = path:
|
process = path:
|
||||||
builtins.zipAttrsWith (
|
builtins.zipAttrsWith (
|
||||||
key: values: let
|
name: values: let
|
||||||
currentPath = path ++ [key];
|
currentPath = path ++ [name];
|
||||||
isSingleValue = builtins.length values == 1;
|
isSingleValue = builtins.length values == 1;
|
||||||
isComplete =
|
isComplete =
|
||||||
predicate currentPath
|
predicate currentPath
|
||||||
(builtins.elemAt values 1)
|
(builtins.elemAt values 1)
|
||||||
(builtins.elemAt values 0);
|
(builtins.elemAt values 0);
|
||||||
in
|
in
|
||||||
if isSingleValue || isComplete
|
if isSingleValue
|
||||||
then builtins.elemAt values 0
|
then builtins.elemAt values 0
|
||||||
|
else if isComplete
|
||||||
|
then builtins.elemAt values 1
|
||||||
else process currentPath values
|
else process currentPath values
|
||||||
);
|
);
|
||||||
in
|
in
|
||||||
|
@ -43,13 +45,13 @@ lib: {
|
||||||
##
|
##
|
||||||
## @type (List String) -> a -> Attrs -> a | b
|
## @type (List String) -> a -> Attrs -> a | b
|
||||||
select = path: fallback: target: let
|
select = path: fallback: target: let
|
||||||
key = builtins.head path;
|
name = builtins.head path;
|
||||||
rest = builtins.tail path;
|
rest = builtins.tail path;
|
||||||
in
|
in
|
||||||
if path == []
|
if path == []
|
||||||
then target
|
then target
|
||||||
else if target ? ${key}
|
else if target ? ${name}
|
||||||
then lib.attrs.select rest fallback target.${key}
|
then lib.attrs.select rest fallback target.${name}
|
||||||
else fallback;
|
else fallback;
|
||||||
|
|
||||||
## Get a value from an attribute set by a path. If the path does not exist,
|
## Get a value from an attribute set by a path. If the path does not exist,
|
||||||
|
@ -61,10 +63,12 @@ lib: {
|
||||||
error = builtins.throw "Path not found in attribute set: ${pathAsString}";
|
error = builtins.throw "Path not found in attribute set: ${pathAsString}";
|
||||||
in
|
in
|
||||||
if lib.attrs.has path target
|
if lib.attrs.has path target
|
||||||
then lib.attrs.select path target
|
then lib.attrs.select path null target
|
||||||
else error;
|
else error;
|
||||||
|
|
||||||
# TODO: Document this.
|
## Create a nested attribute set with a value as the leaf node.
|
||||||
|
##
|
||||||
|
## @type (List String) -> a -> Attrs
|
||||||
set = path: value: let
|
set = path: value: let
|
||||||
length = builtins.length path;
|
length = builtins.length path;
|
||||||
process = depth:
|
process = depth:
|
||||||
|
@ -80,13 +84,13 @@ lib: {
|
||||||
##
|
##
|
||||||
## @type (List String) -> Attrs -> Bool
|
## @type (List String) -> Attrs -> Bool
|
||||||
has = path: target: let
|
has = path: target: let
|
||||||
key = builtins.head path;
|
name = builtins.head path;
|
||||||
rest = builtins.tail path;
|
rest = builtins.tail path;
|
||||||
in
|
in
|
||||||
if path == []
|
if path == []
|
||||||
then true
|
then true
|
||||||
else if target ? ${key}
|
else if target ? ${name}
|
||||||
then lib.attrs.has rest target.${key}
|
then lib.attrs.has rest target.${name}
|
||||||
else false;
|
else false;
|
||||||
|
|
||||||
## Depending on a given condition, either use the given value or an empty
|
## Depending on a given condition, either use the given value or an empty
|
||||||
|
@ -98,43 +102,46 @@ lib: {
|
||||||
then value
|
then value
|
||||||
else {};
|
else {};
|
||||||
|
|
||||||
## Map an attribute set's keys and values to a list.
|
## Map an attribute set's names and values to a list.
|
||||||
##
|
##
|
||||||
## @type Any a => (String -> Any -> a) -> Attrs -> List a
|
## @type Any a => (String -> Any -> a) -> Attrs -> List a
|
||||||
mapToList = f: target:
|
mapToList = f: target:
|
||||||
builtins.map (key: f key target.${key}) (builtins.attrNames target);
|
builtins.map (name: f name target.${name}) (builtins.attrNames target);
|
||||||
|
|
||||||
# TODO: Document this.
|
## Map an attribute set recursively. Only non-set leaf nodes will be mapped.
|
||||||
|
##
|
||||||
|
## @type (List String -> Any -> Any) -> Attrs -> Attrs
|
||||||
mapRecursive = f: target:
|
mapRecursive = f: target:
|
||||||
lib.attrs.mapRecursiveWhen (lib.fp.const true) f target;
|
lib.attrs.mapRecursiveWhen (lib.fp.const true) f target;
|
||||||
|
|
||||||
# TODO: Document this.
|
## Map an attribute set recursively when a given predicate returns true.
|
||||||
|
## Only leaf nodes according to the predicate will be mapped.
|
||||||
|
##
|
||||||
|
## @type (Attrs -> Bool) -> (List String -> Any -> Any) -> Attrs -> Attrs
|
||||||
mapRecursiveWhen = predicate: f: target: let
|
mapRecursiveWhen = predicate: f: target: let
|
||||||
process = path:
|
process = path:
|
||||||
builtins.mapAttrs (
|
builtins.mapAttrs (
|
||||||
key: value:
|
name: value:
|
||||||
if builtins.isAttrs value && predicate value
|
if builtins.isAttrs value && predicate value
|
||||||
then process (path ++ [key]) value
|
then process (path ++ [name]) value
|
||||||
else f (path ++ [key]) value
|
else f (path ++ [name]) value
|
||||||
);
|
);
|
||||||
in
|
in
|
||||||
process [] target;
|
process [] target;
|
||||||
|
|
||||||
# TODO: Document this.
|
## Filter an attribute set by a given predicate. The filter is only performed
|
||||||
|
## on the base level of the attribute set.
|
||||||
|
##
|
||||||
|
## @type (String -> Any -> Bool) -> Attrs -> Attrs
|
||||||
filter = predicate: target: let
|
filter = predicate: target: let
|
||||||
keys = builtins.attrNames target;
|
names = builtins.attrNames target;
|
||||||
process = key: let
|
process = name: let
|
||||||
value = target.${key};
|
value = target.${name};
|
||||||
in
|
in
|
||||||
if predicate key value
|
if predicate name value
|
||||||
then [
|
then [{inherit name value;}]
|
||||||
{
|
|
||||||
name = key;
|
|
||||||
value = value;
|
|
||||||
}
|
|
||||||
]
|
|
||||||
else [];
|
else [];
|
||||||
valid = builtins.concatMap process keys;
|
valid = builtins.concatMap process names;
|
||||||
in
|
in
|
||||||
builtins.listToAttrs valid;
|
builtins.listToAttrs valid;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,79 @@
|
||||||
let
|
let
|
||||||
lib = import ./../default.nix;
|
lib = import ./../default.nix;
|
||||||
in {
|
in {
|
||||||
|
"merge" = {
|
||||||
|
"merges two shallow sets" = let
|
||||||
|
expected = {
|
||||||
|
x = 1;
|
||||||
|
y = 2;
|
||||||
|
};
|
||||||
|
|
||||||
|
actual = lib.attrs.merge {x = 1;} {y = 2;};
|
||||||
|
in
|
||||||
|
expected == actual;
|
||||||
|
|
||||||
|
"overwrites values from the first set" = let
|
||||||
|
expected = {
|
||||||
|
x = 2;
|
||||||
|
};
|
||||||
|
actual = lib.attrs.merge {x = 1;} {x = 2;};
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
|
||||||
|
"does not merge nested sets" = let
|
||||||
|
expected = {
|
||||||
|
x.y = 2;
|
||||||
|
};
|
||||||
|
actual = lib.attrs.merge {x.z = 1;} {x.y = 2;};
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"mergeRecursiveUntil" = {
|
||||||
|
"merges with predicate" = let
|
||||||
|
expected = {
|
||||||
|
x.y.z = 1;
|
||||||
|
};
|
||||||
|
actual =
|
||||||
|
lib.attrs.mergeRecursiveUntil
|
||||||
|
(path: x: y: lib.lists.last path == "z")
|
||||||
|
{x.y.z = 2;}
|
||||||
|
{x.y.z = 1;};
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
|
||||||
|
"handles shallow merges" = let
|
||||||
|
expected = {
|
||||||
|
x.y.z = 1;
|
||||||
|
};
|
||||||
|
actual =
|
||||||
|
lib.attrs.mergeRecursiveUntil
|
||||||
|
(path: x: y: true)
|
||||||
|
{
|
||||||
|
x = {
|
||||||
|
y.z = 2;
|
||||||
|
|
||||||
|
a = false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
{x.y.z = 1;};
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"mergeRecursive" = {
|
||||||
|
"merges two sets deeply" = let
|
||||||
|
expected = {
|
||||||
|
x.y.z = 1;
|
||||||
|
};
|
||||||
|
actual =
|
||||||
|
lib.attrs.mergeRecursive
|
||||||
|
{x.y.z = 2;}
|
||||||
|
{x.y.z = 1;};
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
"select" = {
|
"select" = {
|
||||||
"selects a nested value" = let
|
"selects a nested value" = let
|
||||||
expected = "value";
|
expected = "value";
|
||||||
|
@ -13,5 +86,244 @@ in {
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
actual == expected;
|
actual == expected;
|
||||||
|
|
||||||
|
"handles empty path" = let
|
||||||
|
expected = {
|
||||||
|
x = {
|
||||||
|
y = {
|
||||||
|
z = 1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
actual =
|
||||||
|
lib.attrs.select
|
||||||
|
[]
|
||||||
|
null
|
||||||
|
{
|
||||||
|
x = {
|
||||||
|
y = {
|
||||||
|
z = 1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
|
||||||
|
"handles fallback value" = let
|
||||||
|
expected = "fallback";
|
||||||
|
actual =
|
||||||
|
lib.attrs.select
|
||||||
|
["x" "y" "z"]
|
||||||
|
expected
|
||||||
|
{};
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"selectOrThrow" = {
|
||||||
|
"selects a nested value" = let
|
||||||
|
expected = "value";
|
||||||
|
actual =
|
||||||
|
lib.attrs.selectOrThrow
|
||||||
|
["x" "y" "z"]
|
||||||
|
{
|
||||||
|
x.y.z = expected;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
|
||||||
|
"handles empty path" = let
|
||||||
|
expected = {
|
||||||
|
x = {
|
||||||
|
y = {
|
||||||
|
z = 1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
actual =
|
||||||
|
lib.attrs.selectOrThrow
|
||||||
|
[]
|
||||||
|
{
|
||||||
|
x = {
|
||||||
|
y = {
|
||||||
|
z = 1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
|
||||||
|
"throws on nonexistent path" = let
|
||||||
|
actual =
|
||||||
|
lib.attrs.selectOrThrow
|
||||||
|
["x" "y" "z"]
|
||||||
|
{};
|
||||||
|
|
||||||
|
evaluated = builtins.tryEval (builtins.deepSeq actual actual);
|
||||||
|
in
|
||||||
|
!evaluated.success;
|
||||||
|
};
|
||||||
|
|
||||||
|
"set" = {
|
||||||
|
"creates a nested set" = let
|
||||||
|
expected = {
|
||||||
|
x = {
|
||||||
|
y = {
|
||||||
|
z = 1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
actual = lib.attrs.set ["x" "y" "z"] 1;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
|
||||||
|
"handles empty path" = let
|
||||||
|
expected = 1;
|
||||||
|
actual = lib.attrs.set [] 1;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"has" = {
|
||||||
|
"returns true for a nested value" = let
|
||||||
|
exists = lib.attrs.has ["x" "y" "z"] {x.y.z = 1;};
|
||||||
|
in
|
||||||
|
exists;
|
||||||
|
|
||||||
|
"returns false for a nonexistent value" = let
|
||||||
|
exists = lib.attrs.has ["x" "y" "z"] {};
|
||||||
|
in
|
||||||
|
!exists;
|
||||||
|
|
||||||
|
"handles empty path" = let
|
||||||
|
exists = lib.attrs.has [] {};
|
||||||
|
in
|
||||||
|
exists;
|
||||||
|
};
|
||||||
|
|
||||||
|
"when" = {
|
||||||
|
"returns the value when condition is true" = let
|
||||||
|
expected = "value";
|
||||||
|
actual = lib.attrs.when true expected;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
|
||||||
|
"returns an empty set when condition is false" = let
|
||||||
|
expected = {};
|
||||||
|
actual = lib.attrs.when false "value";
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"mapToList" = {
|
||||||
|
"converts a set to a list" = let
|
||||||
|
expected = [
|
||||||
|
{
|
||||||
|
name = "x";
|
||||||
|
value = 1;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name = "y";
|
||||||
|
value = 2;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
actual =
|
||||||
|
lib.attrs.mapToList
|
||||||
|
(name: value: {inherit name value;})
|
||||||
|
{
|
||||||
|
x = 1;
|
||||||
|
y = 2;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"mapRecursiveWhen" = {
|
||||||
|
"maps a set recursively" = let
|
||||||
|
expected = {
|
||||||
|
x = {
|
||||||
|
y = {
|
||||||
|
z = 2;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
actual =
|
||||||
|
lib.attrs.mapRecursiveWhen
|
||||||
|
(value: true)
|
||||||
|
(path: value: value + 1)
|
||||||
|
{
|
||||||
|
x = {
|
||||||
|
y = {
|
||||||
|
z = 1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
|
||||||
|
"maps a set given a condition" = let
|
||||||
|
expected = {
|
||||||
|
x = {
|
||||||
|
y = {
|
||||||
|
z = 1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
actual =
|
||||||
|
lib.attrs.mapRecursiveWhen
|
||||||
|
(value: !(value ? z))
|
||||||
|
(path: value:
|
||||||
|
# We map before we get to a non-set value
|
||||||
|
if builtins.isAttrs value
|
||||||
|
then value
|
||||||
|
else value + 1)
|
||||||
|
{
|
||||||
|
x = {
|
||||||
|
y = {
|
||||||
|
z = 1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"mapRecursive" = {
|
||||||
|
"maps a set recursively" = let
|
||||||
|
expected = {
|
||||||
|
x = {
|
||||||
|
y = {
|
||||||
|
z = 2;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
actual =
|
||||||
|
lib.attrs.mapRecursive
|
||||||
|
(path: value: value + 1)
|
||||||
|
{
|
||||||
|
x = {
|
||||||
|
y = {
|
||||||
|
z = 1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"filter" = {
|
||||||
|
"filters a set" = let
|
||||||
|
expected = {
|
||||||
|
y = 2;
|
||||||
|
};
|
||||||
|
actual =
|
||||||
|
lib.attrs.filter
|
||||||
|
(name: value: name == "y")
|
||||||
|
{
|
||||||
|
x = 1;
|
||||||
|
y = 2;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,21 @@
|
||||||
lib: {
|
lib: {
|
||||||
bools = {
|
bools = {
|
||||||
into = {
|
into = {
|
||||||
|
## Convert a boolean value into a string.
|
||||||
|
##
|
||||||
|
## @type Bool -> String
|
||||||
string = value:
|
string = value:
|
||||||
if value
|
if value
|
||||||
then "true"
|
then "true"
|
||||||
else "false";
|
else "false";
|
||||||
|
|
||||||
|
## Convert a boolean into either the string "yes" or "no".
|
||||||
|
##
|
||||||
|
## @type Bool -> String
|
||||||
|
yesno = value:
|
||||||
|
if value
|
||||||
|
then "yes"
|
||||||
|
else "no";
|
||||||
};
|
};
|
||||||
|
|
||||||
## Choose between two values based on a condition. When true, the first value
|
## Choose between two values based on a condition. When true, the first value
|
||||||
|
|
161
lib/src/bools/default.test.nix
Normal file
161
lib/src/bools/default.test.nix
Normal file
|
@ -0,0 +1,161 @@
|
||||||
|
let
|
||||||
|
lib = import ./../default.nix;
|
||||||
|
in {
|
||||||
|
"into" = {
|
||||||
|
"string" = {
|
||||||
|
"handles true" = let
|
||||||
|
expected = "true";
|
||||||
|
actual = lib.bools.into.string true;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
|
||||||
|
"handles false" = let
|
||||||
|
expected = "false";
|
||||||
|
actual = lib.bools.into.string false;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"yesno" = {
|
||||||
|
"handles true" = let
|
||||||
|
expected = "yes";
|
||||||
|
actual = lib.bools.into.yesno true;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
"handles false" = let
|
||||||
|
expected = "no";
|
||||||
|
actual = lib.bools.into.yesno false;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
"when" = {
|
||||||
|
"returns first value when true" = let
|
||||||
|
expected = "foo";
|
||||||
|
actual = lib.bools.when true expected "bar";
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
|
||||||
|
"returns second value when false" = let
|
||||||
|
expected = "bar";
|
||||||
|
actual = lib.bools.when false "foo" expected;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"and" = {
|
||||||
|
"returns true when both are true" = let
|
||||||
|
expected = true;
|
||||||
|
actual = lib.bools.and true true;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
"returns false when first is false" = let
|
||||||
|
expected = false;
|
||||||
|
actual = lib.bools.and false true;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
"returns false when second is false" = let
|
||||||
|
expected = false;
|
||||||
|
actual = lib.bools.and true false;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
"returns false when both are false" = let
|
||||||
|
expected = false;
|
||||||
|
actual = lib.bools.and false false;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"and'" = let
|
||||||
|
getTrue = _: true;
|
||||||
|
getFalse = _: false;
|
||||||
|
in {
|
||||||
|
"returns true when both are true" = let
|
||||||
|
expected = true;
|
||||||
|
actual = lib.bools.and' getTrue getTrue null;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
|
||||||
|
"returns false when first is false" = let
|
||||||
|
expected = false;
|
||||||
|
actual = lib.bools.and' getFalse getTrue null;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
|
||||||
|
"returns false when second is false" = let
|
||||||
|
expected = false;
|
||||||
|
actual = lib.bools.and' getTrue getFalse null;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
|
||||||
|
"returns false when both are false" = let
|
||||||
|
expected = false;
|
||||||
|
actual = lib.bools.and' getFalse getFalse null;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"or" = {
|
||||||
|
"returns true when both are true" = let
|
||||||
|
expected = true;
|
||||||
|
actual = lib.bools.or true true;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
"returns true when first is true" = let
|
||||||
|
expected = true;
|
||||||
|
actual = lib.bools.or true false;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
"returns true when second is true" = let
|
||||||
|
expected = true;
|
||||||
|
actual = lib.bools.or false true;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
"returns false when both are false" = let
|
||||||
|
expected = false;
|
||||||
|
actual = lib.bools.or false false;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"or'" = let
|
||||||
|
getTrue = _: true;
|
||||||
|
getFalse = _: false;
|
||||||
|
in {
|
||||||
|
"returns true when both are true" = let
|
||||||
|
expected = true;
|
||||||
|
actual = lib.bools.or' getTrue getTrue null;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
"returns true when first is true" = let
|
||||||
|
expected = true;
|
||||||
|
actual = lib.bools.or' getTrue getFalse null;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
"returns true when second is true" = let
|
||||||
|
expected = true;
|
||||||
|
actual = lib.bools.or' getFalse getTrue null;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
"returns false when both are false" = let
|
||||||
|
expected = false;
|
||||||
|
actual = lib.bools.or' getFalse getFalse null;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"not" = {
|
||||||
|
"returns false when true" = let
|
||||||
|
expected = false;
|
||||||
|
actual = lib.bools.not true;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
|
||||||
|
"returns true when false" = let
|
||||||
|
expected = true;
|
||||||
|
actual = lib.bools.not false;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
}
|
|
@ -40,8 +40,8 @@ let
|
||||||
mergeAttrsRecursiveUntil = predicate: x: y: let
|
mergeAttrsRecursiveUntil = predicate: x: y: let
|
||||||
process = path:
|
process = path:
|
||||||
builtins.zipAttrsWith (
|
builtins.zipAttrsWith (
|
||||||
key: values: let
|
name: values: let
|
||||||
currentPath = path ++ [key];
|
currentPath = path ++ [name];
|
||||||
isSingleValue = builtins.length values == 1;
|
isSingleValue = builtins.length values == 1;
|
||||||
isComplete =
|
isComplete =
|
||||||
predicate currentPath
|
predicate currentPath
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
lib: {
|
lib: {
|
||||||
errors = {
|
errors = {
|
||||||
|
## Prints a message if the condition is not met. The result of
|
||||||
|
## the condition is returned.
|
||||||
|
##
|
||||||
|
## @notest
|
||||||
|
## @type Bool -> String -> Bool
|
||||||
trace = condition: message:
|
trace = condition: message:
|
||||||
if condition
|
if condition
|
||||||
then true
|
then true
|
||||||
|
|
3
lib/src/errors/default.test.nix
Normal file
3
lib/src/errors/default.test.nix
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
let
|
||||||
|
lib = import ./../default.nix;
|
||||||
|
in {}
|
|
@ -21,7 +21,7 @@ lib: {
|
||||||
##
|
##
|
||||||
## @type (List (Any -> Any)) -> Any -> Any
|
## @type (List (Any -> Any)) -> Any -> Any
|
||||||
pipe = fs: (
|
pipe = fs: (
|
||||||
x: builtins.foldl' (value: f: f x) x fs
|
x: builtins.foldl' (value: f: f value) x fs
|
||||||
);
|
);
|
||||||
|
|
||||||
## Reverse the order of arguments to a function that has two parameters.
|
## Reverse the order of arguments to a function that has two parameters.
|
||||||
|
@ -37,13 +37,20 @@ lib: {
|
||||||
## @type (a -> b -> c -> d -> e) -> d -> c -> b -> a -> e
|
## @type (a -> b -> c -> d -> e) -> d -> c -> b -> a -> e
|
||||||
flip4 = f: a: b: c: d: f d c b a;
|
flip4 = f: a: b: c: d: f d c b a;
|
||||||
|
|
||||||
# TODO: Document this.
|
## Get the arguments of a function or functor.
|
||||||
|
## An attribute set is returned with the arguments as keys. The values
|
||||||
|
## are `true` when the argument has a default value specified and `false`
|
||||||
|
## when it does not.
|
||||||
|
##
|
||||||
|
## @type Function -> Attrs
|
||||||
args = f:
|
args = f:
|
||||||
if f ? __functor
|
if f ? __functor
|
||||||
then f.__args__ or lib.fp.args (f.__functor f)
|
then f.__args__ or (lib.fp.args (f.__functor f))
|
||||||
else builtins.functionArgs f;
|
else builtins.functionArgs f;
|
||||||
|
|
||||||
# TODO: Document this.
|
## Create a function that is called with only the arguments it specifies.
|
||||||
|
##
|
||||||
|
## @type Attrs a => (a -> b) -> a -> b
|
||||||
withDynamicArgs = f: args: let
|
withDynamicArgs = f: args: let
|
||||||
fArgs = lib.fp.args f;
|
fArgs = lib.fp.args f;
|
||||||
common = builtins.intersectAttrs fArgs args;
|
common = builtins.intersectAttrs fArgs args;
|
||||||
|
|
139
lib/src/fp/default.test.nix
Normal file
139
lib/src/fp/default.test.nix
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
let
|
||||||
|
lib = import ./../default.nix;
|
||||||
|
in {
|
||||||
|
"id" = {
|
||||||
|
"returns its argument" = let
|
||||||
|
expected = "foo";
|
||||||
|
actual = lib.fp.id expected;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"const" = {
|
||||||
|
"creates a function that returns its argument" = let
|
||||||
|
expected = "foo";
|
||||||
|
actual = lib.fp.const expected "bar";
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"compose" = {
|
||||||
|
"composes two functions" = let
|
||||||
|
f = x: x + 1;
|
||||||
|
g = x: x * 2;
|
||||||
|
expected = 5;
|
||||||
|
actual = lib.fp.compose f g 2;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"pipe" = {
|
||||||
|
"pipes two functions" = let
|
||||||
|
f = x: x + 1;
|
||||||
|
g = x: x * 2;
|
||||||
|
expected = 5;
|
||||||
|
actual = lib.fp.pipe [g f] 2;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"flip2" = {
|
||||||
|
"flips the arguments of a binary function" = let
|
||||||
|
f = a: b: a - b;
|
||||||
|
expected = 1;
|
||||||
|
actual = lib.fp.flip2 f 1 2;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"flip3" = {
|
||||||
|
"flips the arguments of a ternary function" = let
|
||||||
|
f = a: b: c: a - b - c;
|
||||||
|
expected = 0;
|
||||||
|
actual = lib.fp.flip3 f 1 2 3;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"flip4" = {
|
||||||
|
"flips the arguments of a quaternary function" = let
|
||||||
|
f = a: b: c: d: a - b - c - d;
|
||||||
|
expected = -2;
|
||||||
|
actual = lib.fp.flip4 f 1 2 3 4;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"args" = {
|
||||||
|
"gets a functions attr set arguments" = let
|
||||||
|
expected = {
|
||||||
|
x = false;
|
||||||
|
y = true;
|
||||||
|
};
|
||||||
|
actual = lib.fp.args ({
|
||||||
|
x,
|
||||||
|
y ? null,
|
||||||
|
}:
|
||||||
|
null);
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
|
||||||
|
"returns an empty set if the function has no attrs arguments" = let
|
||||||
|
expected = {};
|
||||||
|
actual = lib.fp.args (args: null);
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
|
||||||
|
"supports functors" = let
|
||||||
|
expected = {
|
||||||
|
x = false;
|
||||||
|
y = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
actual = lib.fp.args {
|
||||||
|
__functor = self: {
|
||||||
|
x,
|
||||||
|
y ? null,
|
||||||
|
}:
|
||||||
|
null;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
|
||||||
|
"supports cached functor arguments" = let
|
||||||
|
expected = {
|
||||||
|
x = false;
|
||||||
|
y = true;
|
||||||
|
};
|
||||||
|
actual = lib.fp.args {
|
||||||
|
__args__ = {
|
||||||
|
x = false;
|
||||||
|
y = true;
|
||||||
|
};
|
||||||
|
__functor = self: args:
|
||||||
|
null;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"withDynamicArgs" = {
|
||||||
|
"applies a function with dynamic arguments" = let
|
||||||
|
expected = {x = true;};
|
||||||
|
actual = lib.fp.withDynamicArgs (args @ {x}: args) {
|
||||||
|
x = true;
|
||||||
|
y = true;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
|
||||||
|
"applies all arguments if none are specified" = let
|
||||||
|
expected = {
|
||||||
|
x = true;
|
||||||
|
y = true;
|
||||||
|
};
|
||||||
|
actual = lib.fp.withDynamicArgs (args: args) expected;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
}
|
113
lib/src/generators/default.test.nix
Normal file
113
lib/src/generators/default.test.nix
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
let
|
||||||
|
lib = import ./../default.nix;
|
||||||
|
in {
|
||||||
|
"withRecursion" = {
|
||||||
|
"evaluates within a given limit" = let
|
||||||
|
expected = {
|
||||||
|
x = 1;
|
||||||
|
};
|
||||||
|
actual = lib.generators.withRecursion {limit = 100;} expected;
|
||||||
|
in
|
||||||
|
expected == actual;
|
||||||
|
|
||||||
|
"fails when the limit is reached" = let
|
||||||
|
expected = {
|
||||||
|
x = 1;
|
||||||
|
};
|
||||||
|
actual = lib.generators.withRecursion {limit = -1;} expected;
|
||||||
|
evaluated = builtins.tryEval (builtins.deepSeq actual actual);
|
||||||
|
in
|
||||||
|
!evaluated.success;
|
||||||
|
|
||||||
|
"does not fail when throw is disabled" = let
|
||||||
|
expected = {
|
||||||
|
x = "<unevaluated>";
|
||||||
|
};
|
||||||
|
actual =
|
||||||
|
lib.generators.withRecursion {
|
||||||
|
limit = -1;
|
||||||
|
throw = false;
|
||||||
|
}
|
||||||
|
{x = 1;};
|
||||||
|
evaluated = builtins.tryEval (builtins.deepSeq actual actual);
|
||||||
|
in
|
||||||
|
evaluated.success
|
||||||
|
&& evaluated.value == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"pretty" = {
|
||||||
|
"formats with defaults" = let
|
||||||
|
expected = ''
|
||||||
|
{
|
||||||
|
attrs = { };
|
||||||
|
bool = true;
|
||||||
|
float = 0.0;
|
||||||
|
function = <function>;
|
||||||
|
int = 0;
|
||||||
|
list = [ ];
|
||||||
|
string = "string";
|
||||||
|
}'';
|
||||||
|
actual = lib.generators.pretty {} {
|
||||||
|
attrs = {};
|
||||||
|
bool = true;
|
||||||
|
float = 0.0;
|
||||||
|
function = x: x;
|
||||||
|
int = 0;
|
||||||
|
list = [];
|
||||||
|
string = "string";
|
||||||
|
# NOTE: We are not testing `path` types because they can return out of store
|
||||||
|
# values which are not deterministic.
|
||||||
|
# path = ./.;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
|
||||||
|
"formats with custom prettifiers" = let
|
||||||
|
expected = ''
|
||||||
|
{
|
||||||
|
attrs = { };
|
||||||
|
bool = true;
|
||||||
|
custom = <custom>;
|
||||||
|
float = 0.0;
|
||||||
|
function = <function>;
|
||||||
|
int = 0;
|
||||||
|
list = [ ];
|
||||||
|
string = "string";
|
||||||
|
}'';
|
||||||
|
actual =
|
||||||
|
lib.generators.pretty {
|
||||||
|
allowCustomPrettifiers = true;
|
||||||
|
} {
|
||||||
|
attrs = {};
|
||||||
|
bool = true;
|
||||||
|
float = 0.0;
|
||||||
|
function = x: x;
|
||||||
|
int = 0;
|
||||||
|
list = [];
|
||||||
|
string = "string";
|
||||||
|
custom = {
|
||||||
|
value = 0;
|
||||||
|
__pretty__ = value: "<custom>";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
|
||||||
|
"formats with multiline disabled" = let
|
||||||
|
expected = "{ attrs = { }; bool = true; float = 0.0; function = <function>; int = 0; list = [ ]; string = \"string\"; }";
|
||||||
|
actual =
|
||||||
|
lib.generators.pretty {
|
||||||
|
multiline = false;
|
||||||
|
} {
|
||||||
|
attrs = {};
|
||||||
|
bool = true;
|
||||||
|
float = 0.0;
|
||||||
|
function = x: x;
|
||||||
|
int = 0;
|
||||||
|
list = [];
|
||||||
|
string = "string";
|
||||||
|
};
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
}
|
|
@ -2,11 +2,13 @@ lib: {
|
||||||
importers = {
|
importers = {
|
||||||
## Import a JSON file as a Nix value.
|
## Import a JSON file as a Nix value.
|
||||||
##
|
##
|
||||||
|
## @notest
|
||||||
## @type Path -> a
|
## @type Path -> a
|
||||||
json = file: builtins.fromJSON (builtins.readFile file);
|
json = file: builtins.fromJSON (builtins.readFile file);
|
||||||
|
|
||||||
## Import a TOML file as a Nix value.
|
## Import a TOML file as a Nix value.
|
||||||
##
|
##
|
||||||
|
## @notest
|
||||||
## @type Path -> a
|
## @type Path -> a
|
||||||
toml = file: builtins.fromTOML (builtins.readFile file);
|
toml = file: builtins.fromTOML (builtins.readFile file);
|
||||||
};
|
};
|
||||||
|
|
3
lib/src/importers/default.test.nix
Normal file
3
lib/src/importers/default.test.nix
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
let
|
||||||
|
lib = import ./../default.nix;
|
||||||
|
in {}
|
|
@ -1,7 +1,11 @@
|
||||||
lib: {
|
lib: {
|
||||||
lists = {
|
lists = {
|
||||||
from = {
|
from = {
|
||||||
# TODO: Document this.
|
## Convert a value to a list. If the value is already a list,
|
||||||
|
## it will be returned as-is. If the value is not a list, it
|
||||||
|
## will be wrapped in a list.
|
||||||
|
##
|
||||||
|
## @type a | (List a) -> List a
|
||||||
any = value:
|
any = value:
|
||||||
if builtins.isList value
|
if builtins.isList value
|
||||||
then value
|
then value
|
||||||
|
@ -11,7 +15,7 @@ lib: {
|
||||||
sort = {
|
sort = {
|
||||||
## Perform a natural sort on a list of strings.
|
## Perform a natural sort on a list of strings.
|
||||||
##
|
##
|
||||||
## @type List -> List
|
## @type List String -> List String
|
||||||
natural = list: let
|
natural = list: let
|
||||||
vectorize = string: let
|
vectorize = string: let
|
||||||
serialize = part:
|
serialize = part:
|
||||||
|
@ -27,21 +31,29 @@ lib: {
|
||||||
builtins.map (x: builtins.elemAt x 1) (builtins.sort isLess prepared);
|
builtins.map (x: builtins.elemAt x 1) (builtins.sort isLess prepared);
|
||||||
};
|
};
|
||||||
|
|
||||||
# TODO: Document this.
|
## Map a list using both the index and value of each item. The
|
||||||
|
## index starts at 0.
|
||||||
|
##
|
||||||
|
## @type (Int -> a -> b) -> List a -> List b
|
||||||
mapWithIndex = f: list:
|
mapWithIndex = f: list:
|
||||||
builtins.genList
|
builtins.genList
|
||||||
(i: f i (builtins.elemAt list i))
|
(i: f i (builtins.elemAt list i))
|
||||||
(builtins.length list);
|
(builtins.length list);
|
||||||
|
|
||||||
# TODO: Document this.
|
## Map a list using both the index and value of each item. The
|
||||||
|
## index starts at 1.
|
||||||
|
##
|
||||||
|
## @type (Int -> a -> b) -> List a -> List b
|
||||||
mapWithIndex1 = f: list:
|
mapWithIndex1 = f: list:
|
||||||
builtins.genList
|
builtins.genList
|
||||||
(i: f (i + 1) (builtins.elemAt list i))
|
(i: f (i + 1) (builtins.elemAt list i))
|
||||||
(builtins.length list);
|
(builtins.length list);
|
||||||
|
|
||||||
## Compare two lists.
|
## Compare two lists using a custom compare function. The compare
|
||||||
|
## function is called for each element in the lists that need to
|
||||||
|
## be compared.
|
||||||
##
|
##
|
||||||
## @type (a -> b -> Int) -> List a -> List b -> Int
|
## @type (a -> b -> -1 | 0 | 1) -> List a -> List b -> Int
|
||||||
compare = compare: a: b: let
|
compare = compare: a: b: let
|
||||||
result = compare (builtins.head a) (builtins.head b);
|
result = compare (builtins.head a) (builtins.head b);
|
||||||
in
|
in
|
||||||
|
@ -60,7 +72,7 @@ lib: {
|
||||||
##
|
##
|
||||||
## @type List a -> a
|
## @type List a -> a
|
||||||
last = list:
|
last = list:
|
||||||
assert lib.assertMsg (list != []) "List cannot be empty";
|
assert lib.errors.trace (list != []) "List cannot be empty";
|
||||||
builtins.elemAt list (builtins.length list - 1);
|
builtins.elemAt list (builtins.length list - 1);
|
||||||
|
|
||||||
## Slice part of a list to create a new list.
|
## Slice part of a list to create a new list.
|
||||||
|
@ -137,7 +149,9 @@ lib: {
|
||||||
else [value]
|
else [value]
|
||||||
else [];
|
else [];
|
||||||
|
|
||||||
# TODO: Document this.
|
## Count the number of items in a list that satisfy a given predicate.
|
||||||
|
##
|
||||||
|
## @type (a -> Bool) -> List a -> Int
|
||||||
count = predicate: list:
|
count = predicate: list:
|
||||||
builtins.foldl' (
|
builtins.foldl' (
|
||||||
total: value:
|
total: value:
|
||||||
|
@ -148,7 +162,9 @@ lib: {
|
||||||
0
|
0
|
||||||
list;
|
list;
|
||||||
|
|
||||||
# TODO: Document this.
|
## Remove duplicate items from a list.
|
||||||
|
##
|
||||||
|
## @type List -> List
|
||||||
unique = list: let
|
unique = list: let
|
||||||
filter = result: value:
|
filter = result: value:
|
||||||
if builtins.elem value result
|
if builtins.elem value result
|
||||||
|
|
174
lib/src/lists/default.test.nix
Normal file
174
lib/src/lists/default.test.nix
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
let
|
||||||
|
lib = import ./../default.nix;
|
||||||
|
in {
|
||||||
|
"from" = {
|
||||||
|
"any" = {
|
||||||
|
"returns a list containing the value" = let
|
||||||
|
expected = [1];
|
||||||
|
actual = lib.lists.from.any 1;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
|
||||||
|
"returns the value if the value was already a list" = let
|
||||||
|
expected = [1];
|
||||||
|
actual = lib.lists.from.any expected;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
"sort" = {
|
||||||
|
"natural" = {
|
||||||
|
"sorts a list of strings" = let
|
||||||
|
expected = ["1" "a" "a0" "a1" "b" "c"];
|
||||||
|
actual = lib.lists.sort.natural ["c" "a" "b" "a1" "a0" "1"];
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
"mapWithIndex" = {
|
||||||
|
"maps a list using index 0" = let
|
||||||
|
expected = ["0: a" "1: b" "2: c"];
|
||||||
|
actual = lib.lists.mapWithIndex (i: v: "${builtins.toString i}: ${v}") ["a" "b" "c"];
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"mapWithIndex1" = {
|
||||||
|
"maps a list using index 1" = let
|
||||||
|
expected = ["1: a" "2: b" "3: c"];
|
||||||
|
actual = lib.lists.mapWithIndex1 (i: v: "${builtins.toString i}: ${v}") ["a" "b" "c"];
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"compare" = {
|
||||||
|
"compares two lists" = {
|
||||||
|
"returns -1 if the first list is smaller" = let
|
||||||
|
expected = -1;
|
||||||
|
actual = lib.lists.compare lib.numbers.compare [1 2 3] [1 2 4];
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
"returns 1 if the first list is larger" = let
|
||||||
|
expected = 1;
|
||||||
|
actual = lib.lists.compare lib.numbers.compare [1 2 4] [1 2 3];
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
"returns 0 if the lists are equal" = let
|
||||||
|
expected = 0;
|
||||||
|
actual = lib.lists.compare lib.numbers.compare [1 2 3] [1 2 3];
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
"last" = {
|
||||||
|
"returns the last element of a list" = let
|
||||||
|
expected = 3;
|
||||||
|
actual = lib.lists.last [1 2 3];
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
|
||||||
|
"fails if the list is empty" = let
|
||||||
|
actual = lib.lists.last [];
|
||||||
|
evaluated = builtins.tryEval actual;
|
||||||
|
in
|
||||||
|
!evaluated.success;
|
||||||
|
};
|
||||||
|
|
||||||
|
"slice" = {
|
||||||
|
"slices a list" = {
|
||||||
|
"slices a list from the start" = let
|
||||||
|
expected = [1 2];
|
||||||
|
actual = lib.lists.slice 0 2 [1 2 3];
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
"slices a list from the end" = let
|
||||||
|
expected = [2 3];
|
||||||
|
actual = lib.lists.slice 1 3 [1 2 3];
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
"slices a list from the middle" = let
|
||||||
|
expected = [2];
|
||||||
|
actual = lib.lists.slice 1 1 [1 2 3];
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
"take" = {
|
||||||
|
"takes the first n elements" = let
|
||||||
|
expected = [1 2];
|
||||||
|
actual = lib.lists.take 2 [1 2 3];
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"drop" = {
|
||||||
|
"drops the first n elements" = let
|
||||||
|
expected = [3];
|
||||||
|
actual = lib.lists.drop 2 [1 2 3];
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"reverse" = {
|
||||||
|
"reverses a list" = let
|
||||||
|
expected = [3 2 1];
|
||||||
|
actual = lib.lists.reverse [1 2 3];
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"intersperse" = {
|
||||||
|
"intersperses a list with a separator" = let
|
||||||
|
expected = [1 "-" 2 "-" 3];
|
||||||
|
actual = lib.lists.intersperse "-" [1 2 3];
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
|
||||||
|
"handles lists with less than 2 elements" = let
|
||||||
|
expected = [1];
|
||||||
|
actual = lib.lists.intersperse "-" [1];
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"range" = {
|
||||||
|
"returns a range of numbers" = let
|
||||||
|
expected = [1 2 3 4 5];
|
||||||
|
actual = lib.lists.range 1 5;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"when" = {
|
||||||
|
"returns the list if the condition is true" = let
|
||||||
|
expected = [1 2 3];
|
||||||
|
actual = lib.lists.when true [1 2 3];
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
"returns an empty list if the condition is false" = let
|
||||||
|
expected = [];
|
||||||
|
actual = lib.lists.when false [1 2 3];
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"count" = {
|
||||||
|
"counts the number of elements in a list" = let
|
||||||
|
expected = 2;
|
||||||
|
actual = lib.lists.count (value: value < 3) [1 2 3];
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"unique" = {
|
||||||
|
"removes duplicate elements" = let
|
||||||
|
expected = [1 2 3];
|
||||||
|
actual = lib.lists.unique [1 2 3 1 2 3];
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,12 +1,16 @@
|
||||||
lib: {
|
lib: {
|
||||||
math = {
|
math = {
|
||||||
# TODO: Document this.
|
## Return the smaller of two numbers.
|
||||||
|
##
|
||||||
|
## @type Int -> Int -> Int
|
||||||
min = x: y:
|
min = x: y:
|
||||||
if x < y
|
if x < y
|
||||||
then x
|
then x
|
||||||
else y;
|
else y;
|
||||||
|
|
||||||
# TODO: Document this.
|
## Return the larger of two numbers.
|
||||||
|
##
|
||||||
|
## @type Int -> Int -> Int
|
||||||
max = x: y:
|
max = x: y:
|
||||||
if x > y
|
if x > y
|
||||||
then x
|
then x
|
||||||
|
|
19
lib/src/math/default.test.nix
Normal file
19
lib/src/math/default.test.nix
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
let
|
||||||
|
lib = import ./../default.nix;
|
||||||
|
in {
|
||||||
|
"min" = {
|
||||||
|
"returns the smaller number" = let
|
||||||
|
expected = 1;
|
||||||
|
actual = lib.math.min 1 2;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"max" = {
|
||||||
|
"returns the larger number" = let
|
||||||
|
expected = 2;
|
||||||
|
actual = lib.math.max 1 2;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ lib: {
|
||||||
from = {
|
from = {
|
||||||
## Create a module from a JSON file.
|
## Create a module from a JSON file.
|
||||||
##
|
##
|
||||||
|
## @notest
|
||||||
## @type Path -> Module
|
## @type Path -> Module
|
||||||
json = file: {
|
json = file: {
|
||||||
__file__ = file;
|
__file__ = file;
|
||||||
|
@ -11,6 +12,7 @@ lib: {
|
||||||
|
|
||||||
## Create a module from a TOML file.
|
## Create a module from a TOML file.
|
||||||
##
|
##
|
||||||
|
## @notest
|
||||||
## @type Path -> Module
|
## @type Path -> Module
|
||||||
toml = file: {
|
toml = file: {
|
||||||
__file__ = file;
|
__file__ = file;
|
||||||
|
@ -19,7 +21,12 @@ lib: {
|
||||||
};
|
};
|
||||||
|
|
||||||
apply = {
|
apply = {
|
||||||
# TODO: Document this.
|
## Apply custom definitions such as `merge` and `when` to a definition.
|
||||||
|
## Note that this function does not perform actiosn like `merge`, but
|
||||||
|
## instead pulls out the merge contents to be processed by the module
|
||||||
|
## system.
|
||||||
|
##
|
||||||
|
## @type Definition -> List (Definition | (List Definition))
|
||||||
properties = definition:
|
properties = definition:
|
||||||
if lib.types.is "merge" definition
|
if lib.types.is "merge" definition
|
||||||
then builtins.concatMap lib.modules.apply.properties definition.content
|
then builtins.concatMap lib.modules.apply.properties definition.content
|
||||||
|
@ -32,7 +39,11 @@ lib: {
|
||||||
else []
|
else []
|
||||||
else [definition];
|
else [definition];
|
||||||
|
|
||||||
# TODO: Document this.
|
## Apply overrides for a definition. This uses the priority system
|
||||||
|
## to determine which definition to use. The most important (lowest
|
||||||
|
## priority) choice will be used.
|
||||||
|
##
|
||||||
|
## @type List Definition -> { highestPriority :: Int, values :: List (Definition | (List Definition)) }
|
||||||
overrides = definitions: let
|
overrides = definitions: let
|
||||||
getPriority = definition:
|
getPriority = definition:
|
||||||
if lib.types.is "override" definition.value
|
if lib.types.is "override" definition.value
|
||||||
|
@ -64,7 +75,9 @@ lib: {
|
||||||
definitions;
|
definitions;
|
||||||
};
|
};
|
||||||
|
|
||||||
# TODO: Document this.
|
## Apply ordering for prioritized definitions.
|
||||||
|
##
|
||||||
|
## @type List Definition -> List Definition
|
||||||
order = definitions: let
|
order = definitions: let
|
||||||
normalize = definition:
|
normalize = definition:
|
||||||
if lib.types.is "order" definition
|
if lib.types.is "order" definition
|
||||||
|
@ -80,7 +93,10 @@ lib: {
|
||||||
in
|
in
|
||||||
builtins.sort compare normalized;
|
builtins.sort compare normalized;
|
||||||
|
|
||||||
# TODO: Document this.
|
## Normalize the type of an option. This will set a default type if none
|
||||||
|
## was provided.
|
||||||
|
##
|
||||||
|
## @type List String -> Option -> Option
|
||||||
fixup = location: option:
|
fixup = location: option:
|
||||||
if option.type.getSubModules or null == null
|
if option.type.getSubModules or null == null
|
||||||
then
|
then
|
||||||
|
@ -95,32 +111,43 @@ lib: {
|
||||||
options = [];
|
options = [];
|
||||||
};
|
};
|
||||||
|
|
||||||
# TODO: Document this.
|
## Invert the structure of `merge`, `when`, and `override` definitions so
|
||||||
|
## that they apply to each individual attribute in their respective sets.
|
||||||
|
## Note that this function _only_ supports attribute sets within specialized
|
||||||
|
## definitions such as `when` and `override`. Other values like lists will
|
||||||
|
## throw a type error.
|
||||||
|
##
|
||||||
|
## @type Definition -> List Definition
|
||||||
invert = config:
|
invert = config:
|
||||||
if lib.types.is "merge" config
|
if lib.types.is "merge" config
|
||||||
then builtins.concatMap lib.modules.apply.invert config.content
|
then builtins.concatMap lib.modules.apply.invert config.content
|
||||||
else if lib.types.is "when" config
|
else if lib.types.is "when" config
|
||||||
then
|
then
|
||||||
builtins.map
|
builtins.map
|
||||||
(builtins.mapAttrs (key: value: lib.modules.when config.condition value))
|
(builtins.mapAttrs (name: value: lib.modules.when config.condition value))
|
||||||
(lib.modules.apply.invert config.content)
|
(lib.modules.apply.invert config.content)
|
||||||
else if lib.types.is "override" config
|
else if lib.types.is "override" config
|
||||||
then
|
then
|
||||||
builtins.map
|
builtins.map
|
||||||
(builtins.mapAttrs (key: value: lib.modules.override config.priority value))
|
(builtins.mapAttrs (name: value: lib.modules.override config.priority value))
|
||||||
(lib.modules.apply.invert config.content)
|
(lib.modules.apply.invert config.content)
|
||||||
else [config];
|
else [config];
|
||||||
};
|
};
|
||||||
|
|
||||||
validate = {
|
validate = {
|
||||||
# TODO: Document this.
|
## Check that a module only specifies supported attributes.
|
||||||
|
##
|
||||||
|
## @type Attrs -> Bool
|
||||||
keys = module: let
|
keys = module: let
|
||||||
invalid = builtins.removeAttrs module lib.modules.VALID_KEYS;
|
invalid = builtins.removeAttrs module lib.modules.VALID_KEYS;
|
||||||
in
|
in
|
||||||
invalid == {};
|
invalid == {};
|
||||||
};
|
};
|
||||||
|
|
||||||
# TODO: Document this.
|
## Modules only support certain keys at the root level. This list determines
|
||||||
|
## the valid attributes that users can supply.
|
||||||
|
##
|
||||||
|
## @type List String
|
||||||
VALID_KEYS = [
|
VALID_KEYS = [
|
||||||
"__file__"
|
"__file__"
|
||||||
"__key__"
|
"__key__"
|
||||||
|
@ -132,7 +159,10 @@ lib: {
|
||||||
"meta"
|
"meta"
|
||||||
];
|
];
|
||||||
|
|
||||||
# TODO: Document this.
|
## Normalize a module to a standard structure. All other information will be
|
||||||
|
## lost in the conversion.
|
||||||
|
##
|
||||||
|
## @type String -> String -> Attrs -> Module
|
||||||
normalize = file: key: module: let
|
normalize = file: key: module: let
|
||||||
invalid = builtins.removeAttrs module lib.modules.VALID_KEYS;
|
invalid = builtins.removeAttrs module lib.modules.VALID_KEYS;
|
||||||
invalidKeys = builtins.concatStringsSep ", " (builtins.attrNames invalid);
|
invalidKeys = builtins.concatStringsSep ", " (builtins.attrNames invalid);
|
||||||
|
@ -159,7 +189,11 @@ lib: {
|
||||||
}
|
}
|
||||||
else builtins.throw "Module `${key}` has unsupported attribute(s): ${invalidKeys}";
|
else builtins.throw "Module `${key}` has unsupported attribute(s): ${invalidKeys}";
|
||||||
|
|
||||||
# TODO: Document this.
|
## 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
|
||||||
|
## be evaluated and the result will be returned.
|
||||||
|
##
|
||||||
|
## @type String -> Attrs | (Attrs -> Attrs) -> Attrs -> Attrs
|
||||||
resolve = key: module: args: let
|
resolve = key: module: args: let
|
||||||
dynamicArgs =
|
dynamicArgs =
|
||||||
builtins.mapAttrs
|
builtins.mapAttrs
|
||||||
|
@ -175,24 +209,38 @@ lib: {
|
||||||
then lib.fp.withDynamicArgs module (args // dynamicArgs)
|
then lib.fp.withDynamicArgs module (args // dynamicArgs)
|
||||||
else module;
|
else module;
|
||||||
|
|
||||||
# TODO: Document this.
|
## The default priority to set for values that do not have one provided.
|
||||||
|
##
|
||||||
|
## @type Int
|
||||||
DEFAULT_PRIORITY = 100;
|
DEFAULT_PRIORITY = 100;
|
||||||
|
|
||||||
## Allow for sorting the values provided to a module by priority. The
|
## Allow for sorting the values provided to a module by priority. The
|
||||||
## most important value will be used.
|
## most important value will be used.
|
||||||
##
|
##
|
||||||
## @type Int -> a -> Priority a
|
## @notest
|
||||||
|
## @type Int -> a -> Attrs
|
||||||
order = priority: value: {
|
order = priority: value: {
|
||||||
__type__ = "order";
|
__type__ = "order";
|
||||||
inherit priority value;
|
inherit priority value;
|
||||||
};
|
};
|
||||||
|
|
||||||
orders = {
|
orders = {
|
||||||
# TODO: Document this.
|
## Order a value before others.
|
||||||
|
##
|
||||||
|
## @notest
|
||||||
|
## @type a -> Attrs
|
||||||
before = lib.modules.order 500;
|
before = lib.modules.order 500;
|
||||||
# TODO: Document this.
|
|
||||||
|
## Use the default ordering for a value.
|
||||||
|
##
|
||||||
|
## @notest
|
||||||
|
## @type a -> Attrs
|
||||||
default = lib.modules.order 1000;
|
default = lib.modules.order 1000;
|
||||||
# TODO: Document this.
|
|
||||||
|
## Order a value after others.
|
||||||
|
##
|
||||||
|
## @notest
|
||||||
|
## @type a -> Attrs
|
||||||
after = lib.modules.order 1500;
|
after = lib.modules.order 1500;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -201,36 +249,70 @@ lib: {
|
||||||
## @type List Module -> List (String | Path)
|
## @type List Module -> List (String | Path)
|
||||||
getFiles = builtins.map (module: module.__file__);
|
getFiles = builtins.map (module: module.__file__);
|
||||||
|
|
||||||
# TODO: Document this.
|
## Create a conditional value which is only used when the condition's
|
||||||
|
## value is `true`.
|
||||||
|
##
|
||||||
|
## @notest
|
||||||
|
## @type Bool -> a -> When a
|
||||||
when = condition: content: {
|
when = condition: content: {
|
||||||
__type__ = "when";
|
__type__ = "when";
|
||||||
inherit condition content;
|
inherit condition content;
|
||||||
};
|
};
|
||||||
|
|
||||||
# TODO: Document this.
|
## Merge module attribute sets when evaluated in the module system.
|
||||||
|
##
|
||||||
|
## @notest
|
||||||
|
## @type List Attrs -> Attrs
|
||||||
merge = content: {
|
merge = content: {
|
||||||
__type__ = "merge";
|
__type__ = "merge";
|
||||||
inherit content;
|
inherit content;
|
||||||
};
|
};
|
||||||
|
|
||||||
# TODO: Document this.
|
## Create a value override which can replace other definitions if it
|
||||||
|
## has the higher priority.
|
||||||
|
##
|
||||||
|
## @notest
|
||||||
|
## @type Int -> a -> Attrs
|
||||||
override = priority: content: {
|
override = priority: content: {
|
||||||
__type__ = "override";
|
__type__ = "override";
|
||||||
inherit priority content;
|
inherit priority content;
|
||||||
};
|
};
|
||||||
|
|
||||||
overrides = {
|
overrides = {
|
||||||
# TODO: Document this.
|
## Create an override used for setting the `default.value` from an
|
||||||
|
## option. This uses the lowest priority of all predefined overrides.
|
||||||
|
##
|
||||||
|
## @notest
|
||||||
|
## @type a -> Attrs
|
||||||
option = lib.modules.override 1500;
|
option = lib.modules.override 1500;
|
||||||
# TODO: Document this.
|
|
||||||
|
## Create a default override for a value. This uses a very low priority
|
||||||
|
## so that it can easily be overridden.
|
||||||
|
##
|
||||||
|
## @notest
|
||||||
|
## @type a -> Attrs
|
||||||
default = lib.modules.override 1000;
|
default = lib.modules.override 1000;
|
||||||
# TODO: Document this.
|
|
||||||
|
## Create a high priority override for a value. This is not the highest
|
||||||
|
## priority possible, but it will override nearly everything else except
|
||||||
|
## for very explicit cases.
|
||||||
|
##
|
||||||
|
## @notest
|
||||||
|
## @type a -> Attrs
|
||||||
force = lib.modules.override 50;
|
force = lib.modules.override 50;
|
||||||
# TODO: Document this.
|
|
||||||
|
## Create a high priority override intended to be used only for VM targets.
|
||||||
|
## This allows for forcing certain values even if a user has otherwise
|
||||||
|
## specified `lib.modules.overrides.force`.
|
||||||
|
##
|
||||||
|
## @notest
|
||||||
|
## @type a -> Attrs
|
||||||
vm = lib.modules.override 10;
|
vm = lib.modules.override 10;
|
||||||
};
|
};
|
||||||
|
|
||||||
# TODO: Document this.
|
## Combine multiple modules together.
|
||||||
|
##
|
||||||
|
## @type List String -> List Module -> { matched :: Attrs, unmatched :: List Definition }
|
||||||
combine = prefix: modules: let
|
combine = prefix: modules: let
|
||||||
getConfig = module:
|
getConfig = module:
|
||||||
builtins.map
|
builtins.map
|
||||||
|
@ -246,16 +328,15 @@ lib: {
|
||||||
modules;
|
modules;
|
||||||
|
|
||||||
process = prefix: options: configs: let
|
process = prefix: options: configs: let
|
||||||
# TODO: Document this.
|
|
||||||
byName = attr: f: modules:
|
byName = attr: f: modules:
|
||||||
builtins.zipAttrsWith
|
builtins.zipAttrsWith
|
||||||
(lib.fp.const builtins.concatLists)
|
(key: value: builtins.concatLists value)
|
||||||
(builtins.map (
|
(builtins.map (
|
||||||
module: let
|
module: let
|
||||||
subtree = module.${attr};
|
subtree = module.${attr};
|
||||||
in
|
in
|
||||||
if builtins.isAttrs subtree
|
if builtins.isAttrs subtree
|
||||||
then builtins.mapAttrs (key: f module) subtree
|
then builtins.mapAttrs (name: f module) subtree
|
||||||
else builtins.throw "Value for `${builtins.concatStringsSep "." prefix} is of type `${builtins.typeOf subtree}` but an attribute set was expected."
|
else builtins.throw "Value for `${builtins.concatStringsSep "." prefix} is of type `${builtins.typeOf subtree}` but an attribute set was expected."
|
||||||
)
|
)
|
||||||
modules);
|
modules);
|
||||||
|
@ -348,7 +429,7 @@ lib: {
|
||||||
matched = builtins.mapAttrs (key: value: value.matched) resultsByName;
|
matched = builtins.mapAttrs (key: value: value.matched) resultsByName;
|
||||||
|
|
||||||
unmatched =
|
unmatched =
|
||||||
builtins.mapAttrs (key: value: value.unmatched) resultsByName
|
builtins.mapAttrs (name: value: value.unmatched) resultsByName
|
||||||
// builtins.removeAttrs definitionsByName' (builtins.attrNames matched);
|
// builtins.removeAttrs definitionsByName' (builtins.attrNames matched);
|
||||||
in {
|
in {
|
||||||
inherit matched;
|
inherit matched;
|
||||||
|
@ -374,7 +455,10 @@ lib: {
|
||||||
in
|
in
|
||||||
process prefix modules configs;
|
process prefix modules configs;
|
||||||
|
|
||||||
# TODO: Document this.
|
## Run a set of modules. Custom arguments can also be supplied which will
|
||||||
|
## be provided to all modules statically as they are not modifiable.
|
||||||
|
##
|
||||||
|
## @type { modules? :: List (Attrs | (Attrs -> Attrs)), args? :: Attrs, prefix? :: List String } -> { type :: Module, extend :: lib.modules.run, options :: Attrs, config :: Attrs, __module__ :: Attrs }
|
||||||
run = settings @ {
|
run = settings @ {
|
||||||
modules ? [],
|
modules ? [],
|
||||||
args ? {},
|
args ? {},
|
||||||
|
@ -395,7 +479,6 @@ lib: {
|
||||||
prefix = extensions.prefix or settings.prefix or [];
|
prefix = extensions.prefix or settings.prefix or [];
|
||||||
};
|
};
|
||||||
|
|
||||||
# TODO: Document this.
|
|
||||||
collect = let
|
collect = let
|
||||||
load = args: file: key: module: let
|
load = args: file: key: module: let
|
||||||
moduleFromValue = lib.modules.normalize file key (lib.modules.resolve key module args);
|
moduleFromValue = lib.modules.normalize file key (lib.modules.resolve key module args);
|
||||||
|
@ -532,7 +615,7 @@ lib: {
|
||||||
declared =
|
declared =
|
||||||
lib.attrs.mapRecursiveWhen
|
lib.attrs.mapRecursiveWhen
|
||||||
(value: !(lib.types.is "option" value))
|
(value: !(lib.types.is "option" value))
|
||||||
(key: value: value.value)
|
(name: value: value.value)
|
||||||
options;
|
options;
|
||||||
|
|
||||||
freeform = let
|
freeform = let
|
||||||
|
|
|
@ -1,7 +1,315 @@
|
||||||
let
|
let
|
||||||
lib = import ./../default.nix;
|
lib = import ./../default.nix;
|
||||||
in {
|
in {
|
||||||
examples = {
|
"apply" = {
|
||||||
|
"properties" = {
|
||||||
|
"handles normal values" = let
|
||||||
|
expected = [{}];
|
||||||
|
actual = lib.modules.apply.properties {};
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
|
||||||
|
"handles merge" = let
|
||||||
|
expected = [{x = 1;} {x = 2;}];
|
||||||
|
actual = lib.modules.apply.properties (lib.modules.merge [{x = 1;} {x = 2;}]);
|
||||||
|
in
|
||||||
|
actual
|
||||||
|
== expected;
|
||||||
|
|
||||||
|
"handles when" = let
|
||||||
|
expected = [[{x = 1;}]];
|
||||||
|
actual = lib.modules.apply.properties (lib.modules.when true [{x = 1;}]);
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"overrides" = {
|
||||||
|
"handles normal values" = let
|
||||||
|
expected = {
|
||||||
|
highestPriority = 100;
|
||||||
|
values = [
|
||||||
|
{
|
||||||
|
value = 1;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
actual = lib.modules.apply.overrides [
|
||||||
|
{
|
||||||
|
value = 1;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
|
||||||
|
"handles overrides" = let
|
||||||
|
expected = {
|
||||||
|
highestPriority = 100;
|
||||||
|
values = [
|
||||||
|
{value = "world";}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
actual = lib.modules.apply.overrides [
|
||||||
|
{value = "world";}
|
||||||
|
{value = lib.modules.override 101 "hello";}
|
||||||
|
];
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"order" = {
|
||||||
|
"handles normal values" = let
|
||||||
|
expected = [{}];
|
||||||
|
actual = lib.modules.apply.order [{}];
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
|
||||||
|
"handles priority" = let
|
||||||
|
expected = [
|
||||||
|
{
|
||||||
|
value = 1;
|
||||||
|
priority = 10;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
value = 3;
|
||||||
|
priority = 50;
|
||||||
|
}
|
||||||
|
{value = 2;}
|
||||||
|
];
|
||||||
|
actual = lib.modules.apply.order [
|
||||||
|
{
|
||||||
|
value = 1;
|
||||||
|
priority = 10;
|
||||||
|
}
|
||||||
|
{value = 2;}
|
||||||
|
{
|
||||||
|
value = 3;
|
||||||
|
priority = 50;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"fixup" = {
|
||||||
|
"sets default type for option" = let
|
||||||
|
actual = lib.modules.apply.fixup [] (
|
||||||
|
lib.options.create {}
|
||||||
|
);
|
||||||
|
in
|
||||||
|
actual.type.name == "Unspecified";
|
||||||
|
};
|
||||||
|
|
||||||
|
"invert" = {
|
||||||
|
"inverts merge" = let
|
||||||
|
expected = [{x = 1;} {x = 2;}];
|
||||||
|
actual =
|
||||||
|
lib.modules.apply.invert (lib.modules.merge [{x = 1;} {x = 2;}]);
|
||||||
|
in
|
||||||
|
actual
|
||||||
|
== expected;
|
||||||
|
|
||||||
|
"inverts when" = let
|
||||||
|
expected = [
|
||||||
|
{
|
||||||
|
x = {
|
||||||
|
__type__ = "when";
|
||||||
|
condition = true;
|
||||||
|
content = 1;
|
||||||
|
};
|
||||||
|
y = {
|
||||||
|
__type__ = "when";
|
||||||
|
condition = true;
|
||||||
|
content = 2;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
actual = lib.modules.apply.invert (lib.modules.when true {
|
||||||
|
x = 1;
|
||||||
|
y = 2;
|
||||||
|
});
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
|
||||||
|
"inverts overrides" = let
|
||||||
|
expected = [
|
||||||
|
{
|
||||||
|
x = {
|
||||||
|
__type__ = "override";
|
||||||
|
priority = 100;
|
||||||
|
content = 1;
|
||||||
|
};
|
||||||
|
y = {
|
||||||
|
__type__ = "override";
|
||||||
|
priority = 100;
|
||||||
|
content = 2;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
actual = lib.modules.apply.invert (lib.modules.override 100 {
|
||||||
|
x = 1;
|
||||||
|
y = 2;
|
||||||
|
});
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
"validate" = {
|
||||||
|
"keys" = {
|
||||||
|
"handles an empty set" = let
|
||||||
|
value = lib.modules.validate.keys {};
|
||||||
|
in
|
||||||
|
value;
|
||||||
|
|
||||||
|
"handles a valid module" = let
|
||||||
|
value = lib.modules.validate.keys {
|
||||||
|
__file__ = "virtual:aux/example";
|
||||||
|
__key__ = "aux/example";
|
||||||
|
includes = [];
|
||||||
|
excludes = [];
|
||||||
|
options = {};
|
||||||
|
config = {};
|
||||||
|
freeform = null;
|
||||||
|
meta = {};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
value;
|
||||||
|
|
||||||
|
"handles an invalid module" = let
|
||||||
|
value = lib.modules.validate.keys {
|
||||||
|
invalid = null;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
!value;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
"normalize" = {
|
||||||
|
"handles an empty set" = let
|
||||||
|
expected = {
|
||||||
|
__file__ = "/aux/example.nix";
|
||||||
|
__key__ = "example";
|
||||||
|
config = {};
|
||||||
|
excludes = [];
|
||||||
|
includes = [];
|
||||||
|
options = {};
|
||||||
|
};
|
||||||
|
actual = lib.modules.normalize "/aux/example.nix" "example" {};
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
|
||||||
|
"handles an example module" = let
|
||||||
|
expected = {
|
||||||
|
__file__ = "myfile.nix";
|
||||||
|
__key__ = "mykey";
|
||||||
|
config = {
|
||||||
|
x = true;
|
||||||
|
};
|
||||||
|
excludes = [];
|
||||||
|
includes = [];
|
||||||
|
options = {};
|
||||||
|
};
|
||||||
|
actual = lib.modules.normalize "/aux/example.nix" "example" {
|
||||||
|
__file__ = "myfile.nix";
|
||||||
|
__key__ = "mykey";
|
||||||
|
config.x = true;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"resolve" = {
|
||||||
|
"handles an attribute set" = let
|
||||||
|
expected = {config.x = 1;};
|
||||||
|
actual = lib.modules.resolve "example" {config.x = 1;} {};
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
|
||||||
|
"handles a function" = let
|
||||||
|
expected = {config.x = 1;};
|
||||||
|
actual = lib.modules.resolve "example" (lib.fp.const {config.x = 1;}) {};
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
|
||||||
|
"handles a function with arguments" = let
|
||||||
|
expected = {config.x = 1;};
|
||||||
|
actual = lib.modules.resolve "example" (args: {config.x = args.x;}) {x = 1;};
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"getFiles" = {
|
||||||
|
"gets the files for a list of modules" = let
|
||||||
|
expected = ["/aux/example.nix"];
|
||||||
|
actual = lib.modules.getFiles [{__file__ = "/aux/example.nix";}];
|
||||||
|
in
|
||||||
|
actual
|
||||||
|
== expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"combine" = {
|
||||||
|
"handles empty modules" = let
|
||||||
|
expected = {
|
||||||
|
matched = {};
|
||||||
|
unmatched = [];
|
||||||
|
};
|
||||||
|
actual = lib.modules.combine [] [
|
||||||
|
(lib.modules.normalize "/aux/example.nix" "example" {})
|
||||||
|
];
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
|
||||||
|
"handles a single module" = let
|
||||||
|
expected = {
|
||||||
|
matched = {};
|
||||||
|
unmatched = [
|
||||||
|
{
|
||||||
|
__file__ = "/aux/example.nix";
|
||||||
|
prefix = ["x"];
|
||||||
|
value = 1;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
actual = lib.modules.combine [] [
|
||||||
|
(lib.modules.normalize "/aux/example.nix" "example" {
|
||||||
|
config = {
|
||||||
|
x = 1;
|
||||||
|
};
|
||||||
|
})
|
||||||
|
];
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
|
||||||
|
"handles multiple modules" = let
|
||||||
|
unmatched = [
|
||||||
|
{
|
||||||
|
__file__ = "/aux/example2.nix";
|
||||||
|
prefix = ["y"];
|
||||||
|
value = 2;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
actual = lib.modules.combine [] [
|
||||||
|
(lib.modules.normalize "/aux/example1.nix" "example2" {
|
||||||
|
options = {
|
||||||
|
x = lib.options.create {};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = {
|
||||||
|
x = 1;
|
||||||
|
};
|
||||||
|
})
|
||||||
|
(lib.modules.normalize "/aux/example2.nix" "example2" {
|
||||||
|
config = {
|
||||||
|
y = 2;
|
||||||
|
};
|
||||||
|
})
|
||||||
|
];
|
||||||
|
in
|
||||||
|
(actual.unmatched == unmatched)
|
||||||
|
&& actual.matched ? x;
|
||||||
|
};
|
||||||
|
|
||||||
|
"run" = {
|
||||||
"empty" = let
|
"empty" = let
|
||||||
evaluated = lib.modules.run {
|
evaluated = lib.modules.run {
|
||||||
modules = [
|
modules = [
|
||||||
|
@ -73,5 +381,47 @@ in {
|
||||||
actual = evaluated.config.aux.message;
|
actual = evaluated.config.aux.message;
|
||||||
in
|
in
|
||||||
actual == expected;
|
actual == expected;
|
||||||
|
|
||||||
|
"conditional" = let
|
||||||
|
expected = "Hello, World!";
|
||||||
|
evaluated = lib.modules.run {
|
||||||
|
modules = [
|
||||||
|
{
|
||||||
|
options.aux = {
|
||||||
|
message = lib.options.create {
|
||||||
|
type = lib.types.string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
config = {
|
||||||
|
aux = {
|
||||||
|
message = lib.modules.when true expected;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
in
|
||||||
|
evaluated.config.aux.message == expected;
|
||||||
|
|
||||||
|
"conditional list" = let
|
||||||
|
expected = ["Hello, World!"];
|
||||||
|
evaluated = lib.modules.run {
|
||||||
|
modules = [
|
||||||
|
{
|
||||||
|
options.aux = {
|
||||||
|
message = lib.options.create {
|
||||||
|
type = lib.types.list.of lib.types.string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
config = {
|
||||||
|
aux = {
|
||||||
|
message = lib.modules.when true expected;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
in
|
||||||
|
evaluated.config.aux.message == expected;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,7 @@ lib: {
|
||||||
then "F"
|
then "F"
|
||||||
else builtins.throw "Invalid hex digit.";
|
else builtins.throw "Invalid hex digit.";
|
||||||
in
|
in
|
||||||
lib.strings.concatMapSep
|
lib.strings.concatMap
|
||||||
serialize
|
serialize
|
||||||
(lib.numbers.into.base 16 value);
|
(lib.numbers.into.base 16 value);
|
||||||
};
|
};
|
||||||
|
|
57
lib/src/numbers/default.test.nix
Normal file
57
lib/src/numbers/default.test.nix
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
let
|
||||||
|
lib = import ./../default.nix;
|
||||||
|
in {
|
||||||
|
"into" = {
|
||||||
|
"string" = {
|
||||||
|
"converts an int into a string" = let
|
||||||
|
expected = "1";
|
||||||
|
actual = lib.numbers.into.string 1;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
|
||||||
|
"converts a float into a string" = let
|
||||||
|
expected = "1.0";
|
||||||
|
actual = lib.numbers.into.string 1.0;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"base" = {
|
||||||
|
"converts a number into a given base" = let
|
||||||
|
expected = [1 0 0];
|
||||||
|
actual = lib.numbers.into.base 2 4;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
|
||||||
|
"hex" = {
|
||||||
|
"converts a number into a hex string" = let
|
||||||
|
expected = "64";
|
||||||
|
actual = lib.numbers.into.hex 100;
|
||||||
|
in
|
||||||
|
(builtins.trace actual)
|
||||||
|
actual
|
||||||
|
== expected;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
"compare" = {
|
||||||
|
"returns -1 when first is less than second" = let
|
||||||
|
expected = -1;
|
||||||
|
actual = lib.numbers.compare 1 2;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
|
||||||
|
"returns 0 when first is equal to second" = let
|
||||||
|
expected = 0;
|
||||||
|
actual = lib.numbers.compare 1 1;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
|
||||||
|
"returns 1 when first is greater than second" = let
|
||||||
|
expected = 1;
|
||||||
|
actual = lib.numbers.compare 2 1;
|
||||||
|
in
|
||||||
|
actual == expected;
|
||||||
|
};
|
||||||
|
}
|
|
@ -33,7 +33,9 @@ lib: {
|
||||||
# TODO: Improve this error message to show the location and definitions for the option.
|
# TODO: Improve this error message to show the location and definitions for the option.
|
||||||
else builtins.throw "Cannot merge definitions.";
|
else builtins.throw "Cannot merge definitions.";
|
||||||
|
|
||||||
# TODO: Document this.
|
## Merge multiple option definitions together.
|
||||||
|
##
|
||||||
|
## @type Location -> Type -> List Definition
|
||||||
definitions = location: type: definitions: let
|
definitions = location: type: definitions: let
|
||||||
identifier = lib.options.getIdentifier location;
|
identifier = lib.options.getIdentifier location;
|
||||||
resolve = definition: let
|
resolve = definition: let
|
||||||
|
@ -81,11 +83,14 @@ lib: {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
## Merge multiple option declarations together.
|
||||||
|
##
|
||||||
|
## @type Location -> List Option
|
||||||
declarations = location: options: let
|
declarations = location: options: let
|
||||||
merge = result: option: let
|
merge = result: option: let
|
||||||
mergedType = result.type.mergeType option.options.type.functor;
|
mergedType = result.type.mergeType option.options.type.functor;
|
||||||
isTypeMergeable = mergedType != null;
|
isTypeMergeable = mergedType != null;
|
||||||
shared = key: option.options ? ${key} && result ? ${key};
|
shared = name: option.options ? ${name} && result ? ${name};
|
||||||
typeSet = lib.attrs.when ((shared "type") && isTypeMergeable) {
|
typeSet = lib.attrs.when ((shared "type") && isTypeMergeable) {
|
||||||
type = mergedType;
|
type = mergedType;
|
||||||
};
|
};
|
||||||
|
@ -212,9 +217,12 @@ lib: {
|
||||||
## @type List String -> String
|
## @type List String -> String
|
||||||
getIdentifier = location: let
|
getIdentifier = location: let
|
||||||
special = [
|
special = [
|
||||||
"<name>" # attrsOf (submodule {})
|
# lib.types.attrs.of (lib.types.submodule {})
|
||||||
"*" # listOf (submodule {})
|
"<name>"
|
||||||
"<function body>" # functionTo
|
# lib.types.list.of (submodule {})
|
||||||
|
"*"
|
||||||
|
# lib.types.function
|
||||||
|
"<function body>"
|
||||||
];
|
];
|
||||||
escape = part:
|
escape = part:
|
||||||
if builtins.elem part special
|
if builtins.elem part special
|
||||||
|
@ -258,7 +266,9 @@ lib: {
|
||||||
in
|
in
|
||||||
lib.strings.concatMap serialize definitions;
|
lib.strings.concatMap serialize definitions;
|
||||||
|
|
||||||
# TODO: Document this.
|
## Run a set of definitions, calculating the resolved value and associated information.
|
||||||
|
##
|
||||||
|
## @type Location -> Option -> List Definition -> String & { value :: Any, highestPriority :: Int, isDefined :: Bool, files :: List String, definitions :: List Any, definitionsWithLocations :: List Definition }
|
||||||
run = location: option: definitions: let
|
run = location: option: definitions: let
|
||||||
identifier = lib.options.getIdentifier location;
|
identifier = lib.options.getIdentifier location;
|
||||||
|
|
||||||
|
|
3
lib/src/options/default.test.nix
Normal file
3
lib/src/options/default.test.nix
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
let
|
||||||
|
lib = import ./../default.nix;
|
||||||
|
in {}
|
|
@ -12,7 +12,7 @@ lib: {
|
||||||
if builtins.stringLength value <= 207 && validate value != null
|
if builtins.stringLength value <= 207 && validate value != null
|
||||||
then builtins.unsafeDiscardStringContext value
|
then builtins.unsafeDiscardStringContext value
|
||||||
else
|
else
|
||||||
lib.fp.pipe value [
|
lib.fp.pipe [
|
||||||
# Get rid of string context. This is safe under the assumption that the
|
# Get rid of string context. This is safe under the assumption that the
|
||||||
# resulting string is only used as a derivation name
|
# resulting string is only used as a derivation name
|
||||||
builtins.unsafeDiscardStringContext
|
builtins.unsafeDiscardStringContext
|
||||||
|
@ -36,6 +36,7 @@ lib: {
|
||||||
if builtins.stringLength x == 0
|
if builtins.stringLength x == 0
|
||||||
then "unknown"
|
then "unknown"
|
||||||
else x)
|
else x)
|
||||||
];
|
]
|
||||||
|
value;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
3
lib/src/packages/default.test.nix
Normal file
3
lib/src/packages/default.test.nix
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
let
|
||||||
|
lib = import ./../default.nix;
|
||||||
|
in {}
|
3
lib/src/paths/default.test.nix
Normal file
3
lib/src/paths/default.test.nix
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
let
|
||||||
|
lib = import ./../default.nix;
|
||||||
|
in {}
|
|
@ -17,10 +17,7 @@ lib: {
|
||||||
## definitions. Unlike `fix`, the resulting value is also given a `__unfix__`
|
## definitions. Unlike `fix`, the resulting value is also given a `__unfix__`
|
||||||
## attribute that is set to the original function passed to `fix'`.
|
## attribute that is set to the original function passed to `fix'`.
|
||||||
##
|
##
|
||||||
## FIXME: The below type annotation should include a mention of the `__unfix__`
|
## @type (a -> a) -> a & { __unfix__ :: (a -> a) }
|
||||||
## value.
|
|
||||||
##
|
|
||||||
## @type (a -> a) -> a
|
|
||||||
fix' = f: let
|
fix' = f: let
|
||||||
x =
|
x =
|
||||||
f x
|
f x
|
||||||
|
|
3
lib/src/points/default.test.nix
Normal file
3
lib/src/points/default.test.nix
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
let
|
||||||
|
lib = import ./../default.nix;
|
||||||
|
in {}
|
3
lib/src/strings/default.test.nix
Normal file
3
lib/src/strings/default.test.nix
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
let
|
||||||
|
lib = import ./../default.nix;
|
||||||
|
in {}
|
|
@ -151,7 +151,7 @@ lib: {
|
||||||
# TODO: Document this.
|
# TODO: Document this.
|
||||||
unspecified = lib.types.create {
|
unspecified = lib.types.create {
|
||||||
name = "Unspecified";
|
name = "Unspecified";
|
||||||
description = "unspecified value";
|
description = "unspecified type";
|
||||||
};
|
};
|
||||||
|
|
||||||
# TODO: Document this.
|
# TODO: Document this.
|
||||||
|
@ -388,20 +388,20 @@ lib: {
|
||||||
merge = location: definitions: let
|
merge = location: definitions: let
|
||||||
normalize = definition:
|
normalize = definition:
|
||||||
builtins.mapAttrs
|
builtins.mapAttrs
|
||||||
(key: value: {
|
(name: value: {
|
||||||
__file__ = definition.__file__;
|
__file__ = definition.__file__;
|
||||||
value = value;
|
value = value;
|
||||||
})
|
})
|
||||||
definition.value;
|
definition.value;
|
||||||
normalized = builtins.map normalize definitions;
|
normalized = builtins.map normalize definitions;
|
||||||
zipper = key: definitions:
|
zipper = name: definitions:
|
||||||
(lib.options.merge.definitions (location ++ [key]) type definitions).optional;
|
(lib.options.merge.definitions (location ++ [name]) type definitions).optional;
|
||||||
filtered =
|
filtered =
|
||||||
lib.attrs.filter
|
lib.attrs.filter
|
||||||
(key: value: value ? value)
|
(name: value: value ? value)
|
||||||
(builtins.zipAttrsWith zipper normalized);
|
(builtins.zipAttrsWith zipper normalized);
|
||||||
in
|
in
|
||||||
builtins.mapAttrs (key: value: value.value) filtered;
|
builtins.mapAttrs (name: value: value.value) filtered;
|
||||||
getSubOptions = prefix: type.getSubOptions (prefix ++ ["<name>"]);
|
getSubOptions = prefix: type.getSubOptions (prefix ++ ["<name>"]);
|
||||||
getSubModules = type.getSubModules;
|
getSubModules = type.getSubModules;
|
||||||
withSubModules = modules: lib.types.attrs.of (type.withSubModules modules);
|
withSubModules = modules: lib.types.attrs.of (type.withSubModules modules);
|
||||||
|
@ -421,14 +421,14 @@ lib: {
|
||||||
merge = location: definitions: let
|
merge = location: definitions: let
|
||||||
normalize = definition:
|
normalize = definition:
|
||||||
builtins.mapAttrs
|
builtins.mapAttrs
|
||||||
(key: value: {
|
(name: value: {
|
||||||
__file__ = definition.__file__;
|
__file__ = definition.__file__;
|
||||||
value = value;
|
value = value;
|
||||||
})
|
})
|
||||||
definition.value;
|
definition.value;
|
||||||
normalized = builtins.map normalize definitions;
|
normalized = builtins.map normalize definitions;
|
||||||
zipper = key: definitions: let
|
zipper = name: definitions: let
|
||||||
merged = lib.options.merge.definitions (location ++ [key]) type definitions;
|
merged = lib.options.merge.definitions (location ++ [name]) type definitions;
|
||||||
in
|
in
|
||||||
merged.optional.value or type.fallback.value or merged.merged;
|
merged.optional.value or type.fallback.value or merged.merged;
|
||||||
in
|
in
|
||||||
|
@ -494,7 +494,14 @@ lib: {
|
||||||
j: value: let
|
j: value: let
|
||||||
resolved =
|
resolved =
|
||||||
lib.options.merge.definitions
|
lib.options.merge.definitions
|
||||||
(location ++ ["[definition ${builtins.toString i}-entry ${j}]"]);
|
(location ++ ["[definition ${builtins.toString i}-entry ${j}]"])
|
||||||
|
type
|
||||||
|
[
|
||||||
|
{
|
||||||
|
file = definition.file;
|
||||||
|
value = value;
|
||||||
|
}
|
||||||
|
];
|
||||||
in
|
in
|
||||||
resolved.optional
|
resolved.optional
|
||||||
)
|
)
|
||||||
|
@ -503,7 +510,7 @@ lib: {
|
||||||
definitions;
|
definitions;
|
||||||
merged = builtins.concatLists result;
|
merged = builtins.concatLists result;
|
||||||
filtered = builtins.filter (definition: definition ? value) merged;
|
filtered = builtins.filter (definition: definition ? value) merged;
|
||||||
values = lib.optiosn.getDefinitionValues filtered;
|
values = lib.options.getDefinitionValues filtered;
|
||||||
in
|
in
|
||||||
values;
|
values;
|
||||||
getSubOptions = prefix: type.getSubOptions (prefix ++ ["*"]);
|
getSubOptions = prefix: type.getSubOptions (prefix ++ ["*"]);
|
||||||
|
|
3
lib/src/types/default.test.nix
Normal file
3
lib/src/types/default.test.nix
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
let
|
||||||
|
lib = import ./../default.nix;
|
||||||
|
in {}
|
3
lib/src/versions/default.test.nix
Normal file
3
lib/src/versions/default.test.nix
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
let
|
||||||
|
lib = import ./../default.nix;
|
||||||
|
in {}
|
18
lib/test.sh
Executable file
18
lib/test.sh
Executable file
|
@ -0,0 +1,18 @@
|
||||||
|
#!/usr/bin/env nix-shell
|
||||||
|
#!nix-shell -i bash -p jq
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
namespace=${1:-}
|
||||||
|
|
||||||
|
if [ -z "$namespace" ]; then
|
||||||
|
nix eval -f ./default.test.nix --show-trace --raw
|
||||||
|
else
|
||||||
|
if [ -d "./src/$namespace" ]; then
|
||||||
|
nix eval -f "./src/$namespace/default.test.nix" --show-trace --json | jq
|
||||||
|
else
|
||||||
|
echo "Namespace $namespace not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
Loading…
Reference in a new issue