poem_openapi/types/external/
hashmap.rs1use 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}