forked from DGNum/infrastructure
Tom Hubrecht
88d9b8c3e3
Signed-off-by: Tom Hubrecht <tom.hubrecht@dgnum.eu> Acked-by: Ryan Lahfa <ryan.lahfa@dgnum.eu> Acked-by: Maurice Debray <maurice.debray@dgnum.eu> Acked-by: Lubin Bailly <lubin.bailly@dgnum.eu> Acked-by: Jean-Marc Gailis <jean-marc.gailis@dgnum.eu> as the legal authority, at the time of writing, in DGNum. Acked-by: Elias Coppens <elias.coppens@dgnum.eu> as a member, at the time of writing, of the DGNum executive counsel.
466 lines
9.7 KiB
Nix
466 lines
9.7 KiB
Nix
# SPDX-FileCopyrightText: 2024 Tom Hubrecht <tom.hubrecht@dgnum.eu>
|
||
#
|
||
# SPDX-License-Identifier: EUPL-1.2
|
||
|
||
###
|
||
# Collection of nixpkgs library functions, those are necessary for defining our own lib
|
||
#
|
||
# They have been simplified and builtins are used in some places, instead of lib shims.
|
||
|
||
rec {
|
||
/**
|
||
Does the same as the update operator '//' except that attributes are
|
||
merged until the given predicate is verified. The predicate should
|
||
accept 3 arguments which are the path to reach the attribute, a part of
|
||
the first attribute set and a part of the second attribute set. When
|
||
the predicate is satisfied, the value of the first attribute set is
|
||
replaced by the value of the second attribute set.
|
||
|
||
# Inputs
|
||
|
||
`pred`
|
||
|
||
: Predicate, taking the path to the current attribute as a list of strings for attribute names, and the two values at that path from the original arguments.
|
||
|
||
`lhs`
|
||
|
||
: Left attribute set of the merge.
|
||
|
||
`rhs`
|
||
|
||
: Right attribute set of the merge.
|
||
|
||
# Type
|
||
|
||
```
|
||
recursiveUpdateUntil :: ( [ String ] -> AttrSet -> AttrSet -> Bool ) -> AttrSet -> AttrSet -> AttrSet
|
||
```
|
||
|
||
# Examples
|
||
:::{.example}
|
||
## `lib.attrsets.recursiveUpdateUntil` usage example
|
||
|
||
```nix
|
||
recursiveUpdateUntil (path: l: r: path == ["foo"]) {
|
||
# first attribute set
|
||
foo.bar = 1;
|
||
foo.baz = 2;
|
||
bar = 3;
|
||
} {
|
||
#second attribute set
|
||
foo.bar = 1;
|
||
foo.quz = 2;
|
||
baz = 4;
|
||
}
|
||
|
||
=> {
|
||
foo.bar = 1; # 'foo.*' from the second set
|
||
foo.quz = 2; #
|
||
bar = 3; # 'bar' from the first set
|
||
baz = 4; # 'baz' from the second set
|
||
}
|
||
```
|
||
|
||
:::
|
||
*/
|
||
recursiveUpdateUntil =
|
||
pred: lhs: rhs:
|
||
let
|
||
f =
|
||
attrPath:
|
||
builtins.zipAttrsWith (
|
||
n: values:
|
||
let
|
||
here = attrPath ++ [ n ];
|
||
in
|
||
if builtins.length values == 1 || pred here (builtins.elemAt values 1) (builtins.head values) then
|
||
builtins.head values
|
||
else
|
||
f here values
|
||
);
|
||
in
|
||
f [ ] [
|
||
rhs
|
||
lhs
|
||
];
|
||
|
||
/**
|
||
A recursive variant of the update operator ‘//’. The recursion
|
||
stops when one of the attribute values is not an attribute set,
|
||
in which case the right hand side value takes precedence over the
|
||
left hand side value.
|
||
|
||
# Inputs
|
||
|
||
`lhs`
|
||
|
||
: Left attribute set of the merge.
|
||
|
||
`rhs`
|
||
|
||
: Right attribute set of the merge.
|
||
|
||
# Type
|
||
|
||
```
|
||
recursiveUpdate :: AttrSet -> AttrSet -> AttrSet
|
||
```
|
||
|
||
# Examples
|
||
:::{.example}
|
||
## `lib.attrsets.recursiveUpdate` usage example
|
||
|
||
```nix
|
||
recursiveUpdate {
|
||
boot.loader.grub.enable = true;
|
||
boot.loader.grub.device = "/dev/hda";
|
||
} {
|
||
boot.loader.grub.device = "";
|
||
}
|
||
|
||
returns: {
|
||
boot.loader.grub.enable = true;
|
||
boot.loader.grub.device = "";
|
||
}
|
||
```
|
||
|
||
:::
|
||
*/
|
||
recursiveUpdate =
|
||
lhs: rhs:
|
||
recursiveUpdateUntil (
|
||
_: lhs: rhs:
|
||
!(builtins.isAttrs lhs && builtins.isAttrs rhs)
|
||
) lhs rhs;
|
||
|
||
/**
|
||
Determine whether a string has given prefix.
|
||
|
||
# Inputs
|
||
|
||
`pref`
|
||
: Prefix to check for
|
||
|
||
`str`
|
||
: Input string
|
||
|
||
# Type
|
||
|
||
```
|
||
hasPrefix :: string -> string -> bool
|
||
```
|
||
|
||
# Examples
|
||
:::{.example}
|
||
## `lib.strings.hasPrefix` usage example
|
||
|
||
```nix
|
||
hasPrefix "foo" "foobar"
|
||
=> true
|
||
hasPrefix "foo" "barfoo"
|
||
=> false
|
||
```
|
||
|
||
:::
|
||
*/
|
||
hasPrefix = pref: str: (builtins.substring 0 (builtins.stringLength pref) str == pref);
|
||
|
||
/**
|
||
Escape occurrence of the elements of `list` in `string` by
|
||
prefixing it with a backslash.
|
||
|
||
# Inputs
|
||
|
||
`list`
|
||
: 1\. Function argument
|
||
|
||
`string`
|
||
: 2\. Function argument
|
||
|
||
# Type
|
||
|
||
```
|
||
escape :: [string] -> string -> string
|
||
```
|
||
|
||
# Examples
|
||
:::{.example}
|
||
## `lib.strings.escape` usage example
|
||
|
||
```nix
|
||
escape ["(" ")"] "(foo)"
|
||
=> "\\(foo\\)"
|
||
```
|
||
|
||
:::
|
||
*/
|
||
escape = list: builtins.replaceStrings list (builtins.map (c: "\\${c}") list);
|
||
|
||
/**
|
||
Convert a string `s` to a list of characters (i.e. singleton strings).
|
||
This allows you to, e.g., map a function over each character. However,
|
||
note that this will likely be horribly inefficient; Nix is not a
|
||
general purpose programming language. Complex string manipulations
|
||
should, if appropriate, be done in a derivation.
|
||
Also note that Nix treats strings as a list of bytes and thus doesn't
|
||
handle unicode.
|
||
|
||
# Inputs
|
||
|
||
`s`
|
||
: 1\. Function argument
|
||
|
||
# Type
|
||
|
||
```
|
||
stringToCharacters :: string -> [string]
|
||
```
|
||
|
||
# Examples
|
||
:::{.example}
|
||
## `lib.strings.stringToCharacters` usage example
|
||
|
||
```nix
|
||
stringToCharacters ""
|
||
=> [ ]
|
||
stringToCharacters "abc"
|
||
=> [ "a" "b" "c" ]
|
||
stringToCharacters "🦄"
|
||
=> [ "<EFBFBD>" "<EFBFBD>" "<EFBFBD>" "<EFBFBD>" ]
|
||
```
|
||
|
||
:::
|
||
*/
|
||
stringToCharacters = s: builtins.genList (p: builtins.substring p 1 s) (builtins.stringLength s);
|
||
|
||
/**
|
||
Turn a string `s` into an exact regular expression
|
||
|
||
# Inputs
|
||
|
||
`s`
|
||
: 1\. Function argument
|
||
|
||
# Type
|
||
|
||
```
|
||
escapeRegex :: string -> string
|
||
```
|
||
|
||
# Examples
|
||
:::{.example}
|
||
## `lib.strings.escapeRegex` usage example
|
||
|
||
```nix
|
||
escapeRegex "[^a-z]*"
|
||
=> "\\[\\^a-z]\\*"
|
||
```
|
||
|
||
:::
|
||
*/
|
||
escapeRegex = escape (stringToCharacters "\\[{()^$?*+|.");
|
||
|
||
/**
|
||
Appends string context from string like object `src` to `target`.
|
||
|
||
:::{.warning}
|
||
This is an implementation
|
||
detail of Nix and should be used carefully.
|
||
:::
|
||
|
||
Strings in Nix carry an invisible `context` which is a list of strings
|
||
representing store paths. If the string is later used in a derivation
|
||
attribute, the derivation will properly populate the inputDrvs and
|
||
inputSrcs.
|
||
|
||
# Inputs
|
||
|
||
`src`
|
||
: The string to take the context from. If the argument is not a string,
|
||
it will be implicitly converted to a string.
|
||
|
||
`target`
|
||
: The string to append the context to. If the argument is not a string,
|
||
it will be implicitly converted to a string.
|
||
|
||
# Type
|
||
|
||
```
|
||
addContextFrom :: string -> string -> string
|
||
```
|
||
|
||
# Examples
|
||
:::{.example}
|
||
## `lib.strings.addContextFrom` usage example
|
||
|
||
```nix
|
||
pkgs = import <nixpkgs> { };
|
||
addContextFrom pkgs.coreutils "bar"
|
||
=> "bar"
|
||
```
|
||
|
||
The context can be displayed using the `toString` function:
|
||
|
||
```nix
|
||
nix-repl> builtins.getContext (lib.strings.addContextFrom pkgs.coreutils "bar")
|
||
{
|
||
"/nix/store/m1s1d2dk2dqqlw3j90jl3cjy2cykbdxz-coreutils-9.5.drv" = { ... };
|
||
}
|
||
```
|
||
|
||
:::
|
||
*/
|
||
addContextFrom = src: target: builtins.substring 0 0 src + target;
|
||
|
||
/**
|
||
Cut a string with a separator and produces a list of strings which
|
||
were separated by this separator.
|
||
|
||
# Inputs
|
||
|
||
`sep`
|
||
: 1\. Function argument
|
||
|
||
`s`
|
||
: 2\. Function argument
|
||
|
||
# Type
|
||
|
||
```
|
||
splitString :: string -> string -> [string]
|
||
```
|
||
|
||
# Examples
|
||
:::{.example}
|
||
## `lib.strings.splitString` usage example
|
||
|
||
```nix
|
||
splitString "." "foo.bar.baz"
|
||
=> [ "foo" "bar" "baz" ]
|
||
splitString "/" "/usr/local/bin"
|
||
=> [ "" "usr" "local" "bin" ]
|
||
```
|
||
|
||
:::
|
||
*/
|
||
splitString =
|
||
sep: s:
|
||
let
|
||
splits = builtins.filter builtins.isString (
|
||
builtins.split (escapeRegex (builtins.toString sep)) (builtins.toString s)
|
||
);
|
||
in
|
||
builtins.map (addContextFrom s) splits;
|
||
|
||
/**
|
||
Remove duplicate elements from the `list`. O(n^2) complexity.
|
||
|
||
# Inputs
|
||
|
||
`list`
|
||
|
||
: Input list
|
||
|
||
# Type
|
||
|
||
```
|
||
unique :: [a] -> [a]
|
||
```
|
||
|
||
# Examples
|
||
:::{.example}
|
||
## `lib.lists.unique` usage example
|
||
|
||
```nix
|
||
unique [ 3 2 3 4 ]
|
||
=> [ 3 2 4 ]
|
||
```
|
||
|
||
:::
|
||
*/
|
||
unique = builtins.foldl' (acc: e: if builtins.elem e acc then acc else acc ++ [ e ]) [ ];
|
||
|
||
/**
|
||
Flip the order of the arguments of a binary function.
|
||
|
||
# Inputs
|
||
|
||
`f`
|
||
|
||
: 1\. Function argument
|
||
|
||
`a`
|
||
|
||
: 2\. Function argument
|
||
|
||
`b`
|
||
|
||
: 3\. Function argument
|
||
|
||
# Type
|
||
|
||
```
|
||
flip :: (a -> b -> c) -> (b -> a -> c)
|
||
```
|
||
|
||
# Examples
|
||
:::{.example}
|
||
## `lib.trivial.flip` usage example
|
||
|
||
```nix
|
||
flip concat [1] [2]
|
||
=> [ 2 1 ]
|
||
```
|
||
|
||
:::
|
||
*/
|
||
flip =
|
||
f: a: b:
|
||
f b a;
|
||
|
||
/**
|
||
`warn` *`message`* *`value`*
|
||
|
||
Print a warning before returning the second argument.
|
||
|
||
See [`builtins.warn`](https://nix.dev/manual/nix/latest/language/builtins.html#builtins-warn) (Nix >= 2.23).
|
||
On older versions, the Nix 2.23 behavior is emulated with [`builtins.trace`](https://nix.dev/manual/nix/latest/language/builtins.html#builtins-warn), including the [`NIX_ABORT_ON_WARN`](https://nix.dev/manual/nix/latest/command-ref/conf-file#conf-abort-on-warn) behavior, but not the `nix.conf` setting or command line option.
|
||
|
||
# Inputs
|
||
|
||
*`message`* (String)
|
||
|
||
: Warning message to print before evaluating *`value`*.
|
||
|
||
*`value`* (any value)
|
||
|
||
: Value to return as-is.
|
||
|
||
# Type
|
||
|
||
```
|
||
String -> a -> a
|
||
```
|
||
*/
|
||
warn =
|
||
# Since Nix 2.23, https://github.com/NixOS/nix/pull/10592
|
||
builtins.warn or (
|
||
let
|
||
mustAbort = builtins.elem (builtins.getEnv "NIX_ABORT_ON_WARN") [
|
||
"1"
|
||
"true"
|
||
"yes"
|
||
];
|
||
in
|
||
# Do not eta reduce v, so that we have the same strictness as `builtins.warn`.
|
||
msg: v:
|
||
# `builtins.warn` requires a string message, so we enforce that in our implementation, so that callers aren't accidentally incompatible with newer Nix versions.
|
||
assert builtins.isString msg;
|
||
if mustAbort then
|
||
builtins.trace "[1;31mevaluation warning:[0m ${msg}" (
|
||
abort "NIX_ABORT_ON_WARN=true; warnings are treated as unrecoverable errors."
|
||
)
|
||
else
|
||
builtins.trace "[1;35mevaluation warning:[0m ${msg}" v
|
||
);
|
||
}
|