error_rail/validation/core.rs
1use crate::types::ErrorVec;
2use serde::{Deserialize, Serialize};
3use smallvec::smallvec;
4
5/// Applicative-style validation that accumulates many errors instead of failing fast.
6///
7/// `Validation<E, A>` represents a computation that either succeeds with a value of type `A`
8/// or fails with one or more errors of type `E`. Unlike `Result`, which fails fast on the first
9/// error, `Validation` accumulates all errors, making it ideal for form validation and other
10/// scenarios where you want to collect all problems at once.
11///
12/// # Serde Support
13///
14/// `Validation` implements `Serialize` and `Deserialize` when `E` and `A` do.
15/// This makes it easy to use in API responses or configuration files.
16///
17/// # Type Parameters
18///
19/// * `E` - The error type
20/// * `A` - The success value type
21///
22/// # Variants
23///
24/// * `Valid(A)` - Contains a successful value
25/// * `Invalid(ErrorVec<E>)` - Contains one or more errors
26///
27/// # Examples
28///
29/// ```
30/// use error_rail::validation::Validation;
31///
32/// let valid = Validation::<&str, i32>::valid(42);
33/// assert!(valid.is_valid());
34///
35/// let invalid = Validation::<&str, i32>::invalid("error");
36/// assert!(invalid.is_invalid());
37/// ```
38#[must_use]
39#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Debug, Hash, Serialize, Deserialize)]
40pub enum Validation<E, A> {
41 Valid(A),
42 Invalid(ErrorVec<E>),
43}
44
45impl<E, A> Validation<E, A> {
46 /// Creates a valid value.
47 ///
48 /// # Arguments
49 ///
50 /// * `value` - The success value to wrap
51 ///
52 /// # Examples
53 ///
54 /// ```
55 /// use error_rail::validation::Validation;
56 ///
57 /// let v = Validation::<&str, i32>::valid(42);
58 /// assert_eq!(v.into_value(), Some(42));
59 /// ```
60 #[must_use]
61 #[inline]
62 pub fn valid(value: A) -> Self {
63 Self::Valid(value)
64 }
65
66 /// Creates an invalid value from a single error.
67 ///
68 /// # Arguments
69 ///
70 /// * `error` - The error to wrap
71 ///
72 /// # Examples
73 ///
74 /// ```
75 /// use error_rail::validation::Validation;
76 ///
77 /// let v = Validation::<&str, ()>::invalid("missing field");
78 /// assert!(v.is_invalid());
79 /// ```
80 #[must_use]
81 #[inline]
82 pub fn invalid(error: E) -> Self {
83 Self::Invalid(smallvec![error])
84 }
85
86 /// Creates an invalid value from an iterator of errors.
87 ///
88 /// # Arguments
89 ///
90 /// * `errors` - An iterator of errors to collect
91 ///
92 /// # Examples
93 ///
94 /// ```
95 /// use error_rail::validation::Validation;
96 ///
97 /// let v = Validation::<&str, ()>::invalid_many(["missing", "invalid"]);
98 /// assert!(v.is_invalid());
99 /// assert_eq!(v.into_errors().unwrap().len(), 2);
100 /// ```
101 #[must_use]
102 #[inline]
103 pub fn invalid_many<I>(errors: I) -> Self
104 where
105 I: IntoIterator<Item = E>,
106 {
107 Self::Invalid(errors.into_iter().collect())
108 }
109
110 /// Returns `true` if the validation contains a value.
111 ///
112 /// # Examples
113 ///
114 /// ```
115 /// use error_rail::validation::Validation;
116 ///
117 /// let v = Validation::<&str, i32>::valid(42);
118 /// assert!(v.is_valid());
119 /// ```
120 #[must_use]
121 #[inline]
122 pub fn is_valid(&self) -> bool {
123 matches!(self, Self::Valid(_))
124 }
125
126 /// Returns `true` if the validation contains errors.
127 ///
128 /// # Examples
129 ///
130 /// ```
131 /// use error_rail::validation::Validation;
132 ///
133 /// let v = Validation::<&str, i32>::invalid("error");
134 /// assert!(v.is_invalid());
135 /// ```
136 #[must_use]
137 #[inline]
138 pub fn is_invalid(&self) -> bool {
139 !self.is_valid()
140 }
141
142 /// Maps the valid value using the provided function.
143 ///
144 /// If the validation is invalid, the errors are preserved unchanged.
145 ///
146 /// # Arguments
147 ///
148 /// * `f` - A function that transforms the success value from type `A` to type `B`
149 ///
150 /// # Examples
151 ///
152 /// ```
153 /// use error_rail::validation::Validation;
154 ///
155 /// let v = Validation::<&str, i32>::valid(21);
156 /// let doubled = v.map(|x| x * 2);
157 /// assert_eq!(doubled.into_value(), Some(42));
158 /// ```
159 #[must_use]
160 #[inline]
161 pub fn map<B, F>(self, f: F) -> Validation<E, B>
162 where
163 F: FnOnce(A) -> B,
164 {
165 match self {
166 Self::Valid(value) => Validation::Valid(f(value)),
167 Self::Invalid(errors) => Validation::Invalid(errors),
168 }
169 }
170
171 /// Chains computations that may produce additional validation errors.
172 ///
173 /// Behaves like [`Result::and_then`], propagating invalid states while
174 /// invoking `f` only when the current validation is valid.
175 ///
176 /// # Arguments
177 ///
178 /// * `f` - Function producing the next validation step
179 ///
180 /// # Examples
181 ///
182 /// ```
183 /// use error_rail::validation::Validation;
184 ///
185 /// fn parse_even(input: i32) -> Validation<&'static str, i32> {
186 /// if input % 2 == 0 {
187 /// Validation::valid(input)
188 /// } else {
189 /// Validation::invalid("not even")
190 /// }
191 /// }
192 ///
193 /// let result = Validation::valid(4).and_then(parse_even);
194 /// assert_eq!(result.into_value(), Some(4));
195 ///
196 /// let invalid = Validation::valid(3).and_then(parse_even);
197 /// assert!(invalid.is_invalid());
198 /// ```
199 #[must_use]
200 #[inline]
201 pub fn and_then<B, F>(self, f: F) -> Validation<E, B>
202 where
203 F: FnOnce(A) -> Validation<E, B>,
204 {
205 match self {
206 Self::Valid(value) => f(value),
207 Self::Invalid(errors) => Validation::Invalid(errors),
208 }
209 }
210
211 /// Calls `op` if the validation is invalid, otherwise returns the `Valid` value.
212 ///
213 /// This function can be used for control flow based on validation results.
214 ///
215 /// # Arguments
216 ///
217 /// * `op` - The function to call if the validation is invalid.
218 ///
219 /// # Examples
220 ///
221 /// ```
222 /// use error_rail::validation::Validation;
223 ///
224 /// let v = Validation::<&str, i32>::invalid("error");
225 /// let res = v.or_else(|_errs| Validation::valid(42));
226 /// assert_eq!(res.into_value(), Some(42));
227 /// ```
228 #[must_use]
229 #[inline]
230 pub fn or_else<F>(self, op: F) -> Validation<E, A>
231 where
232 F: FnOnce(ErrorVec<E>) -> Validation<E, A>,
233 {
234 match self {
235 Self::Valid(value) => Validation::Valid(value),
236 Self::Invalid(errors) => op(errors),
237 }
238 }
239
240 /// Maps each error while preserving the success branch.
241 ///
242 /// Transforms all accumulated errors using the provided function,
243 /// leaving valid values unchanged.
244 ///
245 /// # Arguments
246 ///
247 /// * `f` - A function that transforms errors from type `E` to type `G`
248 ///
249 /// # Examples
250 ///
251 /// ```
252 /// use error_rail::validation::Validation;
253 ///
254 /// let v = Validation::<&str, i32>::invalid("error");
255 /// let mapped = v.map_err(|e| format!("Error: {}", e));
256 /// assert!(mapped.is_invalid());
257 /// ```
258 #[must_use]
259 #[inline]
260 pub fn map_err<F, G>(self, f: F) -> Validation<G, A>
261 where
262 F: Fn(E) -> G,
263 {
264 match self {
265 Self::Valid(value) => Validation::Valid(value),
266 Self::Invalid(errors) => Validation::Invalid(errors.into_iter().map(f).collect()),
267 }
268 }
269
270 /// Converts into a `Result`, losing error accumulation if invalid.
271 ///
272 /// The success value becomes `Ok`, and all accumulated errors become `Err`.
273 ///
274 /// # Examples
275 ///
276 /// ```
277 /// use error_rail::validation::Validation;
278 ///
279 /// let v = Validation::<&str, i32>::valid(42);
280 /// assert_eq!(v.to_result(), Ok(42));
281 ///
282 /// let v = Validation::<&str, i32>::invalid("error");
283 /// assert!(v.to_result().is_err());
284 /// ```
285 #[must_use]
286 #[inline]
287 pub fn to_result(self) -> Result<A, ErrorVec<E>> {
288 match self {
289 Self::Valid(value) => Ok(value),
290 Self::Invalid(errors) => Err(errors),
291 }
292 }
293
294 /// Wraps a normal `Result` into a `Validation`, turning the error side into a singleton vec.
295 ///
296 /// # Arguments
297 ///
298 /// * `result` - The result to convert
299 ///
300 /// # Examples
301 ///
302 /// ```
303 /// use error_rail::validation::Validation;
304 ///
305 /// let result: Result<i32, &str> = Ok(42);
306 /// let v = Validation::from_result(result);
307 /// assert!(v.is_valid());
308 /// ```
309 #[must_use]
310 #[inline]
311 pub fn from_result(result: Result<A, E>) -> Self {
312 match result {
313 Ok(value) => Self::Valid(value),
314 Err(error) => Self::invalid(error),
315 }
316 }
317
318 /// Extracts the error list, if any.
319 ///
320 /// Returns `Some(errors)` if invalid, `None` if valid.
321 ///
322 /// # Examples
323 ///
324 /// ```
325 /// use error_rail::validation::Validation;
326 ///
327 /// let v = Validation::<&str, i32>::invalid("error");
328 /// assert_eq!(v.into_errors().unwrap().len(), 1);
329 /// ```
330 #[must_use]
331 #[inline]
332 pub fn into_errors(self) -> Option<ErrorVec<E>> {
333 match self {
334 Self::Valid(_) => None,
335 Self::Invalid(errors) => Some(errors),
336 }
337 }
338
339 /// Extracts the value, if valid.
340 ///
341 /// Returns `Some(value)` if valid, `None` if invalid.
342 ///
343 /// # Examples
344 ///
345 /// ```
346 /// use error_rail::validation::Validation;
347 ///
348 /// let v = Validation::<&str, i32>::valid(42);
349 /// assert_eq!(v.into_value(), Some(42));
350 /// ```
351 #[must_use]
352 #[inline]
353 pub fn into_value(self) -> Option<A> {
354 match self {
355 Self::Valid(value) => Some(value),
356 Self::Invalid(_) => None,
357 }
358 }
359}