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