async_graphql/types/external/
bson.rs

1#[cfg(feature = "chrono")]
2use bson::DateTime as UtcDateTime;
3use bson::{oid::ObjectId, Bson, Document, Uuid};
4#[cfg(feature = "chrono")]
5use chrono::{DateTime, Utc};
6
7use crate::{InputValueError, InputValueResult, Scalar, ScalarType, Value};
8
9#[Scalar(internal)]
10impl ScalarType for ObjectId {
11    fn parse(value: Value) -> InputValueResult<Self> {
12        match value {
13            Value::String(s) => Ok(ObjectId::parse_str(s)?),
14            Value::Object(o) => {
15                let json = Value::Object(o).into_json()?;
16                let bson = Bson::try_from(json)?;
17                bson.as_object_id().ok_or(InputValueError::custom(
18                    "could not parse the value as a BSON ObjectId",
19                ))
20            }
21            _ => Err(InputValueError::expected_type(value)),
22        }
23    }
24
25    fn to_value(&self) -> Value {
26        Value::String(self.to_string())
27    }
28}
29
30#[Scalar(internal, name = "UUID")]
31impl ScalarType for Uuid {
32    fn parse(value: Value) -> InputValueResult<Self> {
33        match value {
34            Value::String(s) => Ok(Uuid::parse_str(s)?),
35            Value::Object(o) => {
36                let json = Value::Object(o).into_json()?;
37                let Bson::Binary(binary) = Bson::try_from(json)? else {
38                    return Err(InputValueError::custom(
39                        "could not parse the value as BSON Binary",
40                    ));
41                };
42                binary.to_uuid().map_err(|_| {
43                    InputValueError::custom("could not deserialize BSON Binary to Uuid")
44                })
45            }
46            _ => Err(InputValueError::expected_type(value)),
47        }
48    }
49
50    fn to_value(&self) -> Value {
51        Value::String(self.to_string())
52    }
53}
54
55#[cfg(feature = "chrono")]
56#[Scalar(internal, name = "DateTime")]
57impl ScalarType for UtcDateTime {
58    fn parse(value: Value) -> InputValueResult<Self> {
59        <DateTime<Utc>>::parse(value)
60            .map_err(InputValueError::propagate)
61            .map(UtcDateTime::from_chrono)
62    }
63
64    fn to_value(&self) -> Value {
65        self.to_chrono().to_value()
66    }
67}
68
69#[Scalar(internal, name = "JSON")]
70impl ScalarType for Bson {
71    fn parse(value: Value) -> InputValueResult<Self> {
72        bson::to_bson(&value).map_err(InputValueError::custom)
73    }
74
75    fn to_value(&self) -> Value {
76        bson::from_bson(self.clone()).unwrap_or_default()
77    }
78}
79
80#[Scalar(internal, name = "JSONObject")]
81impl ScalarType for Document {
82    fn parse(value: Value) -> InputValueResult<Self> {
83        bson::to_document(&value).map_err(InputValueError::custom)
84    }
85
86    fn to_value(&self) -> Value {
87        bson::from_document(self.clone()).unwrap_or_default()
88    }
89}
90
91#[cfg(test)]
92mod tests {
93    use serde_json::json;
94
95    use super::*;
96
97    #[test]
98    fn test_parse_bson_uuid() {
99        let id = Uuid::new();
100        let bson_value = bson::bson!(id);
101        let extended_json_value = json!(bson_value);
102        let gql_value = Value::from_json(extended_json_value).expect("valid json");
103        assert_eq!(
104            id,
105            <Uuid as ScalarType>::parse(gql_value).expect("parsing succeeds")
106        );
107    }
108
109    #[test]
110    fn test_parse_bson_object_id() {
111        let id = ObjectId::from_bytes([42; 12]);
112        let bson_value = bson::bson!(id);
113        let extended_json_value = json!(bson_value);
114        let gql_value = Value::from_json(extended_json_value).expect("valid json");
115        assert_eq!(
116            id,
117            <ObjectId as ScalarType>::parse(gql_value).expect("parsing succeeds")
118        );
119    }
120}