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