async_graphql/types/external/json_object/
hashmap.rs

1use std::{
2    borrow::Cow,
3    collections::HashMap,
4    fmt::Display,
5    hash::{BuildHasher, Hash},
6    str::FromStr,
7};
8
9use async_graphql_parser::{Positioned, types::Field};
10use async_graphql_value::{from_value, to_value};
11use indexmap::IndexMap;
12use serde::{Serialize, de::DeserializeOwned};
13
14use crate::{
15    ContextSelectionSet, InputType, InputValueError, InputValueResult, Name, OutputType,
16    ServerResult, Value,
17    registry::{MetaType, MetaTypeId, Registry},
18};
19
20impl<K, V, S> InputType for HashMap<K, V, S>
21where
22    K: ToString + FromStr + Eq + Hash + Send + Sync,
23    K::Err: Display,
24    V: Serialize + DeserializeOwned + Send + Sync,
25    S: Default + BuildHasher + Send + Sync,
26{
27    type RawValueType = Self;
28
29    fn type_name() -> Cow<'static, str> {
30        Cow::Borrowed("JSONObject")
31    }
32
33    fn create_type_info(registry: &mut Registry) -> String {
34        registry.create_input_type::<Self, _>(MetaTypeId::Scalar, |_| MetaType::Scalar {
35            name: <Self as InputType>::type_name().to_string(),
36            description: Some("A scalar that can represent any JSON Object value.".to_string()),
37            is_valid: None,
38            visible: None,
39            inaccessible: false,
40            tags: Default::default(),
41            specified_by_url: None,
42            directive_invocations: Default::default(),
43            requires_scopes: Default::default(),
44        })
45    }
46
47    fn parse(value: Option<Value>) -> InputValueResult<Self> {
48        let value = value.unwrap_or_default();
49        match value {
50            Value::Object(map) => map
51                .into_iter()
52                .map(|(name, value)| {
53                    Ok((
54                        K::from_str(&name).map_err(|err| {
55                            InputValueError::<Self>::custom(format!("object key: {}", err))
56                        })?,
57                        from_value(value).map_err(|err| format!("object value: {}", err))?,
58                    ))
59                })
60                .collect::<Result<_, _>>()
61                .map_err(InputValueError::propagate),
62            _ => Err(InputValueError::expected_type(value)),
63        }
64    }
65
66    fn to_value(&self) -> Value {
67        let mut map = IndexMap::new();
68        for (name, value) in self {
69            map.insert(
70                Name::new(name.to_string()),
71                to_value(value).unwrap_or_default(),
72            );
73        }
74        Value::Object(map)
75    }
76
77    fn as_raw_value(&self) -> Option<&Self::RawValueType> {
78        Some(self)
79    }
80}
81
82#[cfg_attr(feature = "boxed-trait", async_trait::async_trait)]
83impl<K, V, S> OutputType for HashMap<K, V, S>
84where
85    K: ToString + Eq + Hash + Send + Sync,
86    V: Serialize + Send + Sync,
87    S: Send + Sync,
88{
89    fn type_name() -> Cow<'static, str> {
90        Cow::Borrowed("JSONObject")
91    }
92
93    fn create_type_info(registry: &mut Registry) -> String {
94        registry.create_output_type::<Self, _>(MetaTypeId::Scalar, |_| MetaType::Scalar {
95            name: <Self as OutputType>::type_name().to_string(),
96            description: Some("A scalar that can represent any JSON Object value.".to_string()),
97            is_valid: None,
98            visible: None,
99            inaccessible: false,
100            tags: Default::default(),
101            specified_by_url: None,
102            directive_invocations: Default::default(),
103            requires_scopes: Default::default(),
104        })
105    }
106
107    async fn resolve(
108        &self,
109        _ctx: &ContextSelectionSet<'_>,
110        _field: &Positioned<Field>,
111    ) -> ServerResult<Value> {
112        let mut map = IndexMap::new();
113        for (name, value) in self {
114            map.insert(
115                Name::new(name.to_string()),
116                to_value(value).unwrap_or_default(),
117            );
118        }
119        Ok(Value::Object(map))
120    }
121}