Skip to main content

dpp/validation/validation_result/
mod.rs

1use crate::errors::consensus::ConsensusError;
2use crate::version::PlatformVersion;
3use crate::ProtocolError;
4use std::fmt::Debug;
5
6mod flatten;
7mod merge_many;
8
9#[macro_export]
10macro_rules! check_validation_result_with_data {
11    ($result:expr) => {
12        match $result {
13            Ok(result) => result,
14            Err(e) => return Ok(ValidationResult::new_with_errors(vec![e.into()])),
15        }
16    };
17}
18
19pub type SimpleValidationResult<E> = ValidationResult<(), E>;
20
21pub type ConsensusValidationResult<TData> = ValidationResult<TData, ConsensusError>;
22
23pub type SimpleConsensusValidationResult = ConsensusValidationResult<()>;
24
25#[derive(Debug, Clone)]
26pub struct ValidationResult<TData: Clone, E: Debug> {
27    pub errors: Vec<E>,
28    pub data: Option<TData>,
29}
30
31impl<T: Clone, E: Debug> Default for ValidationResult<T, E> {
32    fn default() -> Self {
33        ValidationResult {
34            errors: Vec::new(),
35            data: None,
36        }
37    }
38}
39
40impl<TData: Clone, E: Debug> ValidationResult<Vec<TData>, E> {
41    /// Aggregate a list of `ValidationResult<Vec<TData>, E>` into a single
42    /// result. Dispatches to the version selected by `platform_version`:
43    ///
44    /// - **v0** (`PROTOCOL_VERSION_11` and below): always returns
45    ///   `data: Some(Vec<...>)`, including `Some(empty_vec)` when no input
46    ///   contributed any data. Preserved for chain reproducibility.
47    /// - **v1** (`PROTOCOL_VERSION_12`+): returns `data: None` when no input
48    ///   contributed any data. Honors the invariant
49    ///   `data.is_none() ⇔ no work done`, which downstream code (e.g.
50    ///   `process_validation_result_v0:241`) relies on to choose between
51    ///   `PaidConsensusError` and `UnpaidConsensusError`.
52    ///
53    /// # v1 caller-intent ambiguity
54    ///
55    /// v1 keys on `aggregate_data.is_empty()` to decide between
56    /// `data: None` and `data: Some(_)`, which collapses two distinct
57    /// caller intents into the same output: every input had `data: None`
58    /// (truly no work) and every input had `data: Some(empty_vec)`
59    /// (validated but produced no output). v1 cannot distinguish those
60    /// at the aggregate level — both yield `data: None` and are routed
61    /// to `UnpaidConsensusError` downstream. Callers that need "validated
62    /// but no actions" must signal that with at least one non-empty entry.
63    ///
64    /// See issue #2867 for context on the v0 → v1 change.
65    pub fn flatten<I: IntoIterator<Item = ValidationResult<Vec<TData>, E>>>(
66        items: I,
67        platform_version: &PlatformVersion,
68    ) -> Result<ValidationResult<Vec<TData>, E>, ProtocolError> {
69        match platform_version.dpp.validation.validation_result.flatten {
70            0 => Ok(flatten::v0::flatten_v0(items)),
71            1 => Ok(flatten::v1::flatten_v1(items)),
72            version => Err(ProtocolError::UnknownVersionMismatch {
73                method: "ValidationResult::flatten".to_string(),
74                known_versions: vec![0, 1],
75                received: version,
76            }),
77        }
78    }
79}
80
81impl<TData: Clone, E: Debug> ValidationResult<TData, E> {
82    /// Aggregate a list of `ValidationResult<TData, E>` into a
83    /// `ValidationResult<Vec<TData>, E>`. Dispatches to the version selected
84    /// by `platform_version`:
85    ///
86    /// - **v0** (`PROTOCOL_VERSION_11` and below): always returns
87    ///   `data: Some(Vec<...>)`, including `Some(empty_vec)` when no input
88    ///   contributed any data. Preserved for chain reproducibility.
89    /// - **v1** (`PROTOCOL_VERSION_12`+): returns `data: None` when no input
90    ///   contributed any data. See [`flatten`] for the invariant this
91    ///   restores.
92    ///
93    /// Unlike [`flatten`], `merge_many` operates on per-item `TData` (not
94    /// `Vec<TData>`), so each `Some(_)` input contributes exactly one
95    /// element — there is no `Some(empty_vec)`-input collapse hazard at
96    /// this layer.
97    ///
98    /// See issue #2867 for context on the v0 → v1 change.
99    ///
100    /// [`flatten`]: ValidationResult::flatten
101    pub fn merge_many<I: IntoIterator<Item = ValidationResult<TData, E>>>(
102        items: I,
103        platform_version: &PlatformVersion,
104    ) -> Result<ValidationResult<Vec<TData>, E>, ProtocolError> {
105        match platform_version.dpp.validation.validation_result.merge_many {
106            0 => Ok(merge_many::v0::merge_many_v0(items)),
107            1 => Ok(merge_many::v1::merge_many_v1(items)),
108            version => Err(ProtocolError::UnknownVersionMismatch {
109                method: "ValidationResult::merge_many".to_string(),
110                known_versions: vec![0, 1],
111                received: version,
112            }),
113        }
114    }
115}
116
117impl<E: Debug> SimpleValidationResult<E> {
118    pub fn merge_many_errors<I: IntoIterator<Item = SimpleValidationResult<E>>>(
119        items: I,
120    ) -> SimpleValidationResult<E> {
121        let errors = items
122            .into_iter()
123            .flat_map(|single_validation_result| single_validation_result.errors)
124            .collect();
125        SimpleValidationResult::new_with_errors(errors)
126    }
127}
128
129impl<TData: Clone, E: Debug> ValidationResult<TData, E> {
130    pub fn new() -> Self {
131        Self {
132            errors: vec![],
133            data: None::<TData>,
134        }
135    }
136
137    pub fn new_with_data(data: TData) -> Self {
138        Self {
139            errors: vec![],
140            data: Some(data),
141        }
142    }
143
144    pub fn new_with_data_and_errors(data: TData, errors: Vec<E>) -> Self {
145        Self {
146            errors,
147            data: Some(data),
148        }
149    }
150
151    pub fn new_with_error(error: E) -> Self {
152        Self {
153            errors: vec![error],
154            data: None,
155        }
156    }
157
158    pub fn new_with_errors(errors: Vec<E>) -> Self {
159        Self { errors, data: None }
160    }
161
162    pub fn map<F, U: Clone>(self, f: F) -> ValidationResult<U, E>
163    where
164        F: FnOnce(TData) -> U,
165    {
166        ValidationResult {
167            errors: self.errors,
168            data: self.data.map(f),
169        }
170    }
171
172    pub fn map_result<F, U: Clone, G>(self, f: F) -> Result<ValidationResult<U, E>, G>
173    where
174        F: FnOnce(TData) -> Result<U, G>,
175    {
176        Ok(ValidationResult {
177            errors: self.errors,
178            data: self.data.map(f).transpose()?,
179        })
180    }
181
182    pub fn and_then_simple_validation<F>(
183        self,
184        f: F,
185    ) -> Result<ValidationResult<TData, E>, ProtocolError>
186    where
187        F: FnOnce(&TData) -> Result<SimpleValidationResult<E>, ProtocolError>,
188    {
189        let new_errors = self.data.as_ref().map(f).transpose()?;
190        let mut result = ValidationResult {
191            errors: self.errors,
192            data: self.data,
193        };
194        if let Some(new_errors) = new_errors {
195            result.add_errors(new_errors.errors)
196        }
197        Ok(result)
198    }
199
200    pub fn and_then_validation<F, U: Clone, G>(self, f: F) -> Result<ValidationResult<U, E>, G>
201    where
202        F: FnOnce(TData) -> Result<ValidationResult<U, E>, G>,
203    {
204        if let Some(data) = self.data {
205            let mut new_validation_result = f(data)?;
206            new_validation_result.add_errors(self.errors);
207            Ok(new_validation_result)
208        } else {
209            Ok(ValidationResult::<U, E>::new_with_errors(self.errors))
210        }
211    }
212
213    pub fn and_then_borrowed_validation<F, U: Clone, G>(
214        self,
215        f: F,
216    ) -> Result<ValidationResult<U, E>, G>
217    where
218        F: FnOnce(&TData) -> Result<ValidationResult<U, E>, G>,
219    {
220        if let Some(data) = self.data.as_ref() {
221            let mut new_validation_result = f(data)?;
222            new_validation_result.add_errors(self.errors);
223            Ok(new_validation_result)
224        } else {
225            Ok(ValidationResult::<U, E>::new_with_errors(self.errors))
226        }
227    }
228
229    pub fn add_error<T>(&mut self, error: T)
230    where
231        T: Into<E>,
232    {
233        self.errors.push(error.into())
234    }
235
236    pub fn add_errors(&mut self, mut errors: Vec<E>) {
237        self.errors.append(&mut errors)
238    }
239
240    pub fn add_errors_into<EI: Into<E>>(&mut self, errors: Vec<EI>) {
241        errors.into_iter().for_each(|e| self.add_error(e.into()))
242    }
243
244    pub fn merge<TOtherData: Clone>(&mut self, mut other: ValidationResult<TOtherData, E>) {
245        self.errors.append(&mut other.errors);
246    }
247
248    pub fn is_valid(&self) -> bool {
249        self.errors.is_empty()
250    }
251
252    pub fn is_err(&self) -> bool {
253        !self.errors.is_empty()
254    }
255
256    pub fn first_error(&self) -> Option<&E> {
257        self.errors.first()
258    }
259
260    pub fn get_error(&self, pos: usize) -> Option<&E> {
261        self.errors.get(pos)
262    }
263
264    pub fn into_result_without_data(self) -> ValidationResult<(), E> {
265        ValidationResult {
266            errors: self.errors,
267            data: None,
268        }
269    }
270
271    pub fn is_valid_with_data(&self) -> bool {
272        self.is_valid() && self.data.is_some()
273    }
274
275    pub fn has_data(&self) -> bool {
276        self.data.is_some()
277    }
278
279    pub fn set_data(&mut self, data: TData) {
280        self.data = Some(data)
281    }
282
283    pub fn into_data(self) -> Result<TData, ProtocolError> {
284        self.data
285            .ok_or(ProtocolError::CorruptedCodeExecution(format!(
286                "trying to push validation result into data (errors are {:?})",
287                self.errors
288            )))
289    }
290
291    pub fn into_data_with_error(mut self) -> Result<Result<TData, E>, ProtocolError> {
292        if let Some(error) = self.errors.pop() {
293            Ok(Err(error))
294        } else {
295            self.data
296                .map(Ok)
297                .ok_or(ProtocolError::CorruptedCodeExecution(format!(
298                    "trying to push validation result into data (errors are {:?})",
299                    self.errors
300                )))
301        }
302    }
303
304    pub fn into_data_and_errors(self) -> Result<(TData, Vec<E>), ProtocolError> {
305        Ok((
306            self.data
307                .ok_or(ProtocolError::CorruptedCodeExecution(format!(
308                    "trying to push validation result into data (errors are {:?})",
309                    self.errors
310                )))?,
311            self.errors,
312        ))
313    }
314
315    pub fn data_as_borrowed(&self) -> Result<&TData, ProtocolError> {
316        self.data
317            .as_ref()
318            .ok_or(ProtocolError::CorruptedCodeExecution(format!(
319                "trying to get validation result as data (errors are {:?})",
320                self.errors
321            )))
322    }
323}
324
325impl<TData: Clone, E: Debug> From<TData> for ValidationResult<TData, E> {
326    fn from(value: TData) -> Self {
327        ValidationResult::new_with_data(value)
328    }
329}
330
331impl<TData: Clone, E: Debug, F: Into<E>> From<Result<TData, F>> for ValidationResult<TData, E> {
332    fn from(value: Result<TData, F>) -> Self {
333        match value {
334            Ok(data) => ValidationResult::new_with_data(data),
335            Err(e) => ValidationResult::new_with_errors(vec![e.into()]),
336        }
337    }
338}
339
340#[cfg(test)]
341mod tests {
342    use super::*;
343
344    // -- new() --
345
346    #[test]
347    fn test_new_has_no_errors() {
348        let result: ValidationResult<i32, String> = ValidationResult::new();
349        assert!(result.errors.is_empty());
350    }
351
352    #[test]
353    fn test_new_has_no_data() {
354        let result: ValidationResult<i32, String> = ValidationResult::new();
355        assert!(result.data.is_none());
356    }
357
358    // -- new_with_data() --
359
360    #[test]
361    fn test_new_with_data_stores_data() {
362        let result: ValidationResult<i32, String> = ValidationResult::new_with_data(42);
363        assert_eq!(result.data, Some(42));
364        assert!(result.errors.is_empty());
365    }
366
367    // -- new_with_error() --
368
369    #[test]
370    fn test_new_with_error_stores_single_error() {
371        let result: ValidationResult<i32, String> =
372            ValidationResult::new_with_error("bad".to_string());
373        assert_eq!(result.errors.len(), 1);
374        assert_eq!(result.errors[0], "bad");
375        assert!(result.data.is_none());
376    }
377
378    // -- new_with_errors() --
379
380    #[test]
381    fn test_new_with_errors_stores_multiple_errors() {
382        let result: ValidationResult<i32, String> =
383            ValidationResult::new_with_errors(vec!["a".to_string(), "b".to_string()]);
384        assert_eq!(result.errors.len(), 2);
385        assert_eq!(result.errors[0], "a");
386        assert_eq!(result.errors[1], "b");
387        assert!(result.data.is_none());
388    }
389
390    #[test]
391    fn test_new_with_errors_empty_vec() {
392        let result: ValidationResult<i32, String> = ValidationResult::new_with_errors(vec![]);
393        assert!(result.errors.is_empty());
394        assert!(result.data.is_none());
395    }
396
397    // -- map() --
398
399    #[test]
400    fn test_map_transforms_data() {
401        let result: ValidationResult<i32, String> = ValidationResult::new_with_data(10);
402        let mapped = result.map(|x| x * 2);
403        assert_eq!(mapped.data, Some(20));
404        assert!(mapped.errors.is_empty());
405    }
406
407    #[test]
408    fn test_map_preserves_errors() {
409        let result: ValidationResult<i32, String> =
410            ValidationResult::new_with_data_and_errors(5, vec!["err".to_string()]);
411        let mapped = result.map(|x| x + 1);
412        assert_eq!(mapped.data, Some(6));
413        assert_eq!(mapped.errors, vec!["err".to_string()]);
414    }
415
416    #[test]
417    fn test_map_with_no_data() {
418        let result: ValidationResult<i32, String> =
419            ValidationResult::new_with_error("err".to_string());
420        let mapped = result.map(|x| x + 1);
421        assert!(mapped.data.is_none());
422        assert_eq!(mapped.errors.len(), 1);
423    }
424
425    // -- map_result() --
426
427    #[test]
428    fn test_map_result_with_ok_closure() {
429        let result: ValidationResult<i32, String> = ValidationResult::new_with_data(10);
430        let mapped: Result<ValidationResult<String, String>, String> =
431            result.map_result(|x| Ok(format!("val={}", x)));
432        let mapped = mapped.unwrap();
433        assert_eq!(mapped.data, Some("val=10".to_string()));
434    }
435
436    #[test]
437    fn test_map_result_with_err_closure() {
438        let result: ValidationResult<i32, String> = ValidationResult::new_with_data(10);
439        let mapped: Result<ValidationResult<i32, String>, String> =
440            result.map_result(|_| Err("fail".to_string()));
441        assert!(mapped.is_err());
442        assert_eq!(mapped.unwrap_err(), "fail");
443    }
444
445    #[test]
446    fn test_map_result_with_no_data() {
447        let result: ValidationResult<i32, String> =
448            ValidationResult::new_with_error("err".to_string());
449        let mapped: Result<ValidationResult<i32, String>, String> =
450            result.map_result(|x| Ok(x + 1));
451        let mapped = mapped.unwrap();
452        assert!(mapped.data.is_none());
453        assert_eq!(mapped.errors, vec!["err".to_string()]);
454    }
455
456    // -- is_valid() / is_err() --
457
458    #[test]
459    fn test_is_valid_true_when_no_errors() {
460        let result: ValidationResult<i32, String> = ValidationResult::new();
461        assert!(result.is_valid());
462        assert!(!result.is_err());
463    }
464
465    #[test]
466    fn test_is_valid_false_when_errors_present() {
467        let result: ValidationResult<i32, String> =
468            ValidationResult::new_with_error("e".to_string());
469        assert!(!result.is_valid());
470        assert!(result.is_err());
471    }
472
473    #[test]
474    fn test_is_valid_with_data_and_no_errors() {
475        let result: ValidationResult<i32, String> = ValidationResult::new_with_data(1);
476        assert!(result.is_valid());
477    }
478
479    #[test]
480    fn test_is_err_with_data_and_errors() {
481        let result: ValidationResult<i32, String> =
482            ValidationResult::new_with_data_and_errors(1, vec!["e".to_string()]);
483        assert!(result.is_err());
484    }
485
486    // -- first_error() --
487
488    #[test]
489    fn test_first_error_returns_first() {
490        let result: ValidationResult<i32, String> =
491            ValidationResult::new_with_errors(vec!["first".to_string(), "second".to_string()]);
492        assert_eq!(result.first_error(), Some(&"first".to_string()));
493    }
494
495    #[test]
496    fn test_first_error_returns_none_when_no_errors() {
497        let result: ValidationResult<i32, String> = ValidationResult::new();
498        assert_eq!(result.first_error(), None);
499    }
500
501    // -- into_data() --
502
503    #[test]
504    fn test_into_data_returns_data_when_present() {
505        let result: ValidationResult<i32, String> = ValidationResult::new_with_data(42);
506        assert_eq!(result.into_data().unwrap(), 42);
507    }
508
509    #[test]
510    fn test_into_data_returns_error_when_no_data() {
511        let result: ValidationResult<i32, String> = ValidationResult::new();
512        assert!(result.into_data().is_err());
513    }
514
515    // -- into_data_with_error() --
516
517    #[test]
518    fn test_into_data_with_error_returns_data_when_valid() {
519        let result: ValidationResult<i32, String> = ValidationResult::new_with_data(42);
520        let inner = result.into_data_with_error().unwrap();
521        assert_eq!(inner.unwrap(), 42);
522    }
523
524    #[test]
525    fn test_into_data_with_error_returns_last_error_when_errors_present() {
526        let result: ValidationResult<i32, String> =
527            ValidationResult::new_with_errors(vec!["first".to_string(), "last".to_string()]);
528        let inner = result.into_data_with_error().unwrap();
529        assert_eq!(inner.unwrap_err(), "last");
530    }
531
532    #[test]
533    fn test_into_data_with_error_returns_protocol_error_when_no_data_and_no_errors() {
534        let result: ValidationResult<i32, String> = ValidationResult::new();
535        assert!(result.into_data_with_error().is_err());
536    }
537
538    // -- into_data_and_errors() --
539
540    #[test]
541    fn test_into_data_and_errors_returns_both() {
542        let result: ValidationResult<i32, String> =
543            ValidationResult::new_with_data_and_errors(10, vec!["e".to_string()]);
544        let (data, errors) = result.into_data_and_errors().unwrap();
545        assert_eq!(data, 10);
546        assert_eq!(errors, vec!["e".to_string()]);
547    }
548
549    #[test]
550    fn test_into_data_and_errors_returns_empty_errors_when_valid() {
551        let result: ValidationResult<i32, String> = ValidationResult::new_with_data(10);
552        let (data, errors) = result.into_data_and_errors().unwrap();
553        assert_eq!(data, 10);
554        assert!(errors.is_empty());
555    }
556
557    #[test]
558    fn test_into_data_and_errors_fails_without_data() {
559        let result: ValidationResult<i32, String> =
560            ValidationResult::new_with_error("e".to_string());
561        assert!(result.into_data_and_errors().is_err());
562    }
563
564    // -- From impls --
565
566    #[test]
567    fn test_from_data_creates_valid_result() {
568        let result: ValidationResult<i32, String> = 42.into();
569        assert_eq!(result.data, Some(42));
570        assert!(result.errors.is_empty());
571    }
572
573    #[test]
574    fn test_from_ok_result_creates_valid_result() {
575        let ok_result: Result<i32, String> = Ok(42);
576        let result: ValidationResult<i32, String> = ok_result.into();
577        assert_eq!(result.data, Some(42));
578        assert!(result.errors.is_empty());
579    }
580
581    #[test]
582    fn test_from_err_result_creates_error_result() {
583        let err_result: Result<i32, String> = Err("bad".to_string());
584        let result: ValidationResult<i32, String> = err_result.into();
585        assert!(result.data.is_none());
586        assert_eq!(result.errors, vec!["bad".to_string()]);
587    }
588
589    // -- facade dispatch (flatten / merge_many take platform_version) --
590    //
591    // These verify the version field on PlatformVersion correctly steers the
592    // facade to v0 vs v1 semantics. Per-version behavior is tested in each
593    // version's own module (e.g. `flatten::v1::tests`).
594
595    #[test]
596    fn test_facade_flatten_v0_returns_some_empty_on_no_data() {
597        // PROTOCOL_VERSION_11 maps to dpp.validation.validation_result.flatten = 0
598        let pv = PlatformVersion::get(11).expect("v11 exists");
599        let r1: ValidationResult<Vec<i32>, ConsensusError> =
600            ValidationResult::new_with_errors(vec![]);
601        let flat = ValidationResult::flatten(vec![r1], pv).expect("dispatch ok");
602        assert_eq!(flat.data, Some(vec![]));
603    }
604
605    #[test]
606    fn test_facade_flatten_v1_returns_none_on_no_data() {
607        // PROTOCOL_VERSION_12 maps to dpp.validation.validation_result.flatten = 1
608        let pv = PlatformVersion::get(12).expect("v12 exists");
609        let r1: ValidationResult<Vec<i32>, ConsensusError> =
610            ValidationResult::new_with_errors(vec![]);
611        let flat = ValidationResult::flatten(vec![r1], pv).expect("dispatch ok");
612        assert!(flat.data.is_none());
613    }
614
615    #[test]
616    fn test_facade_merge_many_v0_returns_some_empty_on_no_data() {
617        let pv = PlatformVersion::get(11).expect("v11 exists");
618        let r1: ValidationResult<i32, ConsensusError> = ValidationResult::new_with_errors(vec![]);
619        let merged = ValidationResult::merge_many(vec![r1], pv).expect("dispatch ok");
620        assert_eq!(merged.data, Some(vec![]));
621    }
622
623    #[test]
624    fn test_facade_merge_many_v1_returns_none_on_no_data() {
625        let pv = PlatformVersion::get(12).expect("v12 exists");
626        let r1: ValidationResult<i32, ConsensusError> = ValidationResult::new_with_errors(vec![]);
627        let merged = ValidationResult::merge_many(vec![r1], pv).expect("dispatch ok");
628        assert!(merged.data.is_none());
629    }
630
631    // -- merge_many_errors() --
632
633    #[test]
634    fn test_merge_many_errors_collects_all_errors() {
635        let r1: SimpleValidationResult<String> =
636            SimpleValidationResult::new_with_errors(vec!["a".to_string()]);
637        let r2: SimpleValidationResult<String> =
638            SimpleValidationResult::new_with_errors(vec!["b".to_string(), "c".to_string()]);
639        let r3: SimpleValidationResult<String> = SimpleValidationResult::new();
640
641        let merged = SimpleValidationResult::merge_many_errors(vec![r1, r2, r3]);
642        assert_eq!(
643            merged.errors,
644            vec!["a".to_string(), "b".to_string(), "c".to_string()]
645        );
646    }
647
648    #[test]
649    fn test_merge_many_errors_empty_input() {
650        let merged: SimpleValidationResult<String> =
651            SimpleValidationResult::merge_many_errors(std::iter::empty());
652        assert!(merged.errors.is_empty());
653    }
654
655    // -- Default --
656
657    #[test]
658    fn test_default_is_empty() {
659        let result: ValidationResult<i32, String> = ValidationResult::default();
660        assert!(result.errors.is_empty());
661        assert!(result.data.is_none());
662    }
663
664    // -- add_error / add_errors / merge --
665
666    #[test]
667    fn test_add_error() {
668        let mut result: ValidationResult<i32, String> = ValidationResult::new();
669        result.add_error("e1".to_string());
670        result.add_error("e2".to_string());
671        assert_eq!(result.errors, vec!["e1".to_string(), "e2".to_string()]);
672    }
673
674    #[test]
675    fn test_add_errors() {
676        let mut result: ValidationResult<i32, String> =
677            ValidationResult::new_with_error("e1".to_string());
678        result.add_errors(vec!["e2".to_string(), "e3".to_string()]);
679        assert_eq!(result.errors.len(), 3);
680    }
681
682    #[test]
683    fn test_merge_appends_errors_from_other() {
684        let mut r1: ValidationResult<i32, String> =
685            ValidationResult::new_with_error("a".to_string());
686        let r2: ValidationResult<String, String> =
687            ValidationResult::new_with_error("b".to_string());
688        r1.merge(r2);
689        assert_eq!(r1.errors, vec!["a".to_string(), "b".to_string()]);
690    }
691
692    // -- get_error / has_data / is_valid_with_data / set_data --
693
694    #[test]
695    fn test_get_error() {
696        let result: ValidationResult<i32, String> =
697            ValidationResult::new_with_errors(vec!["a".to_string(), "b".to_string()]);
698        assert_eq!(result.get_error(0), Some(&"a".to_string()));
699        assert_eq!(result.get_error(1), Some(&"b".to_string()));
700        assert_eq!(result.get_error(2), None);
701    }
702
703    #[test]
704    fn test_has_data() {
705        let with: ValidationResult<i32, String> = ValidationResult::new_with_data(1);
706        let without: ValidationResult<i32, String> = ValidationResult::new();
707        assert!(with.has_data());
708        assert!(!without.has_data());
709    }
710
711    #[test]
712    fn test_is_valid_with_data() {
713        let valid_with_data: ValidationResult<i32, String> = ValidationResult::new_with_data(1);
714        let valid_no_data: ValidationResult<i32, String> = ValidationResult::new();
715        let invalid_with_data: ValidationResult<i32, String> =
716            ValidationResult::new_with_data_and_errors(1, vec!["e".to_string()]);
717        assert!(valid_with_data.is_valid_with_data());
718        assert!(!valid_no_data.is_valid_with_data());
719        assert!(!invalid_with_data.is_valid_with_data());
720    }
721
722    #[test]
723    fn test_set_data() {
724        let mut result: ValidationResult<i32, String> = ValidationResult::new();
725        assert!(result.data.is_none());
726        result.set_data(99);
727        assert_eq!(result.data, Some(99));
728    }
729
730    #[test]
731    fn test_into_result_without_data() {
732        let result: ValidationResult<i32, String> =
733            ValidationResult::new_with_data_and_errors(42, vec!["e".to_string()]);
734        let without_data = result.into_result_without_data();
735        assert!(without_data.data.is_none());
736        assert_eq!(without_data.errors, vec!["e".to_string()]);
737    }
738
739    #[test]
740    fn test_data_as_borrowed() {
741        let result: ValidationResult<i32, String> = ValidationResult::new_with_data(42);
742        assert_eq!(result.data_as_borrowed().unwrap(), &42);
743    }
744
745    #[test]
746    fn test_data_as_borrowed_no_data() {
747        let result: ValidationResult<i32, String> = ValidationResult::new();
748        assert!(result.data_as_borrowed().is_err());
749    }
750}