1use 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
28macro_rules! default_on_null {
40 (@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 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 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 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 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
490pub trait IntoOption<T> {
496 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#[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 #[default]
610 Undefined,
611 Null,
613 Value(T),
615}
616
617impl<T> MaybeUndefined<T> {
618 #[inline]
620 pub const fn is_undefined(&self) -> bool {
621 matches!(self, MaybeUndefined::Undefined)
622 }
623
624 #[inline]
626 pub const fn is_null(&self) -> bool {
627 matches!(self, MaybeUndefined::Null)
628 }
629
630 #[inline]
632 pub const fn is_value(&self) -> bool {
633 matches!(self, MaybeUndefined::Value(_))
634 }
635
636 #[inline]
639 pub const fn value(&self) -> Option<&T> {
640 match self {
641 MaybeUndefined::Value(value) => Some(value),
642 _ => None,
643 }
644 }
645
646 #[inline]
648 pub fn take(self) -> Option<T> {
649 match self {
650 MaybeUndefined::Value(value) => Some(value),
651 _ => None,
652 }
653 }
654
655 #[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 #[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 #[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 #[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 #[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 #[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 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 #[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
845pub trait IntoMaybeUndefined<T> {
849 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}