1{ lib }:
2
3let
4 inherit (lib.strings)
5 concatStringsSep
6 ;
7 inherit (lib.lists)
8 filter
9 ;
10 inherit (lib.trivial)
11 showWarnings
12 ;
13in
14rec {
15
16 /**
17 Throw if pred is false, else return pred.
18 Intended to be used to augment asserts with helpful error messages.
19
20 # Inputs
21
22 `pred`
23
24 : Predicate that needs to succeed, otherwise `msg` is thrown
25
26 `msg`
27
28 : Message to throw in case `pred` fails
29
30 # Type
31
32 ```
33 assertMsg :: Bool -> String -> Bool
34 ```
35
36 # Examples
37 :::{.example}
38 ## `lib.asserts.assertMsg` usage example
39
40 ```nix
41 assertMsg false "nope"
42 stderr> error: nope
43 assert assertMsg ("foo" == "bar") "foo is not bar, silly"; ""
44 stderr> error: foo is not bar, silly
45 ```
46
47 :::
48 */
49 # TODO(Profpatsch): add tests that check stderr
50 assertMsg = pred: msg: pred || builtins.throw msg;
51
52 /**
53 Specialized `assertMsg` for checking if `val` is one of the elements
54 of the list `xs`. Useful for checking enums.
55
56 # Inputs
57
58 `name`
59
60 : The name of the variable the user entered `val` into, for inclusion in the error message
61
62 `val`
63
64 : The value of what the user provided, to be compared against the values in `xs`
65
66 `xs`
67
68 : The list of valid values
69
70 # Type
71
72 ```
73 assertOneOf :: String -> ComparableVal -> List ComparableVal -> Bool
74 ```
75
76 # Examples
77 :::{.example}
78 ## `lib.asserts.assertOneOf` usage example
79
80 ```nix
81 let sslLibrary = "libressl";
82 in assertOneOf "sslLibrary" sslLibrary [ "openssl" "bearssl" ]
83 stderr> error: sslLibrary must be one of [
84 stderr> "openssl"
85 stderr> "bearssl"
86 stderr> ], but is: "libressl"
87 ```
88
89 :::
90 */
91 assertOneOf =
92 name: val: xs:
93 assertMsg (lib.elem val xs) "${name} must be one of ${lib.generators.toPretty { } xs}, but is: ${
94 lib.generators.toPretty { } val
95 }";
96
97 /**
98 Specialized `assertMsg` for checking if every one of `vals` is one of the elements
99 of the list `xs`. Useful for checking lists of supported attributes.
100
101 # Inputs
102
103 `name`
104
105 : The name of the variable the user entered `val` into, for inclusion in the error message
106
107 `vals`
108
109 : The list of values of what the user provided, to be compared against the values in `xs`
110
111 `xs`
112
113 : The list of valid values
114
115 # Type
116
117 ```
118 assertEachOneOf :: String -> List ComparableVal -> List ComparableVal -> Bool
119 ```
120
121 # Examples
122 :::{.example}
123 ## `lib.asserts.assertEachOneOf` usage example
124
125 ```nix
126 let sslLibraries = [ "libressl" "bearssl" ];
127 in assertEachOneOf "sslLibraries" sslLibraries [ "openssl" "bearssl" ]
128 stderr> error: each element in sslLibraries must be one of [
129 stderr> "openssl"
130 stderr> "bearssl"
131 stderr> ], but is: [
132 stderr> "libressl"
133 stderr> "bearssl"
134 stderr> ]
135 ```
136
137 :::
138 */
139 assertEachOneOf =
140 name: vals: xs:
141 assertMsg (lib.all (val: lib.elem val xs) vals)
142 "each element in ${name} must be one of ${lib.generators.toPretty { } xs}, but is: ${
143 lib.generators.toPretty { } vals
144 }";
145
146 /**
147 Wrap a value with logic that throws an error when assertions
148 fail and emits any warnings.
149
150 # Inputs
151
152 `assertions`
153
154 : A list of assertions. If any of their `assertion` attrs is `false`, their `message` attrs will be emitted in a `throw`.
155
156 `warnings`
157
158 : A list of strings to emit as warnings. This function does no filtering on this list.
159
160 `val`
161
162 : A value to return, wrapped in `warn`, if a `throw` is not necessary.
163
164 # Type
165
166 ```
167 checkAssertWarn :: [ { assertion :: Bool; message :: String } ] -> [ String ] -> Any -> Any
168 ```
169
170 # Examples
171 :::{.example}
172 ## `lib.asserts.checkAssertWarn` usage example
173 ```nix
174 checkAssertWarn
175 [ { assertion = false; message = "Will fail"; } ]
176 [ ]
177 null
178 stderr> error:
179 stderr> Failed assertions:
180 stderr> - Will fail
181
182 checkAssertWarn
183 [ { assertion = true; message = "Will not fail"; } ]
184 [ "Will warn" ]
185 null
186 stderr> evaluation warning: Will warn
187 null
188 ```
189
190 :::
191 */
192 checkAssertWarn =
193 assertions: warnings: val:
194 let
195 failedAssertions = map (x: x.message) (filter (x: !x.assertion) assertions);
196 in
197 if failedAssertions != [ ] then
198 throw "\nFailed assertions:\n${concatStringsSep "\n" (map (x: "- ${x}") failedAssertions)}"
199 else
200 showWarnings warnings val;
201
202}