Skip to main content

vld/collections/
map.rs

1use serde_json::Value;
2use std::collections::HashMap;
3
4use crate::error::{value_type_name, IssueCode, PathSegment, VldError};
5use crate::schema::VldSchema;
6
7/// Schema for validating a JSON array of `[key, value]` pairs into a `HashMap`.
8///
9/// Created via [`vld::map()`](crate::map).
10///
11/// # Example
12/// ```ignore
13/// let schema = vld::map(vld::string(), vld::number().int());
14/// // Input: [["a", 1], ["b", 2]]
15/// // Output: HashMap { "a" => 1, "b" => 2 }
16/// ```
17pub struct ZMap<K: VldSchema, V: VldSchema> {
18    key_schema: K,
19    value_schema: V,
20}
21
22impl<K: VldSchema, V: VldSchema> ZMap<K, V> {
23    pub fn new(key_schema: K, value_schema: V) -> Self {
24        Self {
25            key_schema,
26            value_schema,
27        }
28    }
29}
30
31impl<K, V> VldSchema for ZMap<K, V>
32where
33    K: VldSchema,
34    V: VldSchema,
35    K::Output: Eq + std::hash::Hash,
36{
37    type Output = HashMap<K::Output, V::Output>;
38
39    fn parse_value(&self, value: &Value) -> Result<Self::Output, VldError> {
40        let arr = value.as_array().ok_or_else(|| {
41            VldError::single(
42                IssueCode::InvalidType {
43                    expected: "array".to_string(),
44                    received: value_type_name(value),
45                },
46                format!(
47                    "Expected array of [key, value] pairs, received {}",
48                    value_type_name(value)
49                ),
50            )
51        })?;
52
53        let mut result = HashMap::new();
54        let mut errors = VldError::new();
55
56        for (i, item) in arr.iter().enumerate() {
57            let pair = item.as_array().filter(|a| a.len() == 2);
58            match pair {
59                Some(pair) => {
60                    let k = match self.key_schema.parse_value(&pair[0]) {
61                        Ok(k) => Some(k),
62                        Err(e) => {
63                            errors = errors.merge(e.with_prefix(PathSegment::Index(i)));
64                            None
65                        }
66                    };
67                    let v = match self.value_schema.parse_value(&pair[1]) {
68                        Ok(v) => Some(v),
69                        Err(e) => {
70                            errors = errors.merge(e.with_prefix(PathSegment::Index(i)));
71                            None
72                        }
73                    };
74                    if let (Some(k), Some(v)) = (k, v) {
75                        result.insert(k, v);
76                    }
77                }
78                None => {
79                    let mut e = VldError::single(
80                        IssueCode::Custom {
81                            code: "invalid_map_entry".to_string(),
82                        },
83                        "Each Map entry must be a [key, value] array of length 2",
84                    );
85                    e = e.with_prefix(PathSegment::Index(i));
86                    errors = errors.merge(e);
87                }
88            }
89        }
90
91        if errors.is_empty() {
92            Ok(result)
93        } else {
94            Err(errors)
95        }
96    }
97}