Skip to main content

agent_client_protocol_schema/
serde_util.rs

1//! Custom payload adapters, option-like field wrappers, and builder helpers for serde.
2//!
3//! ## Payload adapters
4//!
5//! - [`default_on_null`]: opt a defaultable payload into accepting null.
6//!
7//! ## Types
8//!
9//! - [`MaybeUndefined<T>`]: three-state: undefined (key absent), null, or value.
10//!
11//! ## Builder traits
12//!
13//! - [`IntoOption<T>`]: ergonomic conversion into `Option<T>` for builder methods.
14//! - [`IntoMaybeUndefined<T>`]: ergonomic conversion into `MaybeUndefined<T>` for builder methods.
15//!
16//! `MaybeUndefined` based on: <https://docs.rs/async-graphql/latest/src/async_graphql/types/maybe_undefined.rs.html>
17use std::{
18    borrow::Cow,
19    ffi::OsStr,
20    ops::Deref,
21    path::{Path, PathBuf},
22    sync::Arc,
23};
24
25use serde::{Deserialize, Deserializer, Serialize, Serializer};
26use serde_with::{DeserializeAs, de::DeserializeAsWrap};
27
28// ---- Default-on-null payloads ----
29
30/// Declares a defaultable payload whose `Deserialize` accepts null.
31///
32/// Declare the normal derives except `Deserialize` inside this macro. A private
33/// wire type derives deserialization from the same fields and attributes, so
34/// there is no second field definition to maintain and no public helper methods.
35/// `DefaultOnNull` wraps only deserialization of the entire payload. Serialization
36/// and JSON Schema are still derived directly on the public type.
37///
38/// Opt in explicitly. Implementing `Default` alone does not change wire behavior.
39macro_rules! default_on_null {
40    // Copies one field from the wire struct, keeping only the field's `#[cfg]`
41    // attributes so feature-gated fields compile with the feature off.
42    (@assign $payload:ident $wire:ident $field:ident []) => {
43        $payload.$field = $wire.$field;
44    };
45    (@assign $payload:ident $wire:ident $field:ident [[cfg $($condition:tt)*] $($rest:tt)*]) => {
46        #[cfg $($condition)*]
47        {
48            $crate::serde_util::default_on_null!(@assign $payload $wire $field [$($rest)*]);
49        }
50    };
51    (@assign $payload:ident $wire:ident $field:ident [[$($other:tt)*] $($rest:tt)*]) => {
52        $crate::serde_util::default_on_null!(@assign $payload $wire $field [$($rest)*]);
53    };
54    (
55        $(#[$attribute:meta])*
56        $visibility:vis struct $payload:ident {
57            $(
58                $(#[$($field_attribute:tt)*])*
59                $field_visibility:vis $field:ident: $field_type:ty
60            ),* $(,)?
61        }
62    ) => {
63        $(#[$attribute])*
64        $visibility struct $payload {
65            $(
66                $(#[$($field_attribute)*])*
67                $field_visibility $field: $field_type,
68            )*
69        }
70
71        impl<'de> serde::Deserialize<'de> for $payload {
72            fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
73                $(#[$attribute])*
74                #[derive(serde::Deserialize)]
75                struct Wire {
76                    $(
77                        $(#[$($field_attribute)*])*
78                        $field_visibility $field: $field_type,
79                    )*
80                }
81
82                struct NonNull;
83
84                impl<'de> serde_with::DeserializeAs<'de, $payload> for NonNull {
85                    #[allow(clippy::field_reassign_with_default)]
86                    fn deserialize_as<D: serde::Deserializer<'de>>(
87                        deserializer: D,
88                    ) -> Result<$payload, D::Error> {
89                        let wire = <Wire as serde::Deserialize>::deserialize(deserializer)?;
90                        let mut payload = <$payload as Default>::default();
91                        $(
92                            $crate::serde_util::default_on_null!(
93                                @assign payload wire $field [$([$($field_attribute)*])*]
94                            );
95                        )*
96                        Ok(payload)
97                    }
98                }
99
100                <serde_with::DefaultOnNull<NonNull> as serde_with::DeserializeAs<
101                    'de,
102                    Self,
103                >>::deserialize_as(deserializer)
104            }
105        }
106    };
107}
108
109pub(crate) use default_on_null;
110
111#[cfg(test)]
112mod default_on_null_tests {
113    use serde::{Deserialize, Serialize, de::DeserializeOwned};
114    use serde_json::{Value, json};
115
116    use crate::{
117        MaybeUndefined,
118        rpc::{JsonRpcMessage, Request, Response},
119        v1::{
120            self, LoadSessionResponse, NewSessionResponse, PromptResponse, ReadTextFileResponse,
121            RequestPermissionResponse, WaitForTerminalExitResponse, WriteTextFileResponse,
122        },
123    };
124
125    // Keep one inventory for the value, streaming, serialization, and schema checks.
126    // Feature gates match the payloads, so the inventory also runs without default
127    // features and with individual unstable features enabled.
128    macro_rules! for_each_defaultable_payload {
129        ($check:ident) => {
130            $check::<v1::AuthenticateResponse>();
131            $check::<v1::LogoutRequest>();
132            $check::<v1::LogoutResponse>();
133            $check::<v1::LoadSessionResponse>();
134            $check::<v1::ResumeSessionResponse>();
135            $check::<v1::CloseSessionResponse>();
136            $check::<v1::ListSessionsRequest>();
137            $check::<v1::DeleteSessionResponse>();
138            $check::<v1::SetSessionModeResponse>();
139            $check::<v1::WriteTextFileResponse>();
140            $check::<v1::ReleaseTerminalResponse>();
141            $check::<v1::KillTerminalResponse>();
142            $check::<v1::WaitForTerminalExitResponse>();
143
144            #[cfg(feature = "unstable_auth_status")]
145            $check::<v1::AuthStatusRequest>();
146
147            #[cfg(feature = "unstable_session_archive")]
148            {
149                $check::<v1::ArchiveSessionResponse>();
150                $check::<v1::UnarchiveSessionResponse>();
151            }
152
153            #[cfg(feature = "unstable_llm_providers")]
154            {
155                $check::<v1::ListProvidersRequest>();
156                $check::<v1::SetProviderResponse>();
157                $check::<v1::DisableProviderResponse>();
158            }
159
160            #[cfg(feature = "unstable_protocol_v2")]
161            {
162                use crate::v2;
163
164                $check::<v2::LoginAuthResponse>();
165                $check::<v2::LogoutAuthRequest>();
166                $check::<v2::LogoutAuthResponse>();
167                $check::<v2::ResumeSessionResponse>();
168                $check::<v2::CloseSessionResponse>();
169                $check::<v2::ListSessionsRequest>();
170                $check::<v2::DeleteSessionResponse>();
171                $check::<v2::CancelToolCallResponse>();
172                $check::<v2::CancelTerminalResponse>();
173
174                #[cfg(feature = "unstable_auth_status")]
175                $check::<v2::AuthStatusRequest>();
176
177                #[cfg(feature = "unstable_session_archive")]
178                {
179                    $check::<v2::ArchiveSessionResponse>();
180                    $check::<v2::UnarchiveSessionResponse>();
181                }
182
183                #[cfg(feature = "unstable_llm_providers")]
184                {
185                    $check::<v2::ListProvidersRequest>();
186                    $check::<v2::SetProviderResponse>();
187                    $check::<v2::DisableProviderResponse>();
188                }
189            }
190        };
191    }
192
193    fn assert_defaultable_payload<T>()
194    where
195        T: Default + DeserializeOwned + Serialize + PartialEq + std::fmt::Debug,
196    {
197        let name = std::any::type_name::<T>();
198        for value in [Value::Null, json!({})] {
199            assert_eq!(
200                serde_json::from_value::<T>(value).unwrap(),
201                T::default(),
202                "{name}",
203            );
204        }
205        assert_eq!(
206            serde_json::from_str::<T>("null").unwrap(),
207            T::default(),
208            "{name}",
209        );
210        assert_eq!(
211            serde_json::to_value(T::default()).unwrap(),
212            json!({}),
213            "{name}"
214        );
215
216        let metadata = json!({"_meta": {"example.com/key": ["preserve", 1, null]}});
217        let payload: T = serde_json::from_value(metadata.clone()).unwrap();
218        assert_eq!(serde_json::to_value(payload).unwrap(), metadata, "{name}");
219
220        for value in [json!(false), json!(42), json!("invalid")] {
221            assert!(serde_json::from_value::<T>(value).is_err(), "{name}");
222        }
223
224        // Opting in the payload must not swallow null in surrounding wrappers.
225        assert_eq!(
226            serde_json::from_value::<Option<T>>(Value::Null).unwrap(),
227            None,
228            "{name}",
229        );
230        assert_eq!(
231            serde_json::from_value::<MaybeUndefined<T>>(Value::Null).unwrap(),
232            MaybeUndefined::Null,
233            "{name}",
234        );
235    }
236
237    #[test]
238    fn defaultable_payloads_accept_null_without_losing_information() {
239        for_each_defaultable_payload!(assert_defaultable_payload);
240    }
241
242    #[test]
243    fn inherent_methods_do_not_bypass_null_handling() {
244        // These must resolve to the trait too, not a strict inherent helper.
245        assert_eq!(
246            WriteTextFileResponse::deserialize(Value::Null).unwrap(),
247            WriteTextFileResponse::default(),
248        );
249        assert_eq!(
250            LoadSessionResponse::deserialize(Value::Null).unwrap(),
251            LoadSessionResponse::default(),
252        );
253    }
254
255    #[test]
256    fn optional_payload_fields_are_preserved() {
257        let load = json!({
258            "modes": {
259                "currentModeId": "ask",
260                "availableModes": [{"id": "ask", "name": "Ask"}]
261            },
262            "configOptions": [],
263            "_meta": {"example.com/key": "value"}
264        });
265        let response: LoadSessionResponse = serde_json::from_value(load.clone()).unwrap();
266        assert_eq!(serde_json::to_value(response).unwrap(), load);
267
268        let list = json!({"cwd": "/workspace", "cursor": "next-page"});
269        let request: v1::ListSessionsRequest = serde_json::from_value(list.clone()).unwrap();
270        assert_eq!(serde_json::to_value(request).unwrap(), list);
271
272        #[cfg(feature = "unstable_protocol_v2")]
273        {
274            let request: crate::v2::ListSessionsRequest =
275                serde_json::from_value(list.clone()).unwrap();
276            assert_eq!(serde_json::to_value(request).unwrap(), list);
277        }
278    }
279
280    #[test]
281    fn default_terminal_exit_status_is_unknown_not_success() {
282        let response: WaitForTerminalExitResponse = serde_json::from_value(Value::Null).unwrap();
283        assert_eq!(response.exit_status.exit_code, None);
284        assert_eq!(response.exit_status.signal, None);
285        assert_eq!(response, WaitForTerminalExitResponse::default());
286
287        for value in [
288            json!({"exitCode": 0}),
289            json!({"exitCode": 17}),
290            json!({"signal": "SIGTERM"}),
291        ] {
292            let response: WaitForTerminalExitResponse =
293                serde_json::from_value(value.clone()).unwrap();
294            assert_eq!(serde_json::to_value(response).unwrap(), value);
295        }
296    }
297
298    #[test]
299    fn non_null_payloads_keep_the_derived_deserialization_behavior() {
300        // A default exists, but the field is still required in non-null input.
301        // DefaultOnNull must not become DefaultOnError.
302        super::default_on_null! {
303            #[derive(Default, Debug, Serialize, PartialEq)]
304            struct RequiredField {
305                count: u32,
306            }
307        }
308        #[derive(Deserialize)]
309        struct BaselineWrite {
310            #[serde(
311                default,
312                rename = "_meta",
313                with = "serde_with::As::<serde_with::DefaultOnError>"
314            )]
315            meta: Option<serde_json::Map<String, Value>>,
316        }
317
318        assert_eq!(
319            serde_json::from_value::<RequiredField>(Value::Null).unwrap(),
320            RequiredField::default(),
321        );
322        assert!(serde_json::from_value::<RequiredField>(json!({})).is_err());
323        assert!(serde_json::from_value::<RequiredField>(json!({"count": "invalid"})).is_err());
324
325        for value in [
326            json!({}),
327            json!({"_meta": {"example.com/key": true}}),
328            json!({"_meta": 42}),
329            json!({"modes": "invalid", "configOptions": "invalid"}),
330            json!([]),
331            json!([null]),
332            json!(false),
333            json!(42),
334            json!("invalid"),
335        ] {
336            assert_eq!(
337                serde_json::from_value::<WriteTextFileResponse>(value.clone())
338                    .map(|response| response.meta)
339                    .map_err(|_| ()),
340                serde_json::from_value::<BaselineWrite>(value)
341                    .map(|response| response.meta)
342                    .map_err(|_| ()),
343            );
344        }
345    }
346
347    #[test]
348    fn payloads_with_required_fields_still_reject_null() {
349        assert!(serde_json::from_value::<ReadTextFileResponse>(Value::Null).is_err());
350        assert!(serde_json::from_value::<RequestPermissionResponse>(Value::Null).is_err());
351        assert!(serde_json::from_value::<NewSessionResponse>(Value::Null).is_err());
352        assert!(serde_json::from_value::<PromptResponse>(Value::Null).is_err());
353        assert!(serde_json::from_value::<v1::InitializeResponse>(Value::Null).is_err());
354        assert!(serde_json::from_value::<v1::CreateTerminalResponse>(Value::Null).is_err());
355        assert!(serde_json::from_value::<v1::TerminalOutputResponse>(Value::Null).is_err());
356        assert!(serde_json::from_value::<v1::CreateElicitationResponse>(Value::Null).is_err());
357
358        #[cfg(feature = "unstable_protocol_v2")]
359        {
360            use crate::v2;
361
362            assert!(serde_json::from_value::<v2::InitializeResponse>(Value::Null).is_err());
363            assert!(serde_json::from_value::<v2::NewSessionResponse>(Value::Null).is_err());
364            assert!(serde_json::from_value::<v2::PromptResponse>(Value::Null).is_err());
365            assert!(serde_json::from_value::<v2::RequestPermissionResponse>(Value::Null).is_err());
366            assert!(serde_json::from_value::<v2::CreateElicitationResponse>(Value::Null).is_err());
367        }
368    }
369
370    #[test]
371    fn raw_response_nulls_are_not_rewritten() {
372        let extension: v1::ExtResponse = serde_json::from_value(Value::Null).unwrap();
373        assert_eq!(serde_json::to_value(extension).unwrap(), Value::Null);
374
375        #[cfg(feature = "unstable_mcp_over_acp")]
376        {
377            let mcp: v1::MessageMcpResponse =
378                serde_json::from_value(json!({"result": null})).unwrap();
379            assert_eq!(serde_json::to_value(mcp).unwrap(), json!({"result": null}));
380        }
381
382        #[cfg(feature = "unstable_protocol_v2")]
383        {
384            let extension: crate::v2::ExtResponse = serde_json::from_value(Value::Null).unwrap();
385            assert_eq!(serde_json::to_value(extension).unwrap(), Value::Null);
386
387            #[cfg(feature = "unstable_mcp_over_acp")]
388            {
389                let mcp: crate::v2::MessageMcpResponse =
390                    serde_json::from_value(json!({"result": null})).unwrap();
391                assert_eq!(serde_json::to_value(mcp).unwrap(), json!({"result": null}));
392            }
393        }
394    }
395
396    #[test]
397    fn optional_request_parameters_keep_their_existing_meaning() {
398        type LogoutRequest = Request<v1::LogoutRequest>;
399        for value in [
400            json!({"id": 1, "method": "logout"}),
401            json!({"id": 1, "method": "logout", "params": null}),
402        ] {
403            let request: LogoutRequest = serde_json::from_value(value).unwrap();
404            assert_eq!(request.params, None);
405        }
406        let request: LogoutRequest =
407            serde_json::from_value(json!({"id": 1, "method": "logout", "params": {}})).unwrap();
408        assert_eq!(request.params, Some(v1::LogoutRequest::default()));
409    }
410
411    #[test]
412    fn nullable_fields_keep_their_existing_meaning() {
413        #[derive(Debug, Deserialize, PartialEq)]
414        struct Container {
415            optional: Option<WriteTextFileResponse>,
416            #[serde(default)]
417            patch: MaybeUndefined<WriteTextFileResponse>,
418        }
419
420        assert_eq!(
421            serde_json::from_value::<Option<WriteTextFileResponse>>(Value::Null).unwrap(),
422            None,
423        );
424        assert_eq!(
425            serde_json::from_value::<MaybeUndefined<WriteTextFileResponse>>(Value::Null).unwrap(),
426            MaybeUndefined::Null,
427        );
428        assert_eq!(
429            serde_json::from_value::<Container>(json!({})).unwrap(),
430            Container {
431                optional: None,
432                patch: MaybeUndefined::Undefined,
433            },
434        );
435        assert_eq!(
436            serde_json::from_value::<Container>(json!({"optional": null, "patch": null})).unwrap(),
437            Container {
438                optional: None,
439                patch: MaybeUndefined::Null,
440            },
441        );
442        assert_eq!(
443            serde_json::from_value::<Container>(json!({"optional": {}, "patch": {}})).unwrap(),
444            Container {
445                optional: Some(WriteTextFileResponse::default()),
446                patch: MaybeUndefined::Value(WriteTextFileResponse::default()),
447            },
448        );
449    }
450
451    #[test]
452    fn response_result_is_required_even_when_its_payload_accepts_null() {
453        type WriteResponse = Response<WriteTextFileResponse, Value>;
454        let response: WriteResponse =
455            serde_json::from_value(json!({"id": 1, "result": null})).unwrap();
456        assert_eq!(
457            response,
458            Response::new(1, Ok(WriteTextFileResponse::default())),
459        );
460        assert!(serde_json::from_value::<WriteResponse>(json!({"id": 1})).is_err());
461        assert!(
462            serde_json::from_value::<JsonRpcMessage<WriteResponse>>(
463                json!({"jsonrpc": "2.0", "id": 1})
464            )
465            .is_err()
466        );
467
468        let error = json!({"code": -32603, "message": "Internal error"});
469        let response: JsonRpcMessage<WriteResponse> = serde_json::from_value(json!({
470            "jsonrpc": "2.0",
471            "id": 1,
472            "error": error.clone()
473        }))
474        .unwrap();
475        assert_eq!(response.into_inner(), Response::new(1, Err(error)));
476    }
477
478    #[cfg(feature = "schemars")]
479    #[test]
480    fn defaultable_payload_schemas_still_require_objects() {
481        fn assert_object_schema<T: schemars::JsonSchema>() {
482            let schema = serde_json::to_value(schemars::schema_for!(T)).unwrap();
483            assert_eq!(schema["type"], "object");
484            assert!(schema.get("anyOf").is_none());
485        }
486        for_each_defaultable_payload!(assert_object_schema);
487    }
488}
489
490// ---- IntoOption ----
491
492/// Utility trait for builder methods for optional values.
493/// This allows the caller to either pass in the value itself without wrapping it in `Some`,
494/// or to just pass in an Option if that is what they have.
495pub trait IntoOption<T> {
496    /// Converts this value into an optional builder argument.
497    fn into_option(self) -> Option<T>;
498}
499
500impl<T> IntoOption<T> for Option<T> {
501    fn into_option(self) -> Option<T> {
502        self
503    }
504}
505
506impl<T> IntoOption<T> for T {
507    fn into_option(self) -> Option<T> {
508        Some(self)
509    }
510}
511
512impl IntoOption<String> for &str {
513    fn into_option(self) -> Option<String> {
514        Some(self.into())
515    }
516}
517
518impl IntoOption<String> for &mut str {
519    fn into_option(self) -> Option<String> {
520        Some(self.into())
521    }
522}
523
524impl IntoOption<String> for &String {
525    fn into_option(self) -> Option<String> {
526        Some(self.into())
527    }
528}
529
530impl IntoOption<String> for Box<str> {
531    fn into_option(self) -> Option<String> {
532        Some(self.into())
533    }
534}
535
536impl IntoOption<String> for Cow<'_, str> {
537    fn into_option(self) -> Option<String> {
538        Some(self.into())
539    }
540}
541
542impl IntoOption<String> for Arc<str> {
543    fn into_option(self) -> Option<String> {
544        Some(self.to_string())
545    }
546}
547
548impl<T: ?Sized + AsRef<OsStr>> IntoOption<PathBuf> for &T {
549    fn into_option(self) -> Option<PathBuf> {
550        Some(self.into())
551    }
552}
553
554impl IntoOption<PathBuf> for Box<Path> {
555    fn into_option(self) -> Option<PathBuf> {
556        Some(self.into())
557    }
558}
559
560impl IntoOption<PathBuf> for Cow<'_, Path> {
561    fn into_option(self) -> Option<PathBuf> {
562        Some(self.into())
563    }
564}
565
566impl IntoOption<serde_json::Value> for &str {
567    fn into_option(self) -> Option<serde_json::Value> {
568        Some(self.into())
569    }
570}
571
572impl IntoOption<serde_json::Value> for String {
573    fn into_option(self) -> Option<serde_json::Value> {
574        Some(self.into())
575    }
576}
577
578impl IntoOption<serde_json::Value> for Cow<'_, str> {
579    fn into_option(self) -> Option<serde_json::Value> {
580        Some(self.into())
581    }
582}
583
584// ---- MaybeUndefined ----
585
586/// Similar to `Option`, but it has three states, `undefined`, `null` and `x`.
587///
588/// When using with Serde, you will likely want to skip serialization of `undefined`
589/// and add a `default` for deserialization.
590///
591/// # Example
592///
593/// ```rust
594/// use agent_client_protocol_schema::MaybeUndefined;
595/// use serde::{Serialize, Deserialize};
596///
597/// #[derive(Serialize, Deserialize, Eq, PartialEq, Debug)]
598/// struct A {
599///     #[serde(default, skip_serializing_if = "MaybeUndefined::is_undefined")]
600///     a: MaybeUndefined<i32>,
601/// }
602/// ```
603#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
604#[derive(Copy, Clone, Default, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
605#[cfg_attr(feature = "schemars", schemars(with = "Option<Option<T>>", inline))]
606#[expect(clippy::exhaustive_enums)]
607pub enum MaybeUndefined<T> {
608    /// The field was not present.
609    #[default]
610    Undefined,
611    /// The field was present with a JSON `null` value.
612    Null,
613    /// The field was present with a non-null value.
614    Value(T),
615}
616
617impl<T> MaybeUndefined<T> {
618    /// Returns true if the `MaybeUndefined<T>` is undefined.
619    #[inline]
620    pub const fn is_undefined(&self) -> bool {
621        matches!(self, MaybeUndefined::Undefined)
622    }
623
624    /// Returns true if the `MaybeUndefined<T>` is null.
625    #[inline]
626    pub const fn is_null(&self) -> bool {
627        matches!(self, MaybeUndefined::Null)
628    }
629
630    /// Returns true if the `MaybeUndefined<T>` contains value.
631    #[inline]
632    pub const fn is_value(&self) -> bool {
633        matches!(self, MaybeUndefined::Value(_))
634    }
635
636    /// Borrow the value, returns `None` if the `MaybeUndefined<T>` is
637    /// `undefined` or `null`, otherwise returns `Some(T)`.
638    #[inline]
639    pub const fn value(&self) -> Option<&T> {
640        match self {
641            MaybeUndefined::Value(value) => Some(value),
642            _ => None,
643        }
644    }
645
646    /// Converts the `MaybeUndefined<T>` to `Option<T>`.
647    #[inline]
648    pub fn take(self) -> Option<T> {
649        match self {
650            MaybeUndefined::Value(value) => Some(value),
651            _ => None,
652        }
653    }
654
655    /// Converts the `MaybeUndefined<T>` to `Option<Option<T>>`.
656    #[inline]
657    pub const fn as_opt_ref(&self) -> Option<Option<&T>> {
658        match self {
659            MaybeUndefined::Undefined => None,
660            MaybeUndefined::Null => Some(None),
661            MaybeUndefined::Value(value) => Some(Some(value)),
662        }
663    }
664
665    /// Converts the `MaybeUndefined<T>` to `Option<Option<&U>>`.
666    #[inline]
667    pub fn as_opt_deref<U>(&self) -> Option<Option<&U>>
668    where
669        U: ?Sized,
670        T: Deref<Target = U>,
671    {
672        match self {
673            MaybeUndefined::Undefined => None,
674            MaybeUndefined::Null => Some(None),
675            MaybeUndefined::Value(value) => Some(Some(&**value)),
676        }
677    }
678
679    /// Returns `true` if the `MaybeUndefined<T>` contains the given value.
680    #[inline]
681    pub fn contains_value<U>(&self, x: &U) -> bool
682    where
683        U: PartialEq<T>,
684    {
685        match self {
686            MaybeUndefined::Value(y) => x == y,
687            _ => false,
688        }
689    }
690
691    /// Returns `true` if the `MaybeUndefined<T>` contains the given nullable
692    /// value.
693    #[inline]
694    pub fn contains<U>(&self, x: Option<&U>) -> bool
695    where
696        U: PartialEq<T>,
697    {
698        match self {
699            MaybeUndefined::Value(y) => matches!(x, Some(v) if v == y),
700            MaybeUndefined::Null => x.is_none(),
701            MaybeUndefined::Undefined => false,
702        }
703    }
704
705    /// Maps a `MaybeUndefined<T>` to `MaybeUndefined<U>` by applying a function
706    /// to the contained nullable value
707    #[inline]
708    pub fn map<U, F: FnOnce(Option<T>) -> Option<U>>(self, f: F) -> MaybeUndefined<U> {
709        match self {
710            MaybeUndefined::Value(v) => match f(Some(v)) {
711                Some(v) => MaybeUndefined::Value(v),
712                None => MaybeUndefined::Null,
713            },
714            MaybeUndefined::Null => match f(None) {
715                Some(v) => MaybeUndefined::Value(v),
716                None => MaybeUndefined::Null,
717            },
718            MaybeUndefined::Undefined => MaybeUndefined::Undefined,
719        }
720    }
721
722    /// Maps a `MaybeUndefined<T>` to `MaybeUndefined<U>` by applying a function
723    /// to the contained value
724    #[inline]
725    pub fn map_value<U, F: FnOnce(T) -> U>(self, f: F) -> MaybeUndefined<U> {
726        match self {
727            MaybeUndefined::Value(v) => MaybeUndefined::Value(f(v)),
728            MaybeUndefined::Null => MaybeUndefined::Null,
729            MaybeUndefined::Undefined => MaybeUndefined::Undefined,
730        }
731    }
732
733    /// Update `value` if the `MaybeUndefined<T>` is not undefined.
734    ///
735    /// # Example
736    ///
737    /// ```rust
738    /// use agent_client_protocol_schema::MaybeUndefined;
739    ///
740    /// let mut value = None;
741    ///
742    /// MaybeUndefined::Value(10i32).update_to(&mut value);
743    /// assert_eq!(value, Some(10));
744    ///
745    /// MaybeUndefined::Undefined.update_to(&mut value);
746    /// assert_eq!(value, Some(10));
747    ///
748    /// MaybeUndefined::Null.update_to(&mut value);
749    /// assert_eq!(value, None);
750    /// ```
751    pub fn update_to(self, value: &mut Option<T>) {
752        match self {
753            MaybeUndefined::Value(new) => *value = Some(new),
754            MaybeUndefined::Null => *value = None,
755            MaybeUndefined::Undefined => {}
756        }
757    }
758}
759
760impl<T, E> MaybeUndefined<Result<T, E>> {
761    /// Transposes a `MaybeUndefined` of a [`Result`] into a [`Result`] of a
762    /// `MaybeUndefined`.
763    ///
764    /// [`MaybeUndefined::Undefined`] will be mapped to
765    /// [`Ok`]`(`[`MaybeUndefined::Undefined`]`)`. [`MaybeUndefined::Null`]
766    /// will be mapped to [`Ok`]`(`[`MaybeUndefined::Null`]`)`.
767    /// [`MaybeUndefined::Value`]`(`[`Ok`]`(_))` and
768    /// [`MaybeUndefined::Value`]`(`[`Err`]`(_))` will be mapped to
769    /// [`Ok`]`(`[`MaybeUndefined::Value`]`(_))` and [`Err`]`(_)`.
770    ///
771    /// # Errors
772    ///
773    /// Returns an error if the input is [`MaybeUndefined::Value`]`(`[`Err`]`(_))`.
774    #[inline]
775    pub fn transpose(self) -> Result<MaybeUndefined<T>, E> {
776        match self {
777            MaybeUndefined::Undefined => Ok(MaybeUndefined::Undefined),
778            MaybeUndefined::Null => Ok(MaybeUndefined::Null),
779            MaybeUndefined::Value(Ok(v)) => Ok(MaybeUndefined::Value(v)),
780            MaybeUndefined::Value(Err(e)) => Err(e),
781        }
782    }
783}
784
785impl<T: Serialize> Serialize for MaybeUndefined<T> {
786    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
787        match self {
788            MaybeUndefined::Value(value) => value.serialize(serializer),
789            MaybeUndefined::Null => serializer.serialize_none(),
790            MaybeUndefined::Undefined => serializer.serialize_unit(),
791        }
792    }
793}
794
795impl<'de, T> Deserialize<'de> for MaybeUndefined<T>
796where
797    T: Deserialize<'de>,
798{
799    fn deserialize<D>(deserializer: D) -> Result<MaybeUndefined<T>, D::Error>
800    where
801        D: Deserializer<'de>,
802    {
803        Option::<T>::deserialize(deserializer).map(|value| match value {
804            Some(value) => MaybeUndefined::Value(value),
805            None => MaybeUndefined::Null,
806        })
807    }
808}
809
810impl<T> From<MaybeUndefined<T>> for Option<Option<T>> {
811    fn from(maybe_undefined: MaybeUndefined<T>) -> Self {
812        match maybe_undefined {
813            MaybeUndefined::Undefined => None,
814            MaybeUndefined::Null => Some(None),
815            MaybeUndefined::Value(value) => Some(Some(value)),
816        }
817    }
818}
819
820impl<T> From<Option<Option<T>>> for MaybeUndefined<T> {
821    fn from(value: Option<Option<T>>) -> Self {
822        match value {
823            Some(Some(value)) => Self::Value(value),
824            Some(None) => Self::Null,
825            None => Self::Undefined,
826        }
827    }
828}
829
830impl<'de, T, TAs> DeserializeAs<'de, MaybeUndefined<T>> for MaybeUndefined<TAs>
831where
832    TAs: DeserializeAs<'de, T>,
833{
834    fn deserialize_as<D>(deserializer: D) -> Result<MaybeUndefined<T>, D::Error>
835    where
836        D: Deserializer<'de>,
837    {
838        Option::<DeserializeAsWrap<T, TAs>>::deserialize(deserializer).map(|value| match value {
839            Some(value) => MaybeUndefined::Value(value.into_inner()),
840            None => MaybeUndefined::Null,
841        })
842    }
843}
844
845/// Utility trait for builder methods for optional values.
846/// This allows the caller to either pass in the value itself without wrapping it in `Some`,
847/// or to just pass in an Option if that is what they have, or set it back to undefined.
848pub trait IntoMaybeUndefined<T> {
849    /// Converts this value into a three-state builder argument.
850    fn into_maybe_undefined(self) -> MaybeUndefined<T>;
851}
852
853impl<T> IntoMaybeUndefined<T> for T {
854    fn into_maybe_undefined(self) -> MaybeUndefined<T> {
855        MaybeUndefined::Value(self)
856    }
857}
858
859impl<T> IntoMaybeUndefined<T> for Option<T> {
860    fn into_maybe_undefined(self) -> MaybeUndefined<T> {
861        match self {
862            Some(value) => MaybeUndefined::Value(value),
863            None => MaybeUndefined::Null,
864        }
865    }
866}
867
868impl<T> IntoMaybeUndefined<T> for MaybeUndefined<T> {
869    fn into_maybe_undefined(self) -> MaybeUndefined<T> {
870        self
871    }
872}
873
874impl IntoMaybeUndefined<String> for &str {
875    fn into_maybe_undefined(self) -> MaybeUndefined<String> {
876        MaybeUndefined::Value(self.into())
877    }
878}
879
880impl IntoMaybeUndefined<String> for &mut str {
881    fn into_maybe_undefined(self) -> MaybeUndefined<String> {
882        MaybeUndefined::Value(self.into())
883    }
884}
885
886impl IntoMaybeUndefined<String> for &String {
887    fn into_maybe_undefined(self) -> MaybeUndefined<String> {
888        MaybeUndefined::Value(self.into())
889    }
890}
891
892impl IntoMaybeUndefined<String> for Box<str> {
893    fn into_maybe_undefined(self) -> MaybeUndefined<String> {
894        MaybeUndefined::Value(self.into())
895    }
896}
897
898impl IntoMaybeUndefined<String> for Cow<'_, str> {
899    fn into_maybe_undefined(self) -> MaybeUndefined<String> {
900        MaybeUndefined::Value(self.into())
901    }
902}
903
904impl IntoMaybeUndefined<String> for Arc<str> {
905    fn into_maybe_undefined(self) -> MaybeUndefined<String> {
906        MaybeUndefined::Value(self.to_string())
907    }
908}
909
910impl<T: ?Sized + AsRef<OsStr>> IntoMaybeUndefined<PathBuf> for &T {
911    fn into_maybe_undefined(self) -> MaybeUndefined<PathBuf> {
912        MaybeUndefined::Value(self.into())
913    }
914}
915
916impl IntoMaybeUndefined<PathBuf> for Box<Path> {
917    fn into_maybe_undefined(self) -> MaybeUndefined<PathBuf> {
918        MaybeUndefined::Value(self.into())
919    }
920}
921
922impl IntoMaybeUndefined<PathBuf> for Cow<'_, Path> {
923    fn into_maybe_undefined(self) -> MaybeUndefined<PathBuf> {
924        MaybeUndefined::Value(self.into())
925    }
926}
927
928impl IntoMaybeUndefined<serde_json::Value> for &str {
929    fn into_maybe_undefined(self) -> MaybeUndefined<serde_json::Value> {
930        MaybeUndefined::Value(self.into())
931    }
932}
933
934impl IntoMaybeUndefined<serde_json::Value> for String {
935    fn into_maybe_undefined(self) -> MaybeUndefined<serde_json::Value> {
936        MaybeUndefined::Value(self.into())
937    }
938}
939
940impl IntoMaybeUndefined<serde_json::Value> for Cow<'_, str> {
941    fn into_maybe_undefined(self) -> MaybeUndefined<serde_json::Value> {
942        MaybeUndefined::Value(self.into())
943    }
944}
945
946#[cfg(test)]
947mod tests {
948    use serde::{Deserialize, Serialize};
949    use serde_json::{from_value, json, to_value};
950
951    use super::*;
952
953    #[test]
954    fn test_maybe_undefined_serde() {
955        #[derive(Serialize, Deserialize, Eq, PartialEq, Debug)]
956        struct A {
957            #[serde(default, skip_serializing_if = "MaybeUndefined::is_undefined")]
958            a: MaybeUndefined<i32>,
959        }
960
961        assert_eq!(to_value(MaybeUndefined::Value(100i32)).unwrap(), json!(100));
962
963        assert_eq!(
964            from_value::<MaybeUndefined<i32>>(json!(100)).unwrap(),
965            MaybeUndefined::Value(100)
966        );
967        assert_eq!(
968            from_value::<MaybeUndefined<i32>>(json!(null)).unwrap(),
969            MaybeUndefined::Null
970        );
971
972        assert_eq!(
973            to_value(&A {
974                a: MaybeUndefined::Value(100i32)
975            })
976            .unwrap(),
977            json!({"a": 100})
978        );
979
980        assert_eq!(
981            to_value(&A {
982                a: MaybeUndefined::Null,
983            })
984            .unwrap(),
985            json!({ "a": null })
986        );
987
988        assert_eq!(
989            to_value(&A {
990                a: MaybeUndefined::Undefined,
991            })
992            .unwrap(),
993            json!({})
994        );
995
996        assert_eq!(
997            from_value::<A>(json!({"a": 100})).unwrap(),
998            A {
999                a: MaybeUndefined::Value(100i32)
1000            }
1001        );
1002
1003        assert_eq!(
1004            from_value::<A>(json!({ "a": null })).unwrap(),
1005            A {
1006                a: MaybeUndefined::Null
1007            }
1008        );
1009
1010        assert_eq!(
1011            from_value::<A>(json!({})).unwrap(),
1012            A {
1013                a: MaybeUndefined::Undefined
1014            }
1015        );
1016    }
1017
1018    #[test]
1019    fn test_maybe_undefined_to_nested_option() {
1020        assert_eq!(Option::<Option<i32>>::from(MaybeUndefined::Undefined), None);
1021
1022        assert_eq!(
1023            Option::<Option<i32>>::from(MaybeUndefined::Null),
1024            Some(None)
1025        );
1026
1027        assert_eq!(
1028            Option::<Option<i32>>::from(MaybeUndefined::Value(42)),
1029            Some(Some(42))
1030        );
1031    }
1032
1033    #[test]
1034    fn test_as_opt_ref() {
1035        let value = MaybeUndefined::<String>::Undefined;
1036        let r = value.as_opt_ref();
1037        assert_eq!(r, None);
1038
1039        let value = MaybeUndefined::<String>::Null;
1040        let r = value.as_opt_ref();
1041        assert_eq!(r, Some(None));
1042
1043        let value = MaybeUndefined::<String>::Value("abc".to_string());
1044        let r = value.as_opt_ref();
1045        assert_eq!(r, Some(Some(&"abc".to_string())));
1046    }
1047
1048    #[test]
1049    fn test_as_opt_deref() {
1050        let value = MaybeUndefined::<String>::Undefined;
1051        let r = value.as_opt_deref();
1052        assert_eq!(r, None);
1053
1054        let value = MaybeUndefined::<String>::Null;
1055        let r = value.as_opt_deref();
1056        assert_eq!(r, Some(None));
1057
1058        let value = MaybeUndefined::<String>::Value("abc".to_string());
1059        let r = value.as_opt_deref();
1060        assert_eq!(r, Some(Some("abc")));
1061    }
1062
1063    #[test]
1064    fn test_contains_value() {
1065        let test = "abc";
1066
1067        let mut value: MaybeUndefined<String> = MaybeUndefined::Undefined;
1068        assert!(!value.contains_value(&test));
1069
1070        value = MaybeUndefined::Null;
1071        assert!(!value.contains_value(&test));
1072
1073        value = MaybeUndefined::Value("abc".to_string());
1074        assert!(value.contains_value(&test));
1075    }
1076
1077    #[test]
1078    fn test_contains() {
1079        let test = Some("abc");
1080        let none: Option<&str> = None;
1081
1082        let mut value: MaybeUndefined<String> = MaybeUndefined::Undefined;
1083        assert!(!value.contains(test.as_ref()));
1084        assert!(!value.contains(none.as_ref()));
1085
1086        value = MaybeUndefined::Null;
1087        assert!(!value.contains(test.as_ref()));
1088        assert!(value.contains(none.as_ref()));
1089
1090        value = MaybeUndefined::Value("abc".to_string());
1091        assert!(value.contains(test.as_ref()));
1092        assert!(!value.contains(none.as_ref()));
1093    }
1094
1095    #[test]
1096    fn test_map_value() {
1097        let mut value: MaybeUndefined<i32> = MaybeUndefined::Undefined;
1098        assert_eq!(value.map_value(|v| v > 2), MaybeUndefined::Undefined);
1099
1100        value = MaybeUndefined::Null;
1101        assert_eq!(value.map_value(|v| v > 2), MaybeUndefined::Null);
1102
1103        value = MaybeUndefined::Value(5);
1104        assert_eq!(value.map_value(|v| v > 2), MaybeUndefined::Value(true));
1105    }
1106
1107    #[test]
1108    fn test_map() {
1109        let mut value: MaybeUndefined<i32> = MaybeUndefined::Undefined;
1110        assert_eq!(value.map(|v| Some(v.is_some())), MaybeUndefined::Undefined);
1111
1112        value = MaybeUndefined::Null;
1113        assert_eq!(
1114            value.map(|v| Some(v.is_some())),
1115            MaybeUndefined::Value(false)
1116        );
1117
1118        value = MaybeUndefined::Value(5);
1119        assert_eq!(
1120            value.map(|v| Some(v.is_some())),
1121            MaybeUndefined::Value(true)
1122        );
1123    }
1124
1125    #[test]
1126    fn test_transpose() {
1127        let mut value: MaybeUndefined<Result<i32, &'static str>> = MaybeUndefined::Undefined;
1128        assert_eq!(value.transpose(), Ok(MaybeUndefined::Undefined));
1129
1130        value = MaybeUndefined::Null;
1131        assert_eq!(value.transpose(), Ok(MaybeUndefined::Null));
1132
1133        value = MaybeUndefined::Value(Ok(5));
1134        assert_eq!(value.transpose(), Ok(MaybeUndefined::Value(5)));
1135
1136        value = MaybeUndefined::Value(Err("error"));
1137        assert_eq!(value.transpose(), Err("error"));
1138    }
1139}