poem_openapi/types/
maybe_undefined.rs

1use std::{borrow::Cow, ops::Deref};
2
3use poem::{http::HeaderValue, web::Field as PoemField};
4use serde::{Deserialize, Deserializer, Serialize, Serializer};
5use serde_json::Value;
6
7use crate::{
8    registry::{MetaSchemaRef, Registry},
9    types::{
10        ParseError, ParseFromJSON, ParseFromMultipartField, ParseFromParameter, ParseResult,
11        ToHeader, ToJSON, Type,
12    },
13};
14
15/// Similar to `Option`, but it has three states, `undefined`, `null` and `x`.
16///
17/// # Example
18///
19/// ```
20/// use poem::{test::TestClient, IntoEndpoint};
21/// use poem_openapi::{payload::Json, types::MaybeUndefined, Object, OpenApi, OpenApiService};
22/// use tokio::sync::Mutex;
23/// use serde_json::json;
24///
25/// #[derive(Object, Clone, Default)]
26/// struct Resource {
27///     attr1: Option<i32>,
28///     attr2: Option<String>,
29/// }
30///
31/// #[derive(Object)]
32/// struct UpdateResourceRequest {
33///     attr1: MaybeUndefined<i32>,
34///     attr2: MaybeUndefined<String>,
35/// }
36///
37/// struct Api {
38///     resource: Mutex<Resource>,
39/// }
40///
41/// #[OpenApi]
42/// impl Api {
43///     #[oai(path = "/get", method = "get")]
44///     async fn get_resource(&self) -> Json<Resource> {
45///         Json(self.resource.lock().await.clone())
46///     }
47///
48///     #[oai(path = "/put", method = "put")]
49///     async fn update_resource(&self, req: Json<UpdateResourceRequest>) {
50///         let mut resource = self.resource.lock().await;
51///
52///         match req.0.attr1 {
53///             MaybeUndefined::Null => resource.attr1 = None,
54///             MaybeUndefined::Value(value) => resource.attr1 = Some(value),
55///             MaybeUndefined::Undefined => {}
56///         }
57///
58///         match req.0.attr2 {
59///             MaybeUndefined::Null => resource.attr2 = None,
60///             MaybeUndefined::Value(value) => resource.attr2 = Some(value),
61///             MaybeUndefined::Undefined => {}
62///         }
63///     }
64/// }
65///
66/// let api_service = OpenApiService::new(
67///     Api {
68///         resource: Default::default(),
69///     },
70///     "Test",
71///     "1.0",
72/// );
73///
74/// let cli = TestClient::new(api_service.into_endpoint());
75///
76/// # tokio::runtime::Runtime::new().unwrap().block_on(async {
77/// cli.get("/get").send().await.assert_json(json!({"attr1": null, "attr2": null}));
78///
79/// cli.put("/put").body_json(&json!({"attr1": 100i32})).send().await.assert_status_is_ok();
80/// cli.get("/get").send().await.assert_json(json!({"attr1": 100i32, "attr2": null}));
81///
82/// cli.put("/put").body_json(&json!({"attr1": null, "attr2": "abc"})).send().await.assert_status_is_ok();
83/// cli.get("/get").send().await.assert_json(json!({"attr1": null, "attr2": "abc"}));
84/// # });
85/// ```
86#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
87pub enum MaybeUndefined<T> {
88    /// Undefined
89    Undefined,
90    /// Null
91    Null,
92    /// Value
93    Value(T),
94}
95
96impl<T> Default for MaybeUndefined<T> {
97    fn default() -> Self {
98        Self::Undefined
99    }
100}
101
102impl<T> From<T> for MaybeUndefined<T> {
103    fn from(value: T) -> Self {
104        MaybeUndefined::Value(value)
105    }
106}
107
108impl<T> IntoIterator for MaybeUndefined<T> {
109    type Item = T;
110    type IntoIter = std::option::IntoIter<T>;
111
112    fn into_iter(self) -> Self::IntoIter {
113        self.take().into_iter()
114    }
115}
116
117impl<T> MaybeUndefined<T> {
118    /// Create a `MaybeUndefined<T>` from `Option<T>`, returns
119    /// `MaybeUndefined::Undefined` if this value is none.
120    pub fn from_opt_undefined(value: Option<T>) -> Self {
121        match value {
122            Some(value) => MaybeUndefined::Value(value),
123            None => MaybeUndefined::Undefined,
124        }
125    }
126
127    /// Create a `MaybeUndefined<T>` from `Option<T>`, returns
128    /// `MaybeUndefined::Null` if this value is none.
129    pub fn from_opt_null(value: Option<T>) -> Self {
130        match value {
131            Some(value) => MaybeUndefined::Value(value),
132            None => MaybeUndefined::Null,
133        }
134    }
135
136    /// Returns true if the `MaybeUndefined<T>` is undefined.
137    #[inline]
138    pub const fn is_undefined(&self) -> bool {
139        matches!(self, MaybeUndefined::Undefined)
140    }
141
142    /// Returns true if the `MaybeUndefined<T>` is null.
143    #[inline]
144    pub const fn is_null(&self) -> bool {
145        matches!(self, MaybeUndefined::Null)
146    }
147
148    /// Returns true if the `MaybeUndefined<T>` contains value.
149    #[inline]
150    pub const fn is_value(&self) -> bool {
151        matches!(self, MaybeUndefined::Value(_))
152    }
153
154    /// Returns `None` if the the `MaybeUndefined<T>` is
155    /// `undefined` or `null`, otherwise returns `Some(&T)`.
156    #[inline]
157    pub const fn value(&self) -> Option<&T> {
158        match self {
159            MaybeUndefined::Value(value) => Some(value),
160            _ => None,
161        }
162    }
163
164    /// Returns `None` if the the `MaybeUndefined<T>` is
165    /// `undefined` or `null`, otherwise returns `Some(&mut T)`.
166    #[inline]
167    pub fn value_mut(&mut self) -> Option<&mut T> {
168        match self {
169            MaybeUndefined::Value(value) => Some(value),
170            _ => None,
171        }
172    }
173
174    /// Converts the `MaybeUndefined<T>` to `Option<T>`.
175    #[inline]
176    pub fn take(self) -> Option<T> {
177        match self {
178            MaybeUndefined::Value(value) => Some(value),
179            _ => None,
180        }
181    }
182
183    /// Converts from `&MaybeUndefined<T>` to `MaybeUndefined<&T>`.
184    #[inline]
185    pub const fn as_ref(&self) -> MaybeUndefined<&T> {
186        match self {
187            MaybeUndefined::Undefined => MaybeUndefined::Undefined,
188            MaybeUndefined::Null => MaybeUndefined::Null,
189            MaybeUndefined::Value(value) => MaybeUndefined::Value(value),
190        }
191    }
192
193    /// Converts the `MaybeUndefined<T>` to `Option<Option<T>>`.
194    #[inline]
195    pub const fn as_opt_ref(&self) -> Option<Option<&T>> {
196        match self {
197            MaybeUndefined::Undefined => None,
198            MaybeUndefined::Null => Some(None),
199            MaybeUndefined::Value(value) => Some(Some(value)),
200        }
201    }
202
203    /// Converts the `MaybeUndefined<T>` to `Option<Option<&U>>`.
204    #[inline]
205    pub fn as_opt_deref<U>(&self) -> Option<Option<&U>>
206    where
207        U: ?Sized,
208        T: Deref<Target = U>,
209    {
210        match self {
211            MaybeUndefined::Undefined => None,
212            MaybeUndefined::Null => Some(None),
213            MaybeUndefined::Value(value) => Some(Some(value.deref())),
214        }
215    }
216
217    /// Returns `true` if the `MaybeUndefined<T>` contains the given value.
218    #[inline]
219    pub fn contains_value<U>(&self, x: &U) -> bool
220    where
221        U: PartialEq<T>,
222    {
223        match self {
224            MaybeUndefined::Value(y) => x == y,
225            _ => false,
226        }
227    }
228
229    /// Returns `true` if the `MaybeUndefined<T>` contains the given nullable
230    /// value.
231    #[inline]
232    pub fn contains<U>(&self, x: &Option<U>) -> bool
233    where
234        U: PartialEq<T>,
235    {
236        match self {
237            MaybeUndefined::Value(y) => matches!(x, Some(v) if v == y),
238            MaybeUndefined::Null => x.is_none(),
239            MaybeUndefined::Undefined => false,
240        }
241    }
242
243    /// Maps a `MaybeUndefined<T>` to `MaybeUndefined<U>` by applying a function
244    /// to the contained nullable value
245    #[inline]
246    pub fn map<U, F: FnOnce(Option<T>) -> Option<U>>(self, f: F) -> MaybeUndefined<U> {
247        match self {
248            MaybeUndefined::Value(v) => match f(Some(v)) {
249                Some(v) => MaybeUndefined::Value(v),
250                None => MaybeUndefined::Null,
251            },
252            MaybeUndefined::Null => match f(None) {
253                Some(v) => MaybeUndefined::Value(v),
254                None => MaybeUndefined::Null,
255            },
256            MaybeUndefined::Undefined => MaybeUndefined::Undefined,
257        }
258    }
259
260    /// Maps a `MaybeUndefined<T>` to `MaybeUndefined<U>` by applying a function
261    /// to the contained value
262    #[inline]
263    pub fn map_value<U, F: FnOnce(T) -> U>(self, f: F) -> MaybeUndefined<U> {
264        match self {
265            MaybeUndefined::Value(v) => MaybeUndefined::Value(f(v)),
266            MaybeUndefined::Null => MaybeUndefined::Null,
267            MaybeUndefined::Undefined => MaybeUndefined::Undefined,
268        }
269    }
270
271    /// Update `value` if the `MaybeUndefined<T>` is not undefined.
272    ///
273    /// # Example
274    ///
275    /// ```rust
276    /// use poem_openapi::types::MaybeUndefined;
277    ///
278    /// let mut value = None;
279    ///
280    /// MaybeUndefined::Value(10i32).update_to(&mut value);
281    /// assert_eq!(value, Some(10));
282    ///
283    /// MaybeUndefined::Undefined.update_to(&mut value);
284    /// assert_eq!(value, Some(10));
285    ///
286    /// MaybeUndefined::Null.update_to(&mut value);
287    /// assert_eq!(value, None);
288    /// ```
289    pub fn update_to(self, value: &mut Option<T>) {
290        match self {
291            MaybeUndefined::Value(new) => *value = Some(new),
292            MaybeUndefined::Null => *value = None,
293            MaybeUndefined::Undefined => {}
294        };
295    }
296}
297
298impl<T: Deref> MaybeUndefined<T> {
299    /// Converts from `MaybeUndefined<T>` (or `&MaybeUndefined<T>`) to
300    /// `MaybeUndefined<&T::Target>`.
301    #[inline]
302    pub fn as_deref(&self) -> MaybeUndefined<&T::Target> {
303        match self {
304            MaybeUndefined::Undefined => MaybeUndefined::Undefined,
305            MaybeUndefined::Null => MaybeUndefined::Null,
306            MaybeUndefined::Value(value) => MaybeUndefined::Value(value.deref()),
307        }
308    }
309}
310
311impl<T: Type> Type for MaybeUndefined<T> {
312    const IS_REQUIRED: bool = false;
313
314    type RawValueType = T::RawValueType;
315
316    type RawElementValueType = T::RawElementValueType;
317
318    fn name() -> Cow<'static, str> {
319        format!("optional_{}", T::name()).into()
320    }
321
322    fn schema_ref() -> MetaSchemaRef {
323        T::schema_ref()
324    }
325
326    fn register(registry: &mut Registry) {
327        T::register(registry);
328    }
329
330    fn as_raw_value(&self) -> Option<&Self::RawValueType> {
331        match self {
332            MaybeUndefined::Value(value) => value.as_raw_value(),
333            _ => None,
334        }
335    }
336
337    fn raw_element_iter<'a>(
338        &'a self,
339    ) -> Box<dyn Iterator<Item = &'a Self::RawElementValueType> + 'a> {
340        match self {
341            MaybeUndefined::Value(value) => value.raw_element_iter(),
342            _ => Box::new(std::iter::empty()),
343        }
344    }
345
346    #[inline]
347    fn is_none(&self) -> bool {
348        match self {
349            MaybeUndefined::Undefined | MaybeUndefined::Null => true,
350            MaybeUndefined::Value(_) => false,
351        }
352    }
353}
354
355impl<T: ParseFromJSON> ParseFromJSON for MaybeUndefined<T> {
356    fn parse_from_json(value: Option<Value>) -> ParseResult<Self> {
357        match value {
358            Some(Value::Null) => Ok(MaybeUndefined::Null),
359            Some(value) => Ok(MaybeUndefined::Value(
360                T::parse_from_json(Some(value)).map_err(ParseError::propagate)?,
361            )),
362            None => Ok(MaybeUndefined::Undefined),
363        }
364    }
365}
366
367impl<T: ParseFromParameter> ParseFromParameter for MaybeUndefined<T> {
368    fn parse_from_parameter(_value: &str) -> ParseResult<Self> {
369        unreachable!()
370    }
371
372    fn parse_from_parameters<I: IntoIterator<Item = A>, A: AsRef<str>>(
373        iter: I,
374    ) -> ParseResult<Self> {
375        let mut iter = iter.into_iter().peekable();
376
377        if iter.peek().is_none() {
378            return Ok(MaybeUndefined::Undefined);
379        }
380
381        T::parse_from_parameters(iter)
382            .map_err(ParseError::propagate)
383            .map(MaybeUndefined::Value)
384    }
385}
386
387impl<T: ParseFromMultipartField> ParseFromMultipartField for MaybeUndefined<T> {
388    async fn parse_from_multipart(value: Option<PoemField>) -> ParseResult<Self> {
389        match value {
390            Some(value) => T::parse_from_multipart(Some(value))
391                .await
392                .map_err(ParseError::propagate)
393                .map(MaybeUndefined::Value),
394            None => Ok(MaybeUndefined::Undefined),
395        }
396    }
397}
398
399impl<T: ToJSON> ToJSON for MaybeUndefined<T> {
400    fn to_json(&self) -> Option<Value> {
401        match self {
402            MaybeUndefined::Value(value) => value.to_json(),
403            MaybeUndefined::Undefined => None,
404            MaybeUndefined::Null => Some(Value::Null),
405        }
406    }
407}
408
409impl<T: ToHeader> ToHeader for MaybeUndefined<T> {
410    fn to_header(&self) -> Option<HeaderValue> {
411        match self {
412            MaybeUndefined::Value(value) => value.to_header(),
413            _ => None,
414        }
415    }
416}
417
418impl<T, E> MaybeUndefined<Result<T, E>> {
419    /// Transposes a `MaybeUndefined` of a [`Result`] into a [`Result`] of a
420    /// `MaybeUndefined`.
421    ///
422    /// [`MaybeUndefined::Undefined`] will be mapped to
423    /// [`Ok`]`(`[`MaybeUndefined::Undefined`]`)`. [`MaybeUndefined::Null`]
424    /// will be mapped to [`Ok`]`(`[`MaybeUndefined::Null`]`)`.
425    /// [`MaybeUndefined::Value`]`(`[`Ok`]`(_))` and
426    /// [`MaybeUndefined::Value`]`(`[`Err`]`(_))` will be mapped to
427    /// [`Ok`]`(`[`MaybeUndefined::Value`]`(_))` and [`Err`]`(_)`.
428    #[inline]
429    pub fn transpose(self) -> Result<MaybeUndefined<T>, E> {
430        match self {
431            MaybeUndefined::Undefined => Ok(MaybeUndefined::Undefined),
432            MaybeUndefined::Null => Ok(MaybeUndefined::Null),
433            MaybeUndefined::Value(Ok(v)) => Ok(MaybeUndefined::Value(v)),
434            MaybeUndefined::Value(Err(e)) => Err(e),
435        }
436    }
437}
438
439impl<T: Serialize> Serialize for MaybeUndefined<T> {
440    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
441        match self {
442            MaybeUndefined::Value(value) => value.serialize(serializer),
443            _ => serializer.serialize_none(),
444        }
445    }
446}
447
448impl<'de, T> Deserialize<'de> for MaybeUndefined<T>
449where
450    T: Deserialize<'de>,
451{
452    fn deserialize<D>(deserializer: D) -> Result<MaybeUndefined<T>, D::Error>
453    where
454        D: Deserializer<'de>,
455    {
456        Option::<T>::deserialize(deserializer).map(|value| match value {
457            Some(value) => MaybeUndefined::Value(value),
458            None => MaybeUndefined::Null,
459        })
460    }
461}
462
463impl<T> From<MaybeUndefined<T>> for Option<Option<T>> {
464    fn from(maybe_undefined: MaybeUndefined<T>) -> Self {
465        match maybe_undefined {
466            MaybeUndefined::Undefined => None,
467            MaybeUndefined::Null => Some(None),
468            MaybeUndefined::Value(value) => Some(Some(value)),
469        }
470    }
471}
472
473impl<T> From<Option<Option<T>>> for MaybeUndefined<T> {
474    fn from(value: Option<Option<T>>) -> Self {
475        match value {
476            Some(Some(value)) => Self::Value(value),
477            Some(None) => Self::Null,
478            None => Self::Undefined,
479        }
480    }
481}
482
483#[cfg(test)]
484mod tests {
485    use serde::{Deserialize, Serialize};
486    use serde_json::json;
487
488    use super::*;
489    use crate::Object;
490
491    #[test]
492    fn test_maybe_undefined_serde() {
493        assert_eq!(
494            serde_json::to_value(MaybeUndefined::Value(100i32)).unwrap(),
495            json!(100)
496        );
497
498        assert_eq!(
499            serde_json::from_value::<MaybeUndefined<i32>>(json!(100)).unwrap(),
500            MaybeUndefined::Value(100)
501        );
502        assert_eq!(
503            serde_json::from_value::<MaybeUndefined<i32>>(json!(null)).unwrap(),
504            MaybeUndefined::Null
505        );
506
507        #[derive(Serialize, Deserialize, Eq, PartialEq, Debug)]
508        struct A {
509            a: MaybeUndefined<i32>,
510        }
511
512        assert_eq!(
513            serde_json::to_value(&A {
514                a: MaybeUndefined::Value(100i32)
515            })
516            .unwrap(),
517            json!({"a": 100})
518        );
519
520        assert_eq!(
521            serde_json::to_value(&A {
522                a: MaybeUndefined::Null,
523            })
524            .unwrap(),
525            json!({ "a": null })
526        );
527
528        assert_eq!(
529            serde_json::to_value(&A {
530                a: MaybeUndefined::Undefined,
531            })
532            .unwrap(),
533            json!({ "a": null })
534        );
535
536        assert_eq!(
537            serde_json::from_value::<A>(json!({"a": 100})).unwrap(),
538            A {
539                a: MaybeUndefined::Value(100i32)
540            }
541        );
542
543        assert_eq!(
544            serde_json::from_value::<A>(json!({ "a": null })).unwrap(),
545            A {
546                a: MaybeUndefined::Null
547            }
548        );
549
550        assert_eq!(
551            serde_json::from_value::<A>(json!({})).unwrap(),
552            A {
553                a: MaybeUndefined::Null
554            }
555        );
556    }
557
558    #[test]
559    fn test_maybe_undefined_to_nested_option() {
560        assert_eq!(Option::<Option<i32>>::from(MaybeUndefined::Undefined), None);
561
562        assert_eq!(
563            Option::<Option<i32>>::from(MaybeUndefined::Null),
564            Some(None)
565        );
566
567        assert_eq!(
568            Option::<Option<i32>>::from(MaybeUndefined::Value(42)),
569            Some(Some(42))
570        );
571    }
572
573    #[test]
574    fn test_as_opt_ref() {
575        let mut value = MaybeUndefined::<String>::Undefined;
576        let mut r = value.as_opt_ref();
577        assert_eq!(r, None);
578
579        value = MaybeUndefined::Null;
580        r = value.as_opt_ref();
581        assert_eq!(r, Some(None));
582
583        value = MaybeUndefined::Value("abc".to_string());
584        r = value.as_opt_ref();
585        assert_eq!(r, Some(Some(&"abc".to_string())));
586    }
587
588    #[test]
589    fn test_as_opt_deref() {
590        let mut value = MaybeUndefined::<String>::Undefined;
591        let mut r = value.as_opt_deref();
592        assert_eq!(r, None);
593
594        value = MaybeUndefined::Null;
595        r = value.as_opt_deref();
596        assert_eq!(r, Some(None));
597
598        value = MaybeUndefined::Value("abc".to_string());
599        r = value.as_opt_deref();
600        assert_eq!(r, Some(Some("abc")));
601    }
602
603    #[test]
604    fn test_contains_value() {
605        let test = "abc";
606
607        let mut value: MaybeUndefined<String> = MaybeUndefined::Undefined;
608        assert!(!value.contains_value(&test));
609
610        value = MaybeUndefined::Null;
611        assert!(!value.contains_value(&test));
612
613        value = MaybeUndefined::Value("abc".to_string());
614        assert!(value.contains_value(&test));
615    }
616
617    #[test]
618    fn test_contains() {
619        let test = Some("abc");
620        let none: Option<&str> = None;
621
622        let mut value: MaybeUndefined<String> = MaybeUndefined::Undefined;
623        assert!(!value.contains(&test));
624        assert!(!value.contains(&none));
625
626        value = MaybeUndefined::Null;
627        assert!(!value.contains(&test));
628        assert!(value.contains(&none));
629
630        value = MaybeUndefined::Value("abc".to_string());
631        assert!(value.contains(&test));
632        assert!(!value.contains(&none));
633    }
634
635    #[test]
636    fn test_map_value() {
637        let mut value: MaybeUndefined<i32> = MaybeUndefined::Undefined;
638        assert_eq!(value.map_value(|v| v > 2), MaybeUndefined::Undefined);
639
640        value = MaybeUndefined::Null;
641        assert_eq!(value.map_value(|v| v > 2), MaybeUndefined::Null);
642
643        value = MaybeUndefined::Value(5);
644        assert_eq!(value.map_value(|v| v > 2), MaybeUndefined::Value(true));
645    }
646
647    #[test]
648    fn test_map() {
649        let mut value: MaybeUndefined<i32> = MaybeUndefined::Undefined;
650        assert_eq!(value.map(|v| Some(v.is_some())), MaybeUndefined::Undefined);
651
652        value = MaybeUndefined::Null;
653        assert_eq!(
654            value.map(|v| Some(v.is_some())),
655            MaybeUndefined::Value(false)
656        );
657
658        value = MaybeUndefined::Value(5);
659        assert_eq!(
660            value.map(|v| Some(v.is_some())),
661            MaybeUndefined::Value(true)
662        );
663    }
664
665    #[test]
666    fn test_transpose() {
667        let mut value: MaybeUndefined<Result<i32, &'static str>> = MaybeUndefined::Undefined;
668        assert_eq!(value.transpose(), Ok(MaybeUndefined::Undefined));
669
670        value = MaybeUndefined::Null;
671        assert_eq!(value.transpose(), Ok(MaybeUndefined::Null));
672
673        value = MaybeUndefined::Value(Ok(5));
674        assert_eq!(value.transpose(), Ok(MaybeUndefined::Value(5)));
675
676        value = MaybeUndefined::Value(Err("error"));
677        assert_eq!(value.transpose(), Err("error"));
678    }
679
680    #[test]
681    fn test_parse_from_json() {
682        assert_eq!(
683            MaybeUndefined::<i32>::parse_from_json(Some(json!(100))).unwrap(),
684            MaybeUndefined::Value(100)
685        );
686
687        assert_eq!(
688            MaybeUndefined::<i32>::parse_from_json(Some(json!(null))).unwrap(),
689            MaybeUndefined::Null
690        );
691
692        assert_eq!(
693            MaybeUndefined::<i32>::parse_from_json(None).unwrap(),
694            MaybeUndefined::Undefined
695        );
696
697        #[derive(Debug, Object, PartialEq)]
698        #[oai(internal)]
699        struct MyObj {
700            a: MaybeUndefined<i32>,
701        }
702
703        assert_eq!(
704            MyObj::parse_from_json(Some(json!({
705                "a": 100,
706            })))
707            .unwrap(),
708            MyObj {
709                a: MaybeUndefined::Value(100)
710            }
711        );
712
713        assert_eq!(
714            MyObj::parse_from_json(Some(json!({
715                "a": null,
716            })))
717            .unwrap(),
718            MyObj {
719                a: MaybeUndefined::Null
720            }
721        );
722
723        assert_eq!(
724            MyObj::parse_from_json(Some(json!({}))).unwrap(),
725            MyObj {
726                a: MaybeUndefined::Undefined
727            }
728        );
729    }
730
731    #[test]
732    fn test_to_json() {
733        assert_eq!(
734            MaybeUndefined::<i32>::Value(100).to_json(),
735            Some(json!(100))
736        );
737        assert_eq!(MaybeUndefined::<i32>::Null.to_json(), Some(json!(null)));
738        assert_eq!(MaybeUndefined::<i32>::Undefined.to_json(), None);
739
740        #[derive(Debug, Object, PartialEq)]
741        #[oai(internal)]
742        struct MyObj {
743            a: MaybeUndefined<i32>,
744        }
745
746        assert_eq!(
747            MyObj {
748                a: MaybeUndefined::Value(100)
749            }
750            .to_json(),
751            Some(json!({
752                "a": 100,
753            }))
754        );
755
756        assert_eq!(
757            MyObj {
758                a: MaybeUndefined::Null
759            }
760            .to_json(),
761            Some(json!({
762                "a": null,
763            }))
764        );
765
766        assert_eq!(
767            MyObj {
768                a: MaybeUndefined::Undefined
769            }
770            .to_json(),
771            Some(json!({}))
772        );
773    }
774}