error_accumulator/builder/
array.rs1use std::{error::Error, marker::PhantomData};
2
3use crate::{
4 builder::{ErrorBuilderParent, StructBuilder},
5 cons::Nil,
6 error::AccumulatedError,
7 path::{FieldName, PathSegment, SourcePath},
8};
9
10#[derive(Debug)]
15pub struct ArrayBuilder<Parent, Value> {
16 parent: Parent,
17 base: SourcePath,
18 errors: AccumulatedError,
19 array_name: FieldName,
20 values: Vec<Value>,
21 _marker: PhantomData<Value>,
22}
23
24impl<Parent, Value> ArrayBuilder<Parent, Value>
25where
26 Parent: ErrorBuilderParent<Vec<Value>>,
27{
28 pub(crate) fn new(parent: Parent, base: SourcePath, field: FieldName) -> Self {
29 Self {
30 base,
31 parent,
32 errors: Default::default(),
33 array_name: field,
34 values: Default::default(),
35 _marker: PhantomData,
36 }
37 }
38
39 pub fn of_values<E>(self, values: impl IntoIterator<Item = Result<Value, E>>) -> Self
41 where
42 E: Error + Send + Sync + 'static,
43 {
44 values
45 .into_iter()
46 .enumerate()
47 .fold(self, |rec, (index, result)| rec.value(index, result))
48 }
49
50 pub fn of_structs<I, T, Parser>(self, elements: I, mut parse: Parser) -> Self
58 where
59 I: IntoIterator<Item = T>,
60 Parser: FnMut(StructBuilder<Self, Value, Nil>, T) -> Self,
61 {
62 elements
63 .into_iter()
64 .enumerate()
65 .fold(self, |rec, (index, element)| {
66 parse(rec.strukt(index), element)
67 })
68 }
69
70 pub fn value<E>(mut self, index: usize, result: Result<Value, E>) -> Self
76 where
77 E: Error + Send + Sync + 'static,
78 {
79 match result {
80 Ok(value) => self.values.push(value),
81 Err(error) => {
82 self.errors.append(self.element_path(index), error);
83 }
84 }
85
86 self
87 }
88
89 pub fn strukt(self, index: usize) -> StructBuilder<Self, Value, Nil> {
92 let path = self.element_path(index);
93 StructBuilder::new(self, path)
94 }
95
96 pub fn finish(self) -> Parent::AfterRecord {
99 let result = if self.errors.is_empty() {
100 Ok(self.values)
101 } else {
102 Err(self.errors)
103 };
104
105 self.parent.finish_child_builder(result)
106 }
107
108 fn element_path(&self, index: usize) -> SourcePath {
109 self.base.join(PathSegment::Array {
110 name: self.array_name.clone(),
111 index,
112 })
113 }
114}
115
116impl<Parent, Value> ErrorBuilderParent<Value> for ArrayBuilder<Parent, Value> {
117 type AfterRecord = Self;
118
119 fn finish_child_builder(
120 mut self,
121 child_result: Result<Value, AccumulatedError>,
122 ) -> Self::AfterRecord {
123 match child_result {
124 Ok(value) => self.values.push(value),
125 Err(errors) => {
126 self.errors.merge(errors);
127 }
128 }
129
130 self
131 }
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137 use crate::{ErrorAccumulator, test_util::n};
138
139 #[derive(Debug, PartialEq, Eq)]
140 struct Test(u32);
141
142 #[test]
143 fn should_record_array_of_structs() {
144 let (res,) = ErrorAccumulator::new()
145 .array(n("foo"))
146 .of_structs(vec!["42", "21", "33"], |rec, value| {
147 rec.field(n("num"), value.parse()).on_ok(Test).finish()
148 })
149 .finish()
150 .analyse()
151 .unwrap();
152
153 assert_eq!(vec![Test(42), Test(21), Test(33)], res);
154 }
155
156 #[test]
157 fn should_record_array_of_values() {
158 let (res,) = ErrorAccumulator::new()
159 .array(n("foo"))
160 .of_values(vec!["42".parse(), "21".parse(), "33".parse()])
161 .finish()
162 .analyse()
163 .unwrap();
164
165 assert_eq!(vec![42, 21, 33], res);
166 }
167
168 #[test]
169 fn should_record_error_in_array() {
170 let res = ErrorAccumulator::new()
171 .array(n("foo"))
172 .of_values(vec!["42".parse::<u32>(), "aa".parse(), "bb".parse()])
173 .finish()
174 .analyse()
175 .unwrap_err();
176
177 assert_eq!(
178 res.get_by_path(&SourcePath::new().join(PathSegment::Array {
179 name: n("foo"),
180 index: 1
181 }))
182 .count(),
183 1
184 );
185 assert_eq!(
186 res.get_by_path(&SourcePath::new().join(PathSegment::Array {
187 name: n("foo"),
188 index: 2
189 }))
190 .count(),
191 1
192 );
193 }
194}