Skip to main content

agent_client_protocol_schema/
serde_util.rs

1//! Custom option-like field wrappers and builder helpers for serde.
2//!
3//! ## Types
4//!
5//! - [`MaybeUndefined<T>`] — three-state: undefined (key absent), null, or value.
6//! - [`SkipListener`] — [`serde_with::InspectError`] hook used by every
7//!   `VecSkipError` call site in the protocol types.
8//!
9//! ## Builder traits
10//!
11//! - [`IntoOption<T>`] — ergonomic conversion into `Option<T>` for builder methods.
12//! - [`IntoMaybeUndefined<T>`] — ergonomic conversion into `MaybeUndefined<T>` for builder methods.
13//!
14//! `MaybeUndefined` based on: <https://docs.rs/async-graphql/latest/src/async_graphql/types/maybe_undefined.rs.html>
15use std::{
16    borrow::Cow,
17    ffi::OsStr,
18    ops::Deref,
19    path::{Path, PathBuf},
20    sync::Arc,
21};
22
23use schemars::JsonSchema;
24use serde::{Deserialize, Deserializer, Serialize, Serializer};
25use serde_with::{DeserializeAs, de::DeserializeAsWrap};
26
27// ---- SkipListener ----
28
29/// Inspector passed to every `VecSkipError<_, SkipListener>` in the protocol
30/// types so that malformed list entries dropped during deserialization are
31/// surfaced to observability tooling rather than vanishing silently.
32///
33/// - With the `tracing` feature enabled, this is a zero-sized type whose
34///   [`InspectError`](serde_with::InspectError) implementation emits a
35///   [`tracing::warn!`] event on every skipped entry.
36/// - With the feature disabled (the default), it resolves to `()` — which
37///   `serde_with` ships with a no-op `InspectError` implementation — so call
38///   sites incur zero runtime cost.
39#[cfg(feature = "tracing")]
40#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
41#[non_exhaustive]
42pub(crate) struct SkipListener;
43
44#[cfg(feature = "tracing")]
45impl serde_with::InspectError for SkipListener {
46    fn inspect_error(error: impl serde::de::Error) {
47        tracing::warn!(
48            %error,
49            "skipped malformed list entry during deserialization",
50        );
51    }
52}
53
54/// Zero-cost stand-in for [`SkipListener`] when the `tracing` feature is
55/// disabled. Resolves to `()`, which `serde_with` already ships with a no-op
56/// `InspectError` implementation.
57#[cfg(not(feature = "tracing"))]
58pub(crate) type SkipListener = ();
59
60#[cfg(test)]
61mod skip_listener_tests {
62    use std::cell::Cell;
63
64    use serde::{Deserialize, Serialize};
65    use serde_json::json;
66    use serde_with::{DefaultOnError, VecSkipError, serde_as};
67
68    thread_local! {
69        static SKIP_COUNT: Cell<u32> = const { Cell::new(0) };
70    }
71
72    /// Test-only inspector that counts skipped entries.
73    struct CountingListener;
74
75    impl serde_with::InspectError for CountingListener {
76        fn inspect_error(_error: impl serde::de::Error) {
77            SKIP_COUNT.with(|c| c.set(c.get() + 1));
78        }
79    }
80
81    #[serde_as]
82    #[derive(Serialize, Deserialize, Debug, PartialEq)]
83    struct Wrapper {
84        #[serde_as(deserialize_as = "VecSkipError<_, CountingListener>")]
85        values: Vec<u32>,
86    }
87
88    #[test]
89    fn inspector_runs_for_each_skipped_entry() {
90        SKIP_COUNT.with(|c| c.set(0));
91
92        let input = json!({"values": [1, "oops", 2, {}, 3]});
93        let wrapper: Wrapper = serde_json::from_value(input).unwrap();
94
95        assert_eq!(wrapper.values, vec![1, 2, 3]);
96        assert_eq!(SKIP_COUNT.with(Cell::get), 2);
97    }
98
99    /// Mirrors the pattern applied to every required `Vec<T>` field in the
100    /// protocol: `DefaultOnError<VecSkipError<_, ...>>` + `#[serde(default)]`.
101    /// Element-level failures are skipped; any outer shape error (`null`, a
102    /// string, a map, etc.) collapses to `Default::default()` (i.e. `vec![]`).
103    #[serde_as]
104    #[derive(Deserialize, Debug, PartialEq)]
105    struct ResilientVec {
106        #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, CountingListener>>")]
107        #[serde(default)]
108        values: Vec<u32>,
109    }
110
111    #[test]
112    fn resilient_vec_tolerates_missing_null_and_wrong_type() {
113        // Missing field -> `#[serde(default)]` supplies `vec![]`.
114        let r: ResilientVec = serde_json::from_value(json!({})).unwrap();
115        assert_eq!(r.values, Vec::<u32>::new());
116
117        // Explicit null -> `DefaultOnError` swallows the type error.
118        let r: ResilientVec = serde_json::from_value(json!({"values": null})).unwrap();
119        assert_eq!(r.values, Vec::<u32>::new());
120
121        // Wrong outer type (string) -> `DefaultOnError` swallows.
122        let r: ResilientVec = serde_json::from_value(json!({"values": "oops"})).unwrap();
123        assert_eq!(r.values, Vec::<u32>::new());
124
125        // Wrong outer type (object) -> `DefaultOnError` swallows.
126        let r: ResilientVec = serde_json::from_value(json!({"values": {"k": 1}})).unwrap();
127        assert_eq!(r.values, Vec::<u32>::new());
128
129        // Valid array with element errors -> `VecSkipError` skips per-element.
130        SKIP_COUNT.with(|c| c.set(0));
131        let r: ResilientVec =
132            serde_json::from_value(json!({"values": [1, "oops", 2, {}, 3]})).unwrap();
133        assert_eq!(r.values, vec![1, 2, 3]);
134        assert_eq!(SKIP_COUNT.with(Cell::get), 2);
135    }
136
137    #[test]
138    fn resilient_vec_does_not_invoke_inspector_on_outer_failure() {
139        SKIP_COUNT.with(|c| c.set(0));
140
141        // Outer failures are swallowed silently by `DefaultOnError`; the
142        // inspector only sees per-element failures inside a valid array.
143        let _r: ResilientVec = serde_json::from_value(json!({"values": null})).unwrap();
144        let _r: ResilientVec = serde_json::from_value(json!({"values": "oops"})).unwrap();
145        let _r: ResilientVec = serde_json::from_value(json!({"values": {}})).unwrap();
146
147        assert_eq!(SKIP_COUNT.with(Cell::get), 0);
148    }
149
150    /// Mirrors the pattern applied to every optional `Option<Vec<T>>` field:
151    /// `DefaultOnError<Option<VecSkipError<_, ...>>>` + `#[serde(default)]`.
152    /// `null` becomes `None`; outer shape errors also collapse to `None`;
153    /// element-level failures are skipped inside the array.
154    #[serde_as]
155    #[derive(Deserialize, Debug, PartialEq)]
156    struct ResilientOptionVec {
157        #[serde_as(deserialize_as = "DefaultOnError<Option<VecSkipError<_, CountingListener>>>")]
158        #[serde(default)]
159        values: Option<Vec<u32>>,
160    }
161
162    #[test]
163    fn resilient_option_vec_tolerates_missing_null_and_wrong_type() {
164        // Missing field -> `None`.
165        let r: ResilientOptionVec = serde_json::from_value(json!({})).unwrap();
166        assert_eq!(r.values, None);
167
168        // Explicit null -> `None`.
169        let r: ResilientOptionVec = serde_json::from_value(json!({"values": null})).unwrap();
170        assert_eq!(r.values, None);
171
172        // Empty array -> `Some(vec![])`.
173        let r: ResilientOptionVec = serde_json::from_value(json!({"values": []})).unwrap();
174        assert_eq!(r.values, Some(Vec::<u32>::new()));
175
176        // Valid array -> `Some(vec)`.
177        let r: ResilientOptionVec = serde_json::from_value(json!({"values": [1, 2, 3]})).unwrap();
178        assert_eq!(r.values, Some(vec![1, 2, 3]));
179
180        // Wrong outer type (string) -> `DefaultOnError` collapses to `None`.
181        let r: ResilientOptionVec = serde_json::from_value(json!({"values": "oops"})).unwrap();
182        assert_eq!(r.values, None);
183
184        // Wrong outer type (object) -> `DefaultOnError` collapses to `None`.
185        let r: ResilientOptionVec = serde_json::from_value(json!({"values": {"k": 1}})).unwrap();
186        assert_eq!(r.values, None);
187
188        // Valid array with element errors -> `VecSkipError` skips per-element.
189        SKIP_COUNT.with(|c| c.set(0));
190        let r: ResilientOptionVec =
191            serde_json::from_value(json!({"values": [1, "oops", 2, {}, 3]})).unwrap();
192        assert_eq!(r.values, Some(vec![1, 2, 3]));
193        assert_eq!(SKIP_COUNT.with(Cell::get), 2);
194    }
195}
196
197// ---- IntoOption ----
198
199/// Utility trait for builder methods for optional values.
200/// This allows the caller to either pass in the value itself without wrapping it in `Some`,
201/// or to just pass in an Option if that is what they have.
202pub trait IntoOption<T> {
203    /// Converts this value into an optional builder argument.
204    fn into_option(self) -> Option<T>;
205}
206
207impl<T> IntoOption<T> for Option<T> {
208    fn into_option(self) -> Option<T> {
209        self
210    }
211}
212
213impl<T> IntoOption<T> for T {
214    fn into_option(self) -> Option<T> {
215        Some(self)
216    }
217}
218
219impl IntoOption<String> for &str {
220    fn into_option(self) -> Option<String> {
221        Some(self.into())
222    }
223}
224
225impl IntoOption<String> for &mut str {
226    fn into_option(self) -> Option<String> {
227        Some(self.into())
228    }
229}
230
231impl IntoOption<String> for &String {
232    fn into_option(self) -> Option<String> {
233        Some(self.into())
234    }
235}
236
237impl IntoOption<String> for Box<str> {
238    fn into_option(self) -> Option<String> {
239        Some(self.into())
240    }
241}
242
243impl IntoOption<String> for Cow<'_, str> {
244    fn into_option(self) -> Option<String> {
245        Some(self.into())
246    }
247}
248
249impl IntoOption<String> for Arc<str> {
250    fn into_option(self) -> Option<String> {
251        Some(self.to_string())
252    }
253}
254
255impl<T: ?Sized + AsRef<OsStr>> IntoOption<PathBuf> for &T {
256    fn into_option(self) -> Option<PathBuf> {
257        Some(self.into())
258    }
259}
260
261impl IntoOption<PathBuf> for Box<Path> {
262    fn into_option(self) -> Option<PathBuf> {
263        Some(self.into())
264    }
265}
266
267impl IntoOption<PathBuf> for Cow<'_, Path> {
268    fn into_option(self) -> Option<PathBuf> {
269        Some(self.into())
270    }
271}
272
273impl IntoOption<serde_json::Value> for &str {
274    fn into_option(self) -> Option<serde_json::Value> {
275        Some(self.into())
276    }
277}
278
279impl IntoOption<serde_json::Value> for String {
280    fn into_option(self) -> Option<serde_json::Value> {
281        Some(self.into())
282    }
283}
284
285impl IntoOption<serde_json::Value> for Cow<'_, str> {
286    fn into_option(self) -> Option<serde_json::Value> {
287        Some(self.into())
288    }
289}
290
291// ---- MaybeUndefined ----
292
293/// Similar to `Option`, but it has three states, `undefined`, `null` and `x`.
294///
295/// When using with Serde, you will likely want to skip serialization of `undefined`
296/// and add a `default` for deserialization.
297///
298/// # Example
299///
300/// ```rust
301/// use agent_client_protocol_schema::MaybeUndefined;
302/// use serde::{Serialize, Deserialize};
303///
304/// #[derive(Serialize, Deserialize, Eq, PartialEq, Debug)]
305/// struct A {
306///     #[serde(default, skip_serializing_if = "MaybeUndefined::is_undefined")]
307///     a: MaybeUndefined<i32>,
308/// }
309/// ```
310#[derive(Copy, Clone, Default, PartialEq, PartialOrd, Eq, Ord, Debug, Hash, JsonSchema)]
311#[schemars(with = "Option<Option<T>>", inline)]
312#[expect(clippy::exhaustive_enums)]
313pub enum MaybeUndefined<T> {
314    /// The field was not present.
315    #[default]
316    Undefined,
317    /// The field was present with a JSON `null` value.
318    Null,
319    /// The field was present with a non-null value.
320    Value(T),
321}
322
323impl<T> MaybeUndefined<T> {
324    /// Returns true if the `MaybeUndefined<T>` is undefined.
325    #[inline]
326    pub const fn is_undefined(&self) -> bool {
327        matches!(self, MaybeUndefined::Undefined)
328    }
329
330    /// Returns true if the `MaybeUndefined<T>` is null.
331    #[inline]
332    pub const fn is_null(&self) -> bool {
333        matches!(self, MaybeUndefined::Null)
334    }
335
336    /// Returns true if the `MaybeUndefined<T>` contains value.
337    #[inline]
338    pub const fn is_value(&self) -> bool {
339        matches!(self, MaybeUndefined::Value(_))
340    }
341
342    /// Borrow the value, returns `None` if the `MaybeUndefined<T>` is
343    /// `undefined` or `null`, otherwise returns `Some(T)`.
344    #[inline]
345    pub const fn value(&self) -> Option<&T> {
346        match self {
347            MaybeUndefined::Value(value) => Some(value),
348            _ => None,
349        }
350    }
351
352    /// Converts the `MaybeUndefined<T>` to `Option<T>`.
353    #[inline]
354    pub fn take(self) -> Option<T> {
355        match self {
356            MaybeUndefined::Value(value) => Some(value),
357            _ => None,
358        }
359    }
360
361    /// Converts the `MaybeUndefined<T>` to `Option<Option<T>>`.
362    #[inline]
363    pub const fn as_opt_ref(&self) -> Option<Option<&T>> {
364        match self {
365            MaybeUndefined::Undefined => None,
366            MaybeUndefined::Null => Some(None),
367            MaybeUndefined::Value(value) => Some(Some(value)),
368        }
369    }
370
371    /// Converts the `MaybeUndefined<T>` to `Option<Option<&U>>`.
372    #[inline]
373    pub fn as_opt_deref<U>(&self) -> Option<Option<&U>>
374    where
375        U: ?Sized,
376        T: Deref<Target = U>,
377    {
378        match self {
379            MaybeUndefined::Undefined => None,
380            MaybeUndefined::Null => Some(None),
381            MaybeUndefined::Value(value) => Some(Some(&**value)),
382        }
383    }
384
385    /// Returns `true` if the `MaybeUndefined<T>` contains the given value.
386    #[inline]
387    pub fn contains_value<U>(&self, x: &U) -> bool
388    where
389        U: PartialEq<T>,
390    {
391        match self {
392            MaybeUndefined::Value(y) => x == y,
393            _ => false,
394        }
395    }
396
397    /// Returns `true` if the `MaybeUndefined<T>` contains the given nullable
398    /// value.
399    #[inline]
400    pub fn contains<U>(&self, x: Option<&U>) -> bool
401    where
402        U: PartialEq<T>,
403    {
404        match self {
405            MaybeUndefined::Value(y) => matches!(x, Some(v) if v == y),
406            MaybeUndefined::Null => x.is_none(),
407            MaybeUndefined::Undefined => false,
408        }
409    }
410
411    /// Maps a `MaybeUndefined<T>` to `MaybeUndefined<U>` by applying a function
412    /// to the contained nullable value
413    #[inline]
414    pub fn map<U, F: FnOnce(Option<T>) -> Option<U>>(self, f: F) -> MaybeUndefined<U> {
415        match self {
416            MaybeUndefined::Value(v) => match f(Some(v)) {
417                Some(v) => MaybeUndefined::Value(v),
418                None => MaybeUndefined::Null,
419            },
420            MaybeUndefined::Null => match f(None) {
421                Some(v) => MaybeUndefined::Value(v),
422                None => MaybeUndefined::Null,
423            },
424            MaybeUndefined::Undefined => MaybeUndefined::Undefined,
425        }
426    }
427
428    /// Maps a `MaybeUndefined<T>` to `MaybeUndefined<U>` by applying a function
429    /// to the contained value
430    #[inline]
431    pub fn map_value<U, F: FnOnce(T) -> U>(self, f: F) -> MaybeUndefined<U> {
432        match self {
433            MaybeUndefined::Value(v) => MaybeUndefined::Value(f(v)),
434            MaybeUndefined::Null => MaybeUndefined::Null,
435            MaybeUndefined::Undefined => MaybeUndefined::Undefined,
436        }
437    }
438
439    /// Update `value` if the `MaybeUndefined<T>` is not undefined.
440    ///
441    /// # Example
442    ///
443    /// ```rust
444    /// use agent_client_protocol_schema::MaybeUndefined;
445    ///
446    /// let mut value = None;
447    ///
448    /// MaybeUndefined::Value(10i32).update_to(&mut value);
449    /// assert_eq!(value, Some(10));
450    ///
451    /// MaybeUndefined::Undefined.update_to(&mut value);
452    /// assert_eq!(value, Some(10));
453    ///
454    /// MaybeUndefined::Null.update_to(&mut value);
455    /// assert_eq!(value, None);
456    /// ```
457    pub fn update_to(self, value: &mut Option<T>) {
458        match self {
459            MaybeUndefined::Value(new) => *value = Some(new),
460            MaybeUndefined::Null => *value = None,
461            MaybeUndefined::Undefined => {}
462        }
463    }
464}
465
466impl<T, E> MaybeUndefined<Result<T, E>> {
467    /// Transposes a `MaybeUndefined` of a [`Result`] into a [`Result`] of a
468    /// `MaybeUndefined`.
469    ///
470    /// [`MaybeUndefined::Undefined`] will be mapped to
471    /// [`Ok`]`(`[`MaybeUndefined::Undefined`]`)`. [`MaybeUndefined::Null`]
472    /// will be mapped to [`Ok`]`(`[`MaybeUndefined::Null`]`)`.
473    /// [`MaybeUndefined::Value`]`(`[`Ok`]`(_))` and
474    /// [`MaybeUndefined::Value`]`(`[`Err`]`(_))` will be mapped to
475    /// [`Ok`]`(`[`MaybeUndefined::Value`]`(_))` and [`Err`]`(_)`.
476    ///
477    /// # Errors
478    ///
479    /// Returns an error if the input is [`MaybeUndefined::Value`]`(`[`Err`]`(_))`.
480    #[inline]
481    pub fn transpose(self) -> Result<MaybeUndefined<T>, E> {
482        match self {
483            MaybeUndefined::Undefined => Ok(MaybeUndefined::Undefined),
484            MaybeUndefined::Null => Ok(MaybeUndefined::Null),
485            MaybeUndefined::Value(Ok(v)) => Ok(MaybeUndefined::Value(v)),
486            MaybeUndefined::Value(Err(e)) => Err(e),
487        }
488    }
489}
490
491impl<T: Serialize> Serialize for MaybeUndefined<T> {
492    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
493        match self {
494            MaybeUndefined::Value(value) => value.serialize(serializer),
495            MaybeUndefined::Null => serializer.serialize_none(),
496            MaybeUndefined::Undefined => serializer.serialize_unit(),
497        }
498    }
499}
500
501impl<'de, T> Deserialize<'de> for MaybeUndefined<T>
502where
503    T: Deserialize<'de>,
504{
505    fn deserialize<D>(deserializer: D) -> Result<MaybeUndefined<T>, D::Error>
506    where
507        D: Deserializer<'de>,
508    {
509        Option::<T>::deserialize(deserializer).map(|value| match value {
510            Some(value) => MaybeUndefined::Value(value),
511            None => MaybeUndefined::Null,
512        })
513    }
514}
515
516impl<T> From<MaybeUndefined<T>> for Option<Option<T>> {
517    fn from(maybe_undefined: MaybeUndefined<T>) -> Self {
518        match maybe_undefined {
519            MaybeUndefined::Undefined => None,
520            MaybeUndefined::Null => Some(None),
521            MaybeUndefined::Value(value) => Some(Some(value)),
522        }
523    }
524}
525
526impl<T> From<Option<Option<T>>> for MaybeUndefined<T> {
527    fn from(value: Option<Option<T>>) -> Self {
528        match value {
529            Some(Some(value)) => Self::Value(value),
530            Some(None) => Self::Null,
531            None => Self::Undefined,
532        }
533    }
534}
535
536impl<'de, T, TAs> DeserializeAs<'de, MaybeUndefined<T>> for MaybeUndefined<TAs>
537where
538    TAs: DeserializeAs<'de, T>,
539{
540    fn deserialize_as<D>(deserializer: D) -> Result<MaybeUndefined<T>, D::Error>
541    where
542        D: Deserializer<'de>,
543    {
544        Option::<DeserializeAsWrap<T, TAs>>::deserialize(deserializer).map(|value| match value {
545            Some(value) => MaybeUndefined::Value(value.into_inner()),
546            None => MaybeUndefined::Null,
547        })
548    }
549}
550
551/// Utility trait for builder methods for optional values.
552/// This allows the caller to either pass in the value itself without wrapping it in `Some`,
553/// or to just pass in an Option if that is what they have, or set it back to undefined.
554pub trait IntoMaybeUndefined<T> {
555    /// Converts this value into a three-state builder argument.
556    fn into_maybe_undefined(self) -> MaybeUndefined<T>;
557}
558
559impl<T> IntoMaybeUndefined<T> for T {
560    fn into_maybe_undefined(self) -> MaybeUndefined<T> {
561        MaybeUndefined::Value(self)
562    }
563}
564
565impl<T> IntoMaybeUndefined<T> for Option<T> {
566    fn into_maybe_undefined(self) -> MaybeUndefined<T> {
567        match self {
568            Some(value) => MaybeUndefined::Value(value),
569            None => MaybeUndefined::Null,
570        }
571    }
572}
573
574impl<T> IntoMaybeUndefined<T> for MaybeUndefined<T> {
575    fn into_maybe_undefined(self) -> MaybeUndefined<T> {
576        self
577    }
578}
579
580impl IntoMaybeUndefined<String> for &str {
581    fn into_maybe_undefined(self) -> MaybeUndefined<String> {
582        MaybeUndefined::Value(self.into())
583    }
584}
585
586impl IntoMaybeUndefined<String> for &mut str {
587    fn into_maybe_undefined(self) -> MaybeUndefined<String> {
588        MaybeUndefined::Value(self.into())
589    }
590}
591
592impl IntoMaybeUndefined<String> for &String {
593    fn into_maybe_undefined(self) -> MaybeUndefined<String> {
594        MaybeUndefined::Value(self.into())
595    }
596}
597
598impl IntoMaybeUndefined<String> for Box<str> {
599    fn into_maybe_undefined(self) -> MaybeUndefined<String> {
600        MaybeUndefined::Value(self.into())
601    }
602}
603
604impl IntoMaybeUndefined<String> for Cow<'_, str> {
605    fn into_maybe_undefined(self) -> MaybeUndefined<String> {
606        MaybeUndefined::Value(self.into())
607    }
608}
609
610impl IntoMaybeUndefined<String> for Arc<str> {
611    fn into_maybe_undefined(self) -> MaybeUndefined<String> {
612        MaybeUndefined::Value(self.to_string())
613    }
614}
615
616impl<T: ?Sized + AsRef<OsStr>> IntoMaybeUndefined<PathBuf> for &T {
617    fn into_maybe_undefined(self) -> MaybeUndefined<PathBuf> {
618        MaybeUndefined::Value(self.into())
619    }
620}
621
622impl IntoMaybeUndefined<PathBuf> for Box<Path> {
623    fn into_maybe_undefined(self) -> MaybeUndefined<PathBuf> {
624        MaybeUndefined::Value(self.into())
625    }
626}
627
628impl IntoMaybeUndefined<PathBuf> for Cow<'_, Path> {
629    fn into_maybe_undefined(self) -> MaybeUndefined<PathBuf> {
630        MaybeUndefined::Value(self.into())
631    }
632}
633
634impl IntoMaybeUndefined<serde_json::Value> for &str {
635    fn into_maybe_undefined(self) -> MaybeUndefined<serde_json::Value> {
636        MaybeUndefined::Value(self.into())
637    }
638}
639
640impl IntoMaybeUndefined<serde_json::Value> for String {
641    fn into_maybe_undefined(self) -> MaybeUndefined<serde_json::Value> {
642        MaybeUndefined::Value(self.into())
643    }
644}
645
646impl IntoMaybeUndefined<serde_json::Value> for Cow<'_, str> {
647    fn into_maybe_undefined(self) -> MaybeUndefined<serde_json::Value> {
648        MaybeUndefined::Value(self.into())
649    }
650}
651
652#[cfg(test)]
653mod tests {
654    use serde::{Deserialize, Serialize};
655    use serde_json::{from_value, json, to_value};
656
657    use super::*;
658
659    #[test]
660    fn test_maybe_undefined_serde() {
661        #[derive(Serialize, Deserialize, Eq, PartialEq, Debug)]
662        struct A {
663            #[serde(default, skip_serializing_if = "MaybeUndefined::is_undefined")]
664            a: MaybeUndefined<i32>,
665        }
666
667        assert_eq!(to_value(MaybeUndefined::Value(100i32)).unwrap(), json!(100));
668
669        assert_eq!(
670            from_value::<MaybeUndefined<i32>>(json!(100)).unwrap(),
671            MaybeUndefined::Value(100)
672        );
673        assert_eq!(
674            from_value::<MaybeUndefined<i32>>(json!(null)).unwrap(),
675            MaybeUndefined::Null
676        );
677
678        assert_eq!(
679            to_value(&A {
680                a: MaybeUndefined::Value(100i32)
681            })
682            .unwrap(),
683            json!({"a": 100})
684        );
685
686        assert_eq!(
687            to_value(&A {
688                a: MaybeUndefined::Null,
689            })
690            .unwrap(),
691            json!({ "a": null })
692        );
693
694        assert_eq!(
695            to_value(&A {
696                a: MaybeUndefined::Undefined,
697            })
698            .unwrap(),
699            json!({})
700        );
701
702        assert_eq!(
703            from_value::<A>(json!({"a": 100})).unwrap(),
704            A {
705                a: MaybeUndefined::Value(100i32)
706            }
707        );
708
709        assert_eq!(
710            from_value::<A>(json!({ "a": null })).unwrap(),
711            A {
712                a: MaybeUndefined::Null
713            }
714        );
715
716        assert_eq!(
717            from_value::<A>(json!({})).unwrap(),
718            A {
719                a: MaybeUndefined::Undefined
720            }
721        );
722    }
723
724    #[test]
725    fn test_maybe_undefined_to_nested_option() {
726        assert_eq!(Option::<Option<i32>>::from(MaybeUndefined::Undefined), None);
727
728        assert_eq!(
729            Option::<Option<i32>>::from(MaybeUndefined::Null),
730            Some(None)
731        );
732
733        assert_eq!(
734            Option::<Option<i32>>::from(MaybeUndefined::Value(42)),
735            Some(Some(42))
736        );
737    }
738
739    #[test]
740    fn test_as_opt_ref() {
741        let value = MaybeUndefined::<String>::Undefined;
742        let r = value.as_opt_ref();
743        assert_eq!(r, None);
744
745        let value = MaybeUndefined::<String>::Null;
746        let r = value.as_opt_ref();
747        assert_eq!(r, Some(None));
748
749        let value = MaybeUndefined::<String>::Value("abc".to_string());
750        let r = value.as_opt_ref();
751        assert_eq!(r, Some(Some(&"abc".to_string())));
752    }
753
754    #[test]
755    fn test_as_opt_deref() {
756        let value = MaybeUndefined::<String>::Undefined;
757        let r = value.as_opt_deref();
758        assert_eq!(r, None);
759
760        let value = MaybeUndefined::<String>::Null;
761        let r = value.as_opt_deref();
762        assert_eq!(r, Some(None));
763
764        let value = MaybeUndefined::<String>::Value("abc".to_string());
765        let r = value.as_opt_deref();
766        assert_eq!(r, Some(Some("abc")));
767    }
768
769    #[test]
770    fn test_contains_value() {
771        let test = "abc";
772
773        let mut value: MaybeUndefined<String> = MaybeUndefined::Undefined;
774        assert!(!value.contains_value(&test));
775
776        value = MaybeUndefined::Null;
777        assert!(!value.contains_value(&test));
778
779        value = MaybeUndefined::Value("abc".to_string());
780        assert!(value.contains_value(&test));
781    }
782
783    #[test]
784    fn test_contains() {
785        let test = Some("abc");
786        let none: Option<&str> = None;
787
788        let mut value: MaybeUndefined<String> = MaybeUndefined::Undefined;
789        assert!(!value.contains(test.as_ref()));
790        assert!(!value.contains(none.as_ref()));
791
792        value = MaybeUndefined::Null;
793        assert!(!value.contains(test.as_ref()));
794        assert!(value.contains(none.as_ref()));
795
796        value = MaybeUndefined::Value("abc".to_string());
797        assert!(value.contains(test.as_ref()));
798        assert!(!value.contains(none.as_ref()));
799    }
800
801    #[test]
802    fn test_map_value() {
803        let mut value: MaybeUndefined<i32> = MaybeUndefined::Undefined;
804        assert_eq!(value.map_value(|v| v > 2), MaybeUndefined::Undefined);
805
806        value = MaybeUndefined::Null;
807        assert_eq!(value.map_value(|v| v > 2), MaybeUndefined::Null);
808
809        value = MaybeUndefined::Value(5);
810        assert_eq!(value.map_value(|v| v > 2), MaybeUndefined::Value(true));
811    }
812
813    #[test]
814    fn test_map() {
815        let mut value: MaybeUndefined<i32> = MaybeUndefined::Undefined;
816        assert_eq!(value.map(|v| Some(v.is_some())), MaybeUndefined::Undefined);
817
818        value = MaybeUndefined::Null;
819        assert_eq!(
820            value.map(|v| Some(v.is_some())),
821            MaybeUndefined::Value(false)
822        );
823
824        value = MaybeUndefined::Value(5);
825        assert_eq!(
826            value.map(|v| Some(v.is_some())),
827            MaybeUndefined::Value(true)
828        );
829    }
830
831    #[test]
832    fn test_transpose() {
833        let mut value: MaybeUndefined<Result<i32, &'static str>> = MaybeUndefined::Undefined;
834        assert_eq!(value.transpose(), Ok(MaybeUndefined::Undefined));
835
836        value = MaybeUndefined::Null;
837        assert_eq!(value.transpose(), Ok(MaybeUndefined::Null));
838
839        value = MaybeUndefined::Value(Ok(5));
840        assert_eq!(value.transpose(), Ok(MaybeUndefined::Value(5)));
841
842        value = MaybeUndefined::Value(Err("error"));
843        assert_eq!(value.transpose(), Err("error"));
844    }
845}