poem_openapi/types/external/
hashmap.rs

1use std::{
2    borrow::Cow,
3    collections::HashMap,
4    fmt::Display,
5    hash::{BuildHasher, Hash},
6    str::FromStr,
7};
8
9use serde_json::Value;
10
11use crate::{
12    registry::{MetaSchema, MetaSchemaRef, Registry},
13    types::{ParseError, ParseFromJSON, ParseResult, ToJSON, Type},
14};
15
16impl<K, V, R> Type for HashMap<K, V, R>
17where
18    K: ToString + FromStr + Eq + Hash + Sync + Send,
19    V: Type,
20    R: Sync + Send,
21{
22    const IS_REQUIRED: bool = true;
23
24    type RawValueType = Self;
25
26    type RawElementValueType = V::RawValueType;
27
28    fn name() -> Cow<'static, str> {
29        format!("map_{}", V::name()).into()
30    }
31
32    fn schema_ref() -> MetaSchemaRef {
33        MetaSchemaRef::Inline(Box::new(MetaSchema {
34            additional_properties: Some(Box::new(V::schema_ref())),
35            ..MetaSchema::new("object")
36        }))
37    }
38
39    fn register(registry: &mut Registry) {
40        V::register(registry);
41    }
42
43    fn as_raw_value(&self) -> Option<&Self::RawValueType> {
44        Some(self)
45    }
46
47    fn raw_element_iter<'a>(
48        &'a self,
49    ) -> Box<dyn Iterator<Item = &'a Self::RawElementValueType> + 'a> {
50        Box::new(self.values().filter_map(|item| item.as_raw_value()))
51    }
52
53    fn is_empty(&self) -> bool {
54        HashMap::is_empty(self)
55    }
56}
57
58impl<K, V, R> ParseFromJSON for HashMap<K, V, R>
59where
60    K: ToString + FromStr + Eq + Hash + Sync + Send,
61    K::Err: Display,
62    V: ParseFromJSON,
63    R: Sync + Send + Default + BuildHasher,
64{
65    fn parse_from_json(value: Option<Value>) -> ParseResult<Self> {
66        let value = value.unwrap_or_default();
67        if let Value::Object(value) = value {
68            let mut obj = HashMap::with_hasher(R::default());
69            for (key, value) in value {
70                let key = key
71                    .parse()
72                    .map_err(|err| ParseError::custom(format!("object key: {err}")))?;
73                let value = V::parse_from_json(Some(value)).map_err(ParseError::propagate)?;
74                obj.insert(key, value);
75            }
76            Ok(obj)
77        } else {
78            Err(ParseError::expected_type(value))
79        }
80    }
81}
82
83impl<K, V, R> ToJSON for HashMap<K, V, R>
84where
85    K: ToString + FromStr + Eq + Hash + Sync + Send,
86    V: ToJSON,
87    R: Sync + Send,
88{
89    fn to_json(&self) -> Option<Value> {
90        let mut map = serde_json::Map::new();
91        for (name, value) in self {
92            if let Some(value) = value.to_json() {
93                map.insert(name.to_string(), value);
94            }
95        }
96        Some(Value::Object(map))
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    use super::*;
103
104    #[test]
105    fn test_hashmap() {
106        type MyObj = HashMap<String, i32>;
107
108        assert_eq!(
109            MyObj::schema_ref().unwrap_inline(),
110            &MetaSchema {
111                additional_properties: Some(Box::new(i32::schema_ref())),
112                ..MetaSchema::new("object")
113            }
114        );
115    }
116}