# SPDX-FileCopyrightText: 2024 Tom Hubrecht # # 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 "🦄" => [ "�" "�" "�" "�" ] ``` ::: */ 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 { }; 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 "evaluation warning: ${msg}" ( abort "NIX_ABORT_ON_WARN=true; warnings are treated as unrecoverable errors." ) else builtins.trace "evaluation warning: ${msg}" v ); }