async_graphql/types/
maybe_undefined.rs

1use std::{borrow::Cow, ops::Deref};
2
3use serde::{Deserialize, Deserializer, Serialize, Serializer};
4
5use crate::{InputType, InputValueError, InputValueResult, Value, registry};
6
7/// Similar to `Option`, but it has three states, `undefined`, `null` and `x`.
8///
9/// **Reference:** <https://spec.graphql.org/October2021/#sec-Null-Value>
10///
11/// # Examples
12///
13/// ```rust
14/// use async_graphql::*;
15///
16/// struct Query;
17///
18/// #[Object]
19/// impl Query {
20///     async fn value1(&self, input: MaybeUndefined<i32>) -> i32 {
21///         if input.is_null() {
22///             1
23///         } else if input.is_undefined() {
24///             2
25///         } else {
26///             input.take().unwrap()
27///         }
28///     }
29/// }
30///
31/// # tokio::runtime::Runtime::new().unwrap().block_on(async {
32/// let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
33/// let query = r#"
34///     {
35///         v1:value1(input: 99)
36///         v2:value1(input: null)
37///         v3:value1
38///     }"#;
39/// assert_eq!(
40///     schema.execute(query).await.into_result().unwrap().data,
41///     value!({
42///         "v1": 99,
43///         "v2": 1,
44///         "v3": 2,
45///     })
46/// );
47/// # });
48/// ```
49#[allow(missing_docs)]
50#[derive(Copy, Clone, Default, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
51pub enum MaybeUndefined<T> {
52    #[default]
53    Undefined,
54    Null,
55    Value(T),
56}
57
58impl<T> MaybeUndefined<T> {
59    /// Returns true if the `MaybeUndefined<T>` is undefined.
60    #[inline]
61    pub const fn is_undefined(&self) -> bool {
62        matches!(self, MaybeUndefined::Undefined)
63    }
64
65    /// Returns true if the `MaybeUndefined<T>` is null.
66    #[inline]
67    pub const fn is_null(&self) -> bool {
68        matches!(self, MaybeUndefined::Null)
69    }
70
71    /// Returns true if the `MaybeUndefined<T>` contains value.
72    #[inline]
73    pub const fn is_value(&self) -> bool {
74        matches!(self, MaybeUndefined::Value(_))
75    }
76
77    /// Borrow the value, returns `None` if the the `MaybeUndefined<T>` is
78    /// `undefined` or `null`, otherwise returns `Some(T)`.
79    #[inline]
80    pub const fn value(&self) -> Option<&T> {
81        match self {
82            MaybeUndefined::Value(value) => Some(value),
83            _ => None,
84        }
85    }
86
87    /// Converts the `MaybeUndefined<T>` to `Option<T>`.
88    #[inline]
89    pub fn take(self) -> Option<T> {
90        match self {
91            MaybeUndefined::Value(value) => Some(value),
92            _ => None,
93        }
94    }
95
96    /// Converts the `MaybeUndefined<T>` to `Option<Option<T>>`.
97    #[inline]
98    pub const fn as_opt_ref(&self) -> Option<Option<&T>> {
99        match self {
100            MaybeUndefined::Undefined => None,
101            MaybeUndefined::Null => Some(None),
102            MaybeUndefined::Value(value) => Some(Some(value)),
103        }
104    }
105
106    /// Converts the `MaybeUndefined<T>` to `Option<Option<&U>>`.
107    #[inline]
108    pub fn as_opt_deref<U>(&self) -> Option<Option<&U>>
109    where
110        U: ?Sized,
111        T: Deref<Target = U>,
112    {
113        match self {
114            MaybeUndefined::Undefined => None,
115            MaybeUndefined::Null => Some(None),
116            MaybeUndefined::Value(value) => Some(Some(value.deref())),
117        }
118    }
119
120    /// Returns `true` if the `MaybeUndefined<T>` contains the given value.
121    #[inline]
122    pub fn contains_value<U>(&self, x: &U) -> bool
123    where
124        U: PartialEq<T>,
125    {
126        match self {
127            MaybeUndefined::Value(y) => x == y,
128            _ => false,
129        }
130    }
131
132    /// Returns `true` if the `MaybeUndefined<T>` contains the given nullable
133    /// value.
134    #[inline]
135    pub fn contains<U>(&self, x: &Option<U>) -> bool
136    where
137        U: PartialEq<T>,
138    {
139        match self {
140            MaybeUndefined::Value(y) => matches!(x, Some(v) if v == y),
141            MaybeUndefined::Null => x.is_none(),
142            MaybeUndefined::Undefined => false,
143        }
144    }
145
146    /// Maps a `MaybeUndefined<T>` to `MaybeUndefined<U>` by applying a function
147    /// to the contained nullable value
148    #[inline]
149    pub fn map<U, F: FnOnce(Option<T>) -> Option<U>>(self, f: F) -> MaybeUndefined<U> {
150        match self {
151            MaybeUndefined::Value(v) => match f(Some(v)) {
152                Some(v) => MaybeUndefined::Value(v),
153                None => MaybeUndefined::Null,
154            },
155            MaybeUndefined::Null => match f(None) {
156                Some(v) => MaybeUndefined::Value(v),
157                None => MaybeUndefined::Null,
158            },
159            MaybeUndefined::Undefined => MaybeUndefined::Undefined,
160        }
161    }
162
163    /// Maps a `MaybeUndefined<T>` to `MaybeUndefined<U>` by applying a function
164    /// to the contained value
165    #[inline]
166    pub fn map_value<U, F: FnOnce(T) -> U>(self, f: F) -> MaybeUndefined<U> {
167        match self {
168            MaybeUndefined::Value(v) => MaybeUndefined::Value(f(v)),
169            MaybeUndefined::Null => MaybeUndefined::Null,
170            MaybeUndefined::Undefined => MaybeUndefined::Undefined,
171        }
172    }
173
174    /// Update `value` if the `MaybeUndefined<T>` is not undefined.
175    ///
176    /// # Example
177    ///
178    /// ```rust
179    /// use async_graphql::MaybeUndefined;
180    ///
181    /// let mut value = None;
182    ///
183    /// MaybeUndefined::Value(10i32).update_to(&mut value);
184    /// assert_eq!(value, Some(10));
185    ///
186    /// MaybeUndefined::Undefined.update_to(&mut value);
187    /// assert_eq!(value, Some(10));
188    ///
189    /// MaybeUndefined::Null.update_to(&mut value);
190    /// assert_eq!(value, None);
191    /// ```
192    pub fn update_to(self, value: &mut Option<T>) {
193        match self {
194            MaybeUndefined::Value(new) => *value = Some(new),
195            MaybeUndefined::Null => *value = None,
196            MaybeUndefined::Undefined => {}
197        };
198    }
199}
200
201impl<T: InputType> InputType for MaybeUndefined<T> {
202    type RawValueType = T::RawValueType;
203
204    fn type_name() -> Cow<'static, str> {
205        T::type_name()
206    }
207
208    fn qualified_type_name() -> String {
209        T::type_name().to_string()
210    }
211
212    fn create_type_info(registry: &mut registry::Registry) -> String {
213        T::create_type_info(registry);
214        T::type_name().to_string()
215    }
216
217    fn parse(value: Option<Value>) -> InputValueResult<Self> {
218        match value {
219            None => Ok(MaybeUndefined::Undefined),
220            Some(Value::Null) => Ok(MaybeUndefined::Null),
221            Some(value) => Ok(MaybeUndefined::Value(
222                T::parse(Some(value)).map_err(InputValueError::propagate)?,
223            )),
224        }
225    }
226
227    fn to_value(&self) -> Value {
228        match self {
229            MaybeUndefined::Value(value) => value.to_value(),
230            _ => Value::Null,
231        }
232    }
233
234    fn as_raw_value(&self) -> Option<&Self::RawValueType> {
235        if let MaybeUndefined::Value(value) = self {
236            value.as_raw_value()
237        } else {
238            None
239        }
240    }
241}
242
243impl<T, E> MaybeUndefined<Result<T, E>> {
244    /// Transposes a `MaybeUndefined` of a [`Result`] into a [`Result`] of a
245    /// `MaybeUndefined`.
246    ///
247    /// [`MaybeUndefined::Undefined`] will be mapped to
248    /// [`Ok`]`(`[`MaybeUndefined::Undefined`]`)`. [`MaybeUndefined::Null`]
249    /// will be mapped to [`Ok`]`(`[`MaybeUndefined::Null`]`)`.
250    /// [`MaybeUndefined::Value`]`(`[`Ok`]`(_))` and
251    /// [`MaybeUndefined::Value`]`(`[`Err`]`(_))` will be mapped to
252    /// [`Ok`]`(`[`MaybeUndefined::Value`]`(_))` and [`Err`]`(_)`.
253    #[inline]
254    pub fn transpose(self) -> Result<MaybeUndefined<T>, E> {
255        match self {
256            MaybeUndefined::Undefined => Ok(MaybeUndefined::Undefined),
257            MaybeUndefined::Null => Ok(MaybeUndefined::Null),
258            MaybeUndefined::Value(Ok(v)) => Ok(MaybeUndefined::Value(v)),
259            MaybeUndefined::Value(Err(e)) => Err(e),
260        }
261    }
262}
263
264impl<T: Serialize> Serialize for MaybeUndefined<T> {
265    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
266        match self {
267            MaybeUndefined::Value(value) => value.serialize(serializer),
268            _ => serializer.serialize_none(),
269        }
270    }
271}
272
273impl<'de, T> Deserialize<'de> for MaybeUndefined<T>
274where
275    T: Deserialize<'de>,
276{
277    fn deserialize<D>(deserializer: D) -> Result<MaybeUndefined<T>, D::Error>
278    where
279        D: Deserializer<'de>,
280    {
281        Option::<T>::deserialize(deserializer).map(|value| match value {
282            Some(value) => MaybeUndefined::Value(value),
283            None => MaybeUndefined::Null,
284        })
285    }
286}
287
288impl<T> From<MaybeUndefined<T>> for Option<Option<T>> {
289    fn from(maybe_undefined: MaybeUndefined<T>) -> Self {
290        match maybe_undefined {
291            MaybeUndefined::Undefined => None,
292            MaybeUndefined::Null => Some(None),
293            MaybeUndefined::Value(value) => Some(Some(value)),
294        }
295    }
296}
297
298impl<T> From<Option<Option<T>>> for MaybeUndefined<T> {
299    fn from(value: Option<Option<T>>) -> Self {
300        match value {
301            Some(Some(value)) => Self::Value(value),
302            Some(None) => Self::Null,
303            None => Self::Undefined,
304        }
305    }
306}
307
308#[cfg(test)]
309mod tests {
310    use serde::{Deserialize, Serialize};
311
312    use crate::*;
313
314    #[test]
315    fn test_maybe_undefined_type() {
316        assert_eq!(MaybeUndefined::<i32>::type_name(), "Int");
317        assert_eq!(MaybeUndefined::<i32>::qualified_type_name(), "Int");
318        assert_eq!(&MaybeUndefined::<i32>::type_name(), "Int");
319        assert_eq!(&MaybeUndefined::<i32>::qualified_type_name(), "Int");
320    }
321
322    #[test]
323    fn test_maybe_undefined_serde() {
324        assert_eq!(
325            to_value(MaybeUndefined::Value(100i32)).unwrap(),
326            value!(100)
327        );
328
329        assert_eq!(
330            from_value::<MaybeUndefined<i32>>(value!(100)).unwrap(),
331            MaybeUndefined::Value(100)
332        );
333        assert_eq!(
334            from_value::<MaybeUndefined<i32>>(value!(null)).unwrap(),
335            MaybeUndefined::Null
336        );
337
338        #[derive(Serialize, Deserialize, Eq, PartialEq, Debug)]
339        struct A {
340            a: MaybeUndefined<i32>,
341        }
342
343        assert_eq!(
344            to_value(&A {
345                a: MaybeUndefined::Value(100i32)
346            })
347            .unwrap(),
348            value!({"a": 100})
349        );
350
351        assert_eq!(
352            to_value(&A {
353                a: MaybeUndefined::Null,
354            })
355            .unwrap(),
356            value!({ "a": null })
357        );
358
359        assert_eq!(
360            to_value(&A {
361                a: MaybeUndefined::Undefined,
362            })
363            .unwrap(),
364            value!({ "a": null })
365        );
366
367        assert_eq!(
368            from_value::<A>(value!({"a": 100})).unwrap(),
369            A {
370                a: MaybeUndefined::Value(100i32)
371            }
372        );
373
374        assert_eq!(
375            from_value::<A>(value!({ "a": null })).unwrap(),
376            A {
377                a: MaybeUndefined::Null
378            }
379        );
380
381        assert_eq!(
382            from_value::<A>(value!({})).unwrap(),
383            A {
384                a: MaybeUndefined::Null
385            }
386        );
387    }
388
389    #[test]
390    fn test_maybe_undefined_to_nested_option() {
391        assert_eq!(Option::<Option<i32>>::from(MaybeUndefined::Undefined), None);
392
393        assert_eq!(
394            Option::<Option<i32>>::from(MaybeUndefined::Null),
395            Some(None)
396        );
397
398        assert_eq!(
399            Option::<Option<i32>>::from(MaybeUndefined::Value(42)),
400            Some(Some(42))
401        );
402    }
403
404    #[test]
405    fn test_as_opt_ref() {
406        let value = MaybeUndefined::<String>::Undefined;
407        let r = value.as_opt_ref();
408        assert_eq!(r, None);
409
410        let value = MaybeUndefined::<String>::Null;
411        let r = value.as_opt_ref();
412        assert_eq!(r, Some(None));
413
414        let value = MaybeUndefined::<String>::Value("abc".to_string());
415        let r = value.as_opt_ref();
416        assert_eq!(r, Some(Some(&"abc".to_string())));
417    }
418
419    #[test]
420    fn test_as_opt_deref() {
421        let value = MaybeUndefined::<String>::Undefined;
422        let r = value.as_opt_deref();
423        assert_eq!(r, None);
424
425        let value = MaybeUndefined::<String>::Null;
426        let r = value.as_opt_deref();
427        assert_eq!(r, Some(None));
428
429        let value = MaybeUndefined::<String>::Value("abc".to_string());
430        let r = value.as_opt_deref();
431        assert_eq!(r, Some(Some("abc")));
432    }
433
434    #[test]
435    fn test_contains_value() {
436        let test = "abc";
437
438        let mut value: MaybeUndefined<String> = MaybeUndefined::Undefined;
439        assert!(!value.contains_value(&test));
440
441        value = MaybeUndefined::Null;
442        assert!(!value.contains_value(&test));
443
444        value = MaybeUndefined::Value("abc".to_string());
445        assert!(value.contains_value(&test));
446    }
447
448    #[test]
449    fn test_contains() {
450        let test = Some("abc");
451        let none: Option<&str> = None;
452
453        let mut value: MaybeUndefined<String> = MaybeUndefined::Undefined;
454        assert!(!value.contains(&test));
455        assert!(!value.contains(&none));
456
457        value = MaybeUndefined::Null;
458        assert!(!value.contains(&test));
459        assert!(value.contains(&none));
460
461        value = MaybeUndefined::Value("abc".to_string());
462        assert!(value.contains(&test));
463        assert!(!value.contains(&none));
464    }
465
466    #[test]
467    fn test_map_value() {
468        let mut value: MaybeUndefined<i32> = MaybeUndefined::Undefined;
469        assert_eq!(value.map_value(|v| v > 2), MaybeUndefined::Undefined);
470
471        value = MaybeUndefined::Null;
472        assert_eq!(value.map_value(|v| v > 2), MaybeUndefined::Null);
473
474        value = MaybeUndefined::Value(5);
475        assert_eq!(value.map_value(|v| v > 2), MaybeUndefined::Value(true));
476    }
477
478    #[test]
479    fn test_map() {
480        let mut value: MaybeUndefined<i32> = MaybeUndefined::Undefined;
481        assert_eq!(value.map(|v| Some(v.is_some())), MaybeUndefined::Undefined);
482
483        value = MaybeUndefined::Null;
484        assert_eq!(
485            value.map(|v| Some(v.is_some())),
486            MaybeUndefined::Value(false)
487        );
488
489        value = MaybeUndefined::Value(5);
490        assert_eq!(
491            value.map(|v| Some(v.is_some())),
492            MaybeUndefined::Value(true)
493        );
494    }
495
496    #[test]
497    fn test_transpose() {
498        let mut value: MaybeUndefined<Result<i32, &'static str>> = MaybeUndefined::Undefined;
499        assert_eq!(value.transpose(), Ok(MaybeUndefined::Undefined));
500
501        value = MaybeUndefined::Null;
502        assert_eq!(value.transpose(), Ok(MaybeUndefined::Null));
503
504        value = MaybeUndefined::Value(Ok(5));
505        assert_eq!(value.transpose(), Ok(MaybeUndefined::Value(5)));
506
507        value = MaybeUndefined::Value(Err("error"));
508        assert_eq!(value.transpose(), Err("error"));
509    }
510}