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