async_graphql/types/external/json_object/
btreemap.rs

1use 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}