1use serde_json::Value;
2
3use crate::error::{value_type_name, IssueCode, PathSegment, VldError};
4use crate::schema::VldSchema;
5
6pub struct ZArray<T: VldSchema> {
17 element: T,
18 min_len: Option<usize>,
19 max_len: Option<usize>,
20 exact_len: Option<usize>,
21 contains: Option<serde_json::Value>,
22 min_contains: Option<usize>,
23 max_contains: Option<usize>,
24 unique: bool,
25}
26
27impl<T: VldSchema> ZArray<T> {
28 pub fn new(element: T) -> Self {
29 Self {
30 element,
31 min_len: None,
32 max_len: None,
33 exact_len: None,
34 contains: None,
35 min_contains: None,
36 max_contains: None,
37 unique: false,
38 }
39 }
40
41 pub fn min_len(mut self, len: usize) -> Self {
43 self.min_len = Some(len);
44 self
45 }
46
47 pub fn max_len(mut self, len: usize) -> Self {
49 self.max_len = Some(len);
50 self
51 }
52
53 pub fn len(mut self, len: usize) -> Self {
55 self.exact_len = Some(len);
56 self
57 }
58
59 pub fn non_empty(self) -> Self {
61 self.min_len(1)
62 }
63
64 pub fn contains(mut self, value: impl Into<serde_json::Value>) -> Self {
66 self.contains = Some(value.into());
67 self
68 }
69
70 pub fn min_contains(mut self, n: usize) -> Self {
72 self.min_contains = Some(n);
73 self
74 }
75
76 pub fn max_contains(mut self, n: usize) -> Self {
78 self.max_contains = Some(n);
79 self
80 }
81
82 pub fn unique(mut self) -> Self {
84 self.unique = true;
85 self
86 }
87
88 #[allow(dead_code)]
89 pub(crate) fn element_schema(&self) -> &T {
90 &self.element
91 }
92
93 #[cfg(feature = "openapi")]
97 pub fn to_json_schema_inner(&self) -> serde_json::Value
98 where
99 T: crate::json_schema::JsonSchema,
100 {
101 let mut schema = serde_json::json!({
102 "type": "array",
103 "items": self.element.json_schema(),
104 });
105 if let Some(min) = self.min_len {
106 schema["minItems"] = serde_json::json!(min);
107 }
108 if let Some(max) = self.max_len {
109 schema["maxItems"] = serde_json::json!(max);
110 }
111 if let Some(exact) = self.exact_len {
112 schema["minItems"] = serde_json::json!(exact);
113 schema["maxItems"] = serde_json::json!(exact);
114 }
115 if let Some(ref contains) = self.contains {
116 schema["contains"] = contains.clone();
117 }
118 if let Some(min_contains) = self.min_contains {
119 schema["minContains"] = serde_json::json!(min_contains);
120 }
121 if let Some(max_contains) = self.max_contains {
122 schema["maxContains"] = serde_json::json!(max_contains);
123 }
124 if self.unique {
125 schema["uniqueItems"] = serde_json::json!(true);
126 }
127 schema
128 }
129}
130
131impl<T: VldSchema> VldSchema for ZArray<T> {
132 type Output = Vec<T::Output>;
133
134 fn parse_value(&self, value: &Value) -> Result<Vec<T::Output>, VldError> {
135 let arr = value.as_array().ok_or_else(|| {
136 VldError::single(
137 IssueCode::InvalidType {
138 expected: "array".to_string(),
139 received: value_type_name(value),
140 },
141 format!("Expected array, received {}", value_type_name(value)),
142 )
143 })?;
144
145 let mut errors = VldError::new();
146
147 if let Some(min) = self.min_len {
149 if arr.len() < min {
150 errors.push(
151 IssueCode::TooSmall {
152 minimum: min as f64,
153 inclusive: true,
154 },
155 format!("Array must have at least {} elements", min),
156 );
157 }
158 }
159
160 if let Some(max) = self.max_len {
161 if arr.len() > max {
162 errors.push(
163 IssueCode::TooBig {
164 maximum: max as f64,
165 inclusive: true,
166 },
167 format!("Array must have at most {} elements", max),
168 );
169 }
170 }
171
172 if let Some(exact) = self.exact_len {
173 if arr.len() != exact {
174 errors.push(
175 IssueCode::Custom {
176 code: "invalid_length".to_string(),
177 },
178 format!("Array must have exactly {} elements", exact),
179 );
180 }
181 }
182
183 if self.unique {
184 for i in 0..arr.len() {
185 for j in (i + 1)..arr.len() {
186 if arr[i] == arr[j] {
187 errors.push(
188 IssueCode::Custom {
189 code: "not_unique".to_string(),
190 },
191 "Array items must be unique",
192 );
193 break;
194 }
195 }
196 }
197 }
198
199 if let Some(ref contains) = self.contains {
200 let count = arr.iter().filter(|v| *v == contains).count();
201 if count == 0 {
202 errors.push(
203 IssueCode::Custom {
204 code: "missing_contains".to_string(),
205 },
206 "Array must contain required value",
207 );
208 }
209 if let Some(min_contains) = self.min_contains {
210 if count < min_contains {
211 errors.push(
212 IssueCode::TooSmall {
213 minimum: min_contains as f64,
214 inclusive: true,
215 },
216 format!(
217 "Array must contain required value at least {} time(s)",
218 min_contains
219 ),
220 );
221 }
222 }
223 if let Some(max_contains) = self.max_contains {
224 if count > max_contains {
225 errors.push(
226 IssueCode::TooBig {
227 maximum: max_contains as f64,
228 inclusive: true,
229 },
230 format!(
231 "Array must contain required value at most {} time(s)",
232 max_contains
233 ),
234 );
235 }
236 }
237 }
238
239 let mut results = Vec::with_capacity(arr.len());
241
242 for (i, item) in arr.iter().enumerate() {
243 match self.element.parse_value(item) {
244 Ok(v) => results.push(v),
245 Err(e) => {
246 errors = errors.merge(e.with_prefix(PathSegment::Index(i)));
247 }
248 }
249 }
250
251 if errors.is_empty() {
252 Ok(results)
253 } else {
254 Err(errors)
255 }
256 }
257}