async_graphql/types/
json.rs

1use std::{
2    borrow::Cow,
3    ops::{Deref, DerefMut},
4};
5
6use serde::{de::DeserializeOwned, Deserialize, Serialize};
7
8use crate::{
9    from_value,
10    parser::types::Field,
11    registry::{MetaType, MetaTypeId, Registry},
12    to_value, ContextSelectionSet, InputType, InputValueResult, OutputType, Positioned,
13    ServerResult, Value,
14};
15
16/// A scalar that can represent any JSON value.
17///
18/// If the inner type cannot be serialized as JSON (e.g. it has non-string keys)
19/// it will be `null`.
20#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Hash, Default)]
21#[serde(transparent)]
22pub struct Json<T>(pub T);
23
24impl<T> Deref for Json<T> {
25    type Target = T;
26
27    fn deref(&self) -> &Self::Target {
28        &self.0
29    }
30}
31
32impl<T> DerefMut for Json<T> {
33    fn deref_mut(&mut self) -> &mut Self::Target {
34        &mut self.0
35    }
36}
37
38impl<T> From<T> for Json<T> {
39    fn from(value: T) -> Self {
40        Self(value)
41    }
42}
43
44impl<T: DeserializeOwned + Serialize + Send + Sync> InputType for Json<T> {
45    type RawValueType = T;
46
47    fn type_name() -> Cow<'static, str> {
48        Cow::Borrowed("JSON")
49    }
50
51    fn create_type_info(registry: &mut Registry) -> String {
52        registry.create_input_type::<Json<T>, _>(MetaTypeId::Scalar, |_| MetaType::Scalar {
53            name: <Self as InputType>::type_name().to_string(),
54            description: Some("A scalar that can represent any JSON value.".to_string()),
55            is_valid: None,
56            visible: None,
57            inaccessible: false,
58            tags: Default::default(),
59            specified_by_url: None,
60            directive_invocations: Default::default(),
61        })
62    }
63
64    fn parse(value: Option<Value>) -> InputValueResult<Self> {
65        Ok(from_value(value.unwrap_or_default())?)
66    }
67
68    fn to_value(&self) -> Value {
69        Value::String(serde_json::to_string(&self.0).unwrap_or_default())
70    }
71
72    fn as_raw_value(&self) -> Option<&Self::RawValueType> {
73        Some(&self.0)
74    }
75}
76
77#[cfg_attr(feature = "boxed-trait", async_trait::async_trait)]
78impl<T: Serialize + Send + Sync> OutputType for Json<T> {
79    fn type_name() -> Cow<'static, str> {
80        Cow::Borrowed("JSON")
81    }
82
83    fn create_type_info(registry: &mut Registry) -> String {
84        registry.create_output_type::<Json<T>, _>(MetaTypeId::Scalar, |_| MetaType::Scalar {
85            name: <Self as OutputType>::type_name().to_string(),
86            description: Some("A scalar that can represent any JSON value.".to_string()),
87            is_valid: None,
88            visible: None,
89            inaccessible: false,
90            tags: Default::default(),
91            specified_by_url: None,
92            directive_invocations: Default::default(),
93        })
94    }
95
96    async fn resolve(
97        &self,
98        _ctx: &ContextSelectionSet<'_>,
99        _field: &Positioned<Field>,
100    ) -> ServerResult<Value> {
101        Ok(to_value(&self.0).ok().unwrap_or_default())
102    }
103}
104
105impl InputType for serde_json::Value {
106    type RawValueType = serde_json::Value;
107
108    fn type_name() -> Cow<'static, str> {
109        Cow::Borrowed("JSON")
110    }
111
112    fn create_type_info(registry: &mut Registry) -> String {
113        registry.create_input_type::<serde_json::Value, _>(MetaTypeId::Scalar, |_| {
114            MetaType::Scalar {
115                name: <Self as InputType>::type_name().to_string(),
116                description: Some("A scalar that can represent any JSON value.".to_string()),
117                is_valid: None,
118                visible: None,
119                inaccessible: false,
120                tags: Default::default(),
121                specified_by_url: None,
122                directive_invocations: Default::default(),
123            }
124        })
125    }
126
127    fn parse(value: Option<Value>) -> InputValueResult<Self> {
128        Ok(from_value(value.unwrap_or_default())?)
129    }
130
131    fn to_value(&self) -> Value {
132        Value::String(self.to_string())
133    }
134
135    fn as_raw_value(&self) -> Option<&Self::RawValueType> {
136        Some(&self)
137    }
138}
139
140#[cfg_attr(feature = "boxed-trait", async_trait::async_trait)]
141impl OutputType for serde_json::Value {
142    fn type_name() -> Cow<'static, str> {
143        Cow::Borrowed("JSON")
144    }
145
146    fn create_type_info(registry: &mut Registry) -> String {
147        registry.create_output_type::<serde_json::Value, _>(MetaTypeId::Scalar, |_| {
148            MetaType::Scalar {
149                name: <Self as OutputType>::type_name().to_string(),
150                description: Some("A scalar that can represent any JSON value.".to_string()),
151                is_valid: None,
152                visible: None,
153                inaccessible: false,
154                tags: Default::default(),
155                specified_by_url: None,
156                directive_invocations: Default::default(),
157            }
158        })
159    }
160
161    async fn resolve(
162        &self,
163        _ctx: &ContextSelectionSet<'_>,
164        _field: &Positioned<Field>,
165    ) -> ServerResult<Value> {
166        Ok(to_value(self).ok().unwrap_or_default())
167    }
168}
169
170#[cfg(test)]
171mod test {
172    use std::collections::HashMap;
173
174    use serde::{Deserialize, Serialize};
175
176    use crate::*;
177
178    #[tokio::test]
179    async fn test_json_type() {
180        #[derive(Serialize, Deserialize)]
181        struct MyStruct {
182            a: i32,
183            b: i32,
184            c: HashMap<String, i32>,
185        }
186
187        struct Query;
188
189        #[Object(internal)]
190        impl Query {
191            async fn obj(&self, input: Json<MyStruct>) -> Json<MyStruct> {
192                input
193            }
194        }
195
196        let query = r#"{ obj(input: { a: 1, b: 2, c: { a: 11, b: 22 } } ) }"#;
197        let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
198        assert_eq!(
199            schema.execute(query).await.into_result().unwrap().data,
200            value!({
201             "obj": {
202                 "a": 1,
203                 "b": 2,
204                 "c": { "a": 11, "b": 22 }
205             }
206            })
207        );
208    }
209
210    #[tokio::test]
211    async fn test_json_type_for_serialize_only() {
212        #[derive(Serialize)]
213        struct MyStruct {
214            a: i32,
215            b: i32,
216            c: HashMap<String, i32>,
217        }
218
219        struct Query;
220
221        #[Object(internal)]
222        impl Query {
223            async fn obj(&self) -> Json<MyStruct> {
224                MyStruct {
225                    a: 1,
226                    b: 2,
227                    c: {
228                        let mut values = HashMap::new();
229                        values.insert("a".to_string(), 11);
230                        values.insert("b".to_string(), 22);
231                        values
232                    },
233                }
234                .into()
235            }
236        }
237
238        let query = r#"{ obj }"#;
239        let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
240        assert_eq!(
241            schema.execute(query).await.into_result().unwrap().data,
242            value!({
243             "obj": {
244                 "a": 1,
245                 "b": 2,
246                 "c": { "a": 11, "b": 22 }
247             }
248            })
249        );
250    }
251}