1use std::{
20 borrow::Cow,
21 ffi::OsStr,
22 ops::Deref,
23 path::{Path, PathBuf},
24 sync::Arc,
25};
26
27use serde::{Deserialize, Deserializer, Serialize, Serializer};
28use serde_with::{DeserializeAs, de::DeserializeAsWrap};
29
30macro_rules! default_on_null {
42 (
43 $(#[$attribute:meta])*
44 $visibility:vis struct $payload:ident {
45 $(
46 $(#[$field_attribute:meta])*
47 $field_visibility:vis $field:ident: $field_type:ty
48 ),* $(,)?
49 }
50 ) => {
51 $(#[$attribute])*
52 $visibility struct $payload {
53 $(
54 $(#[$field_attribute])*
55 $field_visibility $field: $field_type,
56 )*
57 }
58
59 impl<'de> serde::Deserialize<'de> for $payload {
60 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
61 $(#[$attribute])*
62 #[derive(serde::Deserialize)]
63 struct Wire {
64 $(
65 $(#[$field_attribute])*
66 $field_visibility $field: $field_type,
67 )*
68 }
69
70 struct NonNull;
71
72 impl<'de> serde_with::DeserializeAs<'de, $payload> for NonNull {
73 fn deserialize_as<D: serde::Deserializer<'de>>(
74 deserializer: D,
75 ) -> Result<$payload, D::Error> {
76 let wire = <Wire as serde::Deserialize>::deserialize(deserializer)?;
77 Ok($payload {
78 $($field: wire.$field,)*
79 })
80 }
81 }
82
83 <serde_with::DefaultOnNull<NonNull> as serde_with::DeserializeAs<
84 'de,
85 Self,
86 >>::deserialize_as(deserializer)
87 }
88 }
89 };
90}
91
92pub(crate) use default_on_null;
93
94#[cfg(test)]
95mod default_on_null_tests {
96 use serde::{Deserialize, Serialize, de::DeserializeOwned};
97 use serde_json::{Value, json};
98
99 use crate::{
100 MaybeUndefined,
101 rpc::{JsonRpcMessage, Request, Response},
102 v1::{
103 self, LoadSessionResponse, NewSessionResponse, PromptResponse, ReadTextFileResponse,
104 RequestPermissionResponse, WaitForTerminalExitResponse, WriteTextFileResponse,
105 },
106 };
107
108 macro_rules! for_each_defaultable_payload {
112 ($check:ident) => {
113 $check::<v1::AuthenticateResponse>();
114 $check::<v1::LogoutRequest>();
115 $check::<v1::LogoutResponse>();
116 $check::<v1::LoadSessionResponse>();
117 $check::<v1::ResumeSessionResponse>();
118 $check::<v1::CloseSessionResponse>();
119 $check::<v1::ListSessionsRequest>();
120 $check::<v1::DeleteSessionResponse>();
121 $check::<v1::SetSessionModeResponse>();
122 $check::<v1::WriteTextFileResponse>();
123 $check::<v1::ReleaseTerminalResponse>();
124 $check::<v1::KillTerminalResponse>();
125 $check::<v1::WaitForTerminalExitResponse>();
126
127 #[cfg(feature = "unstable_llm_providers")]
128 {
129 $check::<v1::ListProvidersRequest>();
130 $check::<v1::SetProviderResponse>();
131 $check::<v1::DisableProviderResponse>();
132 }
133 #[cfg(feature = "unstable_nes")]
134 {
135 $check::<v1::StartNesRequest>();
136 $check::<v1::CloseNesResponse>();
137 }
138 #[cfg(feature = "unstable_mcp_over_acp")]
139 {
140 $check::<v1::DisconnectMcpResponse>();
141 }
142
143 #[cfg(feature = "unstable_protocol_v2")]
144 {
145 use crate::v2;
146
147 $check::<v2::LoginAuthResponse>();
148 $check::<v2::LogoutAuthRequest>();
149 $check::<v2::LogoutAuthResponse>();
150 $check::<v2::ResumeSessionResponse>();
151 $check::<v2::CloseSessionResponse>();
152 $check::<v2::ListSessionsRequest>();
153 $check::<v2::DeleteSessionResponse>();
154
155 #[cfg(feature = "unstable_llm_providers")]
156 {
157 $check::<v2::ListProvidersRequest>();
158 $check::<v2::SetProviderResponse>();
159 $check::<v2::DisableProviderResponse>();
160 }
161 #[cfg(feature = "unstable_nes")]
162 {
163 $check::<v2::StartNesRequest>();
164 $check::<v2::CloseNesResponse>();
165 }
166 #[cfg(feature = "unstable_mcp_over_acp")]
167 {
168 $check::<v2::DisconnectMcpResponse>();
169 }
170 }
171 };
172 }
173
174 fn assert_defaultable_payload<T>()
175 where
176 T: Default + DeserializeOwned + Serialize + PartialEq + std::fmt::Debug,
177 {
178 let name = std::any::type_name::<T>();
179 for value in [Value::Null, json!({})] {
180 assert_eq!(
181 serde_json::from_value::<T>(value).unwrap(),
182 T::default(),
183 "{name}",
184 );
185 }
186 assert_eq!(
187 serde_json::from_str::<T>("null").unwrap(),
188 T::default(),
189 "{name}",
190 );
191 assert_eq!(
192 serde_json::to_value(T::default()).unwrap(),
193 json!({}),
194 "{name}"
195 );
196
197 let metadata = json!({"_meta": {"example.com/key": ["preserve", 1, null]}});
198 let payload: T = serde_json::from_value(metadata.clone()).unwrap();
199 assert_eq!(serde_json::to_value(payload).unwrap(), metadata, "{name}");
200
201 for value in [json!(false), json!(42), json!("invalid")] {
202 assert!(serde_json::from_value::<T>(value).is_err(), "{name}");
203 }
204
205 assert_eq!(
207 serde_json::from_value::<Option<T>>(Value::Null).unwrap(),
208 None,
209 "{name}",
210 );
211 assert_eq!(
212 serde_json::from_value::<MaybeUndefined<T>>(Value::Null).unwrap(),
213 MaybeUndefined::Null,
214 "{name}",
215 );
216 }
217
218 #[test]
219 fn defaultable_payloads_accept_null_without_losing_information() {
220 for_each_defaultable_payload!(assert_defaultable_payload);
221 }
222
223 #[test]
224 fn inherent_methods_do_not_bypass_null_handling() {
225 assert_eq!(
227 WriteTextFileResponse::deserialize(Value::Null).unwrap(),
228 WriteTextFileResponse::default(),
229 );
230 assert_eq!(
231 LoadSessionResponse::deserialize(Value::Null).unwrap(),
232 LoadSessionResponse::default(),
233 );
234 }
235
236 #[test]
237 fn optional_payload_fields_are_preserved() {
238 let load = json!({
239 "modes": {
240 "currentModeId": "ask",
241 "availableModes": [{"id": "ask", "name": "Ask"}]
242 },
243 "configOptions": [],
244 "_meta": {"example.com/key": "value"}
245 });
246 let response: LoadSessionResponse = serde_json::from_value(load.clone()).unwrap();
247 assert_eq!(serde_json::to_value(response).unwrap(), load);
248
249 let list = json!({"cwd": "/workspace", "cursor": "next-page"});
250 let request: v1::ListSessionsRequest = serde_json::from_value(list.clone()).unwrap();
251 assert_eq!(serde_json::to_value(request).unwrap(), list);
252
253 #[cfg(feature = "unstable_protocol_v2")]
254 {
255 let request: crate::v2::ListSessionsRequest =
256 serde_json::from_value(list.clone()).unwrap();
257 assert_eq!(serde_json::to_value(request).unwrap(), list);
258 }
259 }
260
261 #[test]
262 fn default_terminal_exit_status_is_unknown_not_success() {
263 let response: WaitForTerminalExitResponse = serde_json::from_value(Value::Null).unwrap();
264 assert_eq!(response.exit_status.exit_code, None);
265 assert_eq!(response.exit_status.signal, None);
266 assert_eq!(response, WaitForTerminalExitResponse::default());
267
268 for value in [
269 json!({"exitCode": 0}),
270 json!({"exitCode": 17}),
271 json!({"signal": "SIGTERM"}),
272 ] {
273 let response: WaitForTerminalExitResponse =
274 serde_json::from_value(value.clone()).unwrap();
275 assert_eq!(serde_json::to_value(response).unwrap(), value);
276 }
277 }
278
279 #[test]
280 fn non_null_payloads_keep_the_derived_deserialization_behavior() {
281 super::default_on_null! {
284 #[derive(Default, Debug, Serialize, PartialEq)]
285 struct RequiredField {
286 count: u32,
287 }
288 }
289 #[derive(Deserialize)]
290 struct BaselineWrite {
291 #[serde(
292 default,
293 rename = "_meta",
294 with = "serde_with::As::<serde_with::DefaultOnError>"
295 )]
296 meta: Option<serde_json::Map<String, Value>>,
297 }
298
299 assert_eq!(
300 serde_json::from_value::<RequiredField>(Value::Null).unwrap(),
301 RequiredField::default(),
302 );
303 assert!(serde_json::from_value::<RequiredField>(json!({})).is_err());
304 assert!(serde_json::from_value::<RequiredField>(json!({"count": "invalid"})).is_err());
305
306 for value in [
307 json!({}),
308 json!({"_meta": {"example.com/key": true}}),
309 json!({"_meta": 42}),
310 json!({"modes": "invalid", "configOptions": "invalid"}),
311 json!([]),
312 json!([null]),
313 json!(false),
314 json!(42),
315 json!("invalid"),
316 ] {
317 assert_eq!(
318 serde_json::from_value::<WriteTextFileResponse>(value.clone())
319 .map(|response| response.meta)
320 .map_err(|_| ()),
321 serde_json::from_value::<BaselineWrite>(value)
322 .map(|response| response.meta)
323 .map_err(|_| ()),
324 );
325 }
326 }
327
328 #[test]
329 fn payloads_with_required_fields_still_reject_null() {
330 assert!(serde_json::from_value::<ReadTextFileResponse>(Value::Null).is_err());
331 assert!(serde_json::from_value::<RequestPermissionResponse>(Value::Null).is_err());
332 assert!(serde_json::from_value::<NewSessionResponse>(Value::Null).is_err());
333 assert!(serde_json::from_value::<PromptResponse>(Value::Null).is_err());
334 assert!(serde_json::from_value::<v1::InitializeResponse>(Value::Null).is_err());
335 assert!(serde_json::from_value::<v1::CreateTerminalResponse>(Value::Null).is_err());
336 assert!(serde_json::from_value::<v1::TerminalOutputResponse>(Value::Null).is_err());
337 assert!(serde_json::from_value::<v1::CreateElicitationResponse>(Value::Null).is_err());
338
339 #[cfg(feature = "unstable_protocol_v2")]
340 {
341 use crate::v2;
342
343 assert!(serde_json::from_value::<v2::InitializeResponse>(Value::Null).is_err());
344 assert!(serde_json::from_value::<v2::NewSessionResponse>(Value::Null).is_err());
345 assert!(serde_json::from_value::<v2::PromptResponse>(Value::Null).is_err());
346 assert!(serde_json::from_value::<v2::RequestPermissionResponse>(Value::Null).is_err());
347 assert!(serde_json::from_value::<v2::CreateElicitationResponse>(Value::Null).is_err());
348 }
349 }
350
351 #[test]
352 fn raw_response_nulls_are_not_rewritten() {
353 let extension: v1::ExtResponse = serde_json::from_value(Value::Null).unwrap();
354 assert_eq!(serde_json::to_value(extension).unwrap(), Value::Null);
355
356 #[cfg(feature = "unstable_mcp_over_acp")]
357 {
358 let mcp: v1::MessageMcpResponse = serde_json::from_value(Value::Null).unwrap();
359 assert_eq!(serde_json::to_value(mcp).unwrap(), Value::Null);
360 }
361
362 #[cfg(feature = "unstable_protocol_v2")]
363 {
364 let extension: crate::v2::ExtResponse = serde_json::from_value(Value::Null).unwrap();
365 assert_eq!(serde_json::to_value(extension).unwrap(), Value::Null);
366
367 #[cfg(feature = "unstable_mcp_over_acp")]
368 {
369 let mcp: crate::v2::MessageMcpResponse =
370 serde_json::from_value(Value::Null).unwrap();
371 assert_eq!(serde_json::to_value(mcp).unwrap(), Value::Null);
372 }
373 }
374 }
375
376 #[test]
377 fn optional_request_parameters_keep_their_existing_meaning() {
378 type LogoutRequest = Request<v1::LogoutRequest>;
379 for value in [
380 json!({"id": 1, "method": "logout"}),
381 json!({"id": 1, "method": "logout", "params": null}),
382 ] {
383 let request: LogoutRequest = serde_json::from_value(value).unwrap();
384 assert_eq!(request.params, None);
385 }
386 let request: LogoutRequest =
387 serde_json::from_value(json!({"id": 1, "method": "logout", "params": {}})).unwrap();
388 assert_eq!(request.params, Some(v1::LogoutRequest::default()));
389 }
390
391 #[test]
392 fn nullable_fields_keep_their_existing_meaning() {
393 #[derive(Debug, Deserialize, PartialEq)]
394 struct Container {
395 optional: Option<WriteTextFileResponse>,
396 #[serde(default)]
397 patch: MaybeUndefined<WriteTextFileResponse>,
398 }
399
400 assert_eq!(
401 serde_json::from_value::<Option<WriteTextFileResponse>>(Value::Null).unwrap(),
402 None,
403 );
404 assert_eq!(
405 serde_json::from_value::<MaybeUndefined<WriteTextFileResponse>>(Value::Null).unwrap(),
406 MaybeUndefined::Null,
407 );
408 assert_eq!(
409 serde_json::from_value::<Container>(json!({})).unwrap(),
410 Container {
411 optional: None,
412 patch: MaybeUndefined::Undefined,
413 },
414 );
415 assert_eq!(
416 serde_json::from_value::<Container>(json!({"optional": null, "patch": null})).unwrap(),
417 Container {
418 optional: None,
419 patch: MaybeUndefined::Null,
420 },
421 );
422 assert_eq!(
423 serde_json::from_value::<Container>(json!({"optional": {}, "patch": {}})).unwrap(),
424 Container {
425 optional: Some(WriteTextFileResponse::default()),
426 patch: MaybeUndefined::Value(WriteTextFileResponse::default()),
427 },
428 );
429 }
430
431 #[test]
432 fn response_result_is_required_even_when_its_payload_accepts_null() {
433 type WriteResponse = Response<WriteTextFileResponse, Value>;
434 let response: WriteResponse =
435 serde_json::from_value(json!({"id": 1, "result": null})).unwrap();
436 assert_eq!(
437 response,
438 Response::new(1, Ok(WriteTextFileResponse::default())),
439 );
440 assert!(serde_json::from_value::<WriteResponse>(json!({"id": 1})).is_err());
441 assert!(
442 serde_json::from_value::<JsonRpcMessage<WriteResponse>>(
443 json!({"jsonrpc": "2.0", "id": 1})
444 )
445 .is_err()
446 );
447
448 let error = json!({"code": -32603, "message": "Internal error"});
449 let response: JsonRpcMessage<WriteResponse> = serde_json::from_value(json!({
450 "jsonrpc": "2.0",
451 "id": 1,
452 "error": error.clone()
453 }))
454 .unwrap();
455 assert_eq!(response.into_inner(), Response::new(1, Err(error)));
456 }
457
458 #[cfg(feature = "schemars")]
459 #[test]
460 fn defaultable_payload_schemas_still_require_objects() {
461 fn assert_object_schema<T: schemars::JsonSchema>() {
462 let schema = serde_json::to_value(schemars::schema_for!(T)).unwrap();
463 assert_eq!(schema["type"], "object");
464 assert!(schema.get("anyOf").is_none());
465 }
466 for_each_defaultable_payload!(assert_object_schema);
467 }
468}
469
470#[cfg(feature = "tracing")]
483#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
484#[non_exhaustive]
485pub(crate) struct SkipListener;
486
487#[cfg(feature = "tracing")]
488impl serde_with::InspectError for SkipListener {
489 fn inspect_error(error: impl serde::de::Error) {
490 tracing::warn!(
491 %error,
492 "skipped malformed list entry during deserialization",
493 );
494 }
495}
496
497#[cfg(not(feature = "tracing"))]
501pub(crate) type SkipListener = ();
502
503#[cfg(test)]
504mod skip_listener_tests {
505 use std::cell::Cell;
506
507 use serde::{Deserialize, Serialize};
508 use serde_json::json;
509 use serde_with::{DefaultOnError, VecSkipError, serde_as};
510
511 thread_local! {
512 static SKIP_COUNT: Cell<u32> = const { Cell::new(0) };
513 }
514
515 struct CountingListener;
517
518 impl serde_with::InspectError for CountingListener {
519 fn inspect_error(_error: impl serde::de::Error) {
520 SKIP_COUNT.with(|c| c.set(c.get() + 1));
521 }
522 }
523
524 #[serde_as]
525 #[derive(Serialize, Deserialize, Debug, PartialEq)]
526 struct Wrapper {
527 #[serde_as(deserialize_as = "VecSkipError<_, CountingListener>")]
528 values: Vec<u32>,
529 }
530
531 #[test]
532 fn inspector_runs_for_each_skipped_entry() {
533 SKIP_COUNT.with(|c| c.set(0));
534
535 let input = json!({"values": [1, "oops", 2, {}, 3]});
536 let wrapper: Wrapper = serde_json::from_value(input).unwrap();
537
538 assert_eq!(wrapper.values, vec![1, 2, 3]);
539 assert_eq!(SKIP_COUNT.with(Cell::get), 2);
540 }
541
542 #[serde_as]
547 #[derive(Deserialize, Debug, PartialEq)]
548 struct ResilientVec {
549 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, CountingListener>>")]
550 #[serde(default)]
551 values: Vec<u32>,
552 }
553
554 #[test]
555 fn resilient_vec_tolerates_missing_null_and_wrong_type() {
556 let r: ResilientVec = serde_json::from_value(json!({})).unwrap();
558 assert_eq!(r.values, Vec::<u32>::new());
559
560 let r: ResilientVec = serde_json::from_value(json!({"values": null})).unwrap();
562 assert_eq!(r.values, Vec::<u32>::new());
563
564 let r: ResilientVec = serde_json::from_value(json!({"values": "oops"})).unwrap();
566 assert_eq!(r.values, Vec::<u32>::new());
567
568 let r: ResilientVec = serde_json::from_value(json!({"values": {"k": 1}})).unwrap();
570 assert_eq!(r.values, Vec::<u32>::new());
571
572 SKIP_COUNT.with(|c| c.set(0));
574 let r: ResilientVec =
575 serde_json::from_value(json!({"values": [1, "oops", 2, {}, 3]})).unwrap();
576 assert_eq!(r.values, vec![1, 2, 3]);
577 assert_eq!(SKIP_COUNT.with(Cell::get), 2);
578 }
579
580 #[test]
581 fn resilient_vec_does_not_invoke_inspector_on_outer_failure() {
582 SKIP_COUNT.with(|c| c.set(0));
583
584 let _r: ResilientVec = serde_json::from_value(json!({"values": null})).unwrap();
587 let _r: ResilientVec = serde_json::from_value(json!({"values": "oops"})).unwrap();
588 let _r: ResilientVec = serde_json::from_value(json!({"values": {}})).unwrap();
589
590 assert_eq!(SKIP_COUNT.with(Cell::get), 0);
591 }
592
593 #[serde_as]
598 #[derive(Deserialize, Debug, PartialEq)]
599 struct ResilientOptionVec {
600 #[serde_as(deserialize_as = "DefaultOnError<Option<VecSkipError<_, CountingListener>>>")]
601 #[serde(default)]
602 values: Option<Vec<u32>>,
603 }
604
605 #[test]
606 fn resilient_option_vec_tolerates_missing_null_and_wrong_type() {
607 let r: ResilientOptionVec = serde_json::from_value(json!({})).unwrap();
609 assert_eq!(r.values, None);
610
611 let r: ResilientOptionVec = serde_json::from_value(json!({"values": null})).unwrap();
613 assert_eq!(r.values, None);
614
615 let r: ResilientOptionVec = serde_json::from_value(json!({"values": []})).unwrap();
617 assert_eq!(r.values, Some(Vec::<u32>::new()));
618
619 let r: ResilientOptionVec = serde_json::from_value(json!({"values": [1, 2, 3]})).unwrap();
621 assert_eq!(r.values, Some(vec![1, 2, 3]));
622
623 let r: ResilientOptionVec = serde_json::from_value(json!({"values": "oops"})).unwrap();
625 assert_eq!(r.values, None);
626
627 let r: ResilientOptionVec = serde_json::from_value(json!({"values": {"k": 1}})).unwrap();
629 assert_eq!(r.values, None);
630
631 SKIP_COUNT.with(|c| c.set(0));
633 let r: ResilientOptionVec =
634 serde_json::from_value(json!({"values": [1, "oops", 2, {}, 3]})).unwrap();
635 assert_eq!(r.values, Some(vec![1, 2, 3]));
636 assert_eq!(SKIP_COUNT.with(Cell::get), 2);
637 }
638}
639
640pub trait IntoOption<T> {
646 fn into_option(self) -> Option<T>;
648}
649
650impl<T> IntoOption<T> for Option<T> {
651 fn into_option(self) -> Option<T> {
652 self
653 }
654}
655
656impl<T> IntoOption<T> for T {
657 fn into_option(self) -> Option<T> {
658 Some(self)
659 }
660}
661
662impl IntoOption<String> for &str {
663 fn into_option(self) -> Option<String> {
664 Some(self.into())
665 }
666}
667
668impl IntoOption<String> for &mut str {
669 fn into_option(self) -> Option<String> {
670 Some(self.into())
671 }
672}
673
674impl IntoOption<String> for &String {
675 fn into_option(self) -> Option<String> {
676 Some(self.into())
677 }
678}
679
680impl IntoOption<String> for Box<str> {
681 fn into_option(self) -> Option<String> {
682 Some(self.into())
683 }
684}
685
686impl IntoOption<String> for Cow<'_, str> {
687 fn into_option(self) -> Option<String> {
688 Some(self.into())
689 }
690}
691
692impl IntoOption<String> for Arc<str> {
693 fn into_option(self) -> Option<String> {
694 Some(self.to_string())
695 }
696}
697
698impl<T: ?Sized + AsRef<OsStr>> IntoOption<PathBuf> for &T {
699 fn into_option(self) -> Option<PathBuf> {
700 Some(self.into())
701 }
702}
703
704impl IntoOption<PathBuf> for Box<Path> {
705 fn into_option(self) -> Option<PathBuf> {
706 Some(self.into())
707 }
708}
709
710impl IntoOption<PathBuf> for Cow<'_, Path> {
711 fn into_option(self) -> Option<PathBuf> {
712 Some(self.into())
713 }
714}
715
716impl IntoOption<serde_json::Value> for &str {
717 fn into_option(self) -> Option<serde_json::Value> {
718 Some(self.into())
719 }
720}
721
722impl IntoOption<serde_json::Value> for String {
723 fn into_option(self) -> Option<serde_json::Value> {
724 Some(self.into())
725 }
726}
727
728impl IntoOption<serde_json::Value> for Cow<'_, str> {
729 fn into_option(self) -> Option<serde_json::Value> {
730 Some(self.into())
731 }
732}
733
734#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
754#[derive(Copy, Clone, Default, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
755#[cfg_attr(feature = "schemars", schemars(with = "Option<Option<T>>", inline))]
756#[expect(clippy::exhaustive_enums)]
757pub enum MaybeUndefined<T> {
758 #[default]
760 Undefined,
761 Null,
763 Value(T),
765}
766
767impl<T> MaybeUndefined<T> {
768 #[inline]
770 pub const fn is_undefined(&self) -> bool {
771 matches!(self, MaybeUndefined::Undefined)
772 }
773
774 #[inline]
776 pub const fn is_null(&self) -> bool {
777 matches!(self, MaybeUndefined::Null)
778 }
779
780 #[inline]
782 pub const fn is_value(&self) -> bool {
783 matches!(self, MaybeUndefined::Value(_))
784 }
785
786 #[inline]
789 pub const fn value(&self) -> Option<&T> {
790 match self {
791 MaybeUndefined::Value(value) => Some(value),
792 _ => None,
793 }
794 }
795
796 #[inline]
798 pub fn take(self) -> Option<T> {
799 match self {
800 MaybeUndefined::Value(value) => Some(value),
801 _ => None,
802 }
803 }
804
805 #[inline]
807 pub const fn as_opt_ref(&self) -> Option<Option<&T>> {
808 match self {
809 MaybeUndefined::Undefined => None,
810 MaybeUndefined::Null => Some(None),
811 MaybeUndefined::Value(value) => Some(Some(value)),
812 }
813 }
814
815 #[inline]
817 pub fn as_opt_deref<U>(&self) -> Option<Option<&U>>
818 where
819 U: ?Sized,
820 T: Deref<Target = U>,
821 {
822 match self {
823 MaybeUndefined::Undefined => None,
824 MaybeUndefined::Null => Some(None),
825 MaybeUndefined::Value(value) => Some(Some(&**value)),
826 }
827 }
828
829 #[inline]
831 pub fn contains_value<U>(&self, x: &U) -> bool
832 where
833 U: PartialEq<T>,
834 {
835 match self {
836 MaybeUndefined::Value(y) => x == y,
837 _ => false,
838 }
839 }
840
841 #[inline]
844 pub fn contains<U>(&self, x: Option<&U>) -> bool
845 where
846 U: PartialEq<T>,
847 {
848 match self {
849 MaybeUndefined::Value(y) => matches!(x, Some(v) if v == y),
850 MaybeUndefined::Null => x.is_none(),
851 MaybeUndefined::Undefined => false,
852 }
853 }
854
855 #[inline]
858 pub fn map<U, F: FnOnce(Option<T>) -> Option<U>>(self, f: F) -> MaybeUndefined<U> {
859 match self {
860 MaybeUndefined::Value(v) => match f(Some(v)) {
861 Some(v) => MaybeUndefined::Value(v),
862 None => MaybeUndefined::Null,
863 },
864 MaybeUndefined::Null => match f(None) {
865 Some(v) => MaybeUndefined::Value(v),
866 None => MaybeUndefined::Null,
867 },
868 MaybeUndefined::Undefined => MaybeUndefined::Undefined,
869 }
870 }
871
872 #[inline]
875 pub fn map_value<U, F: FnOnce(T) -> U>(self, f: F) -> MaybeUndefined<U> {
876 match self {
877 MaybeUndefined::Value(v) => MaybeUndefined::Value(f(v)),
878 MaybeUndefined::Null => MaybeUndefined::Null,
879 MaybeUndefined::Undefined => MaybeUndefined::Undefined,
880 }
881 }
882
883 pub fn update_to(self, value: &mut Option<T>) {
902 match self {
903 MaybeUndefined::Value(new) => *value = Some(new),
904 MaybeUndefined::Null => *value = None,
905 MaybeUndefined::Undefined => {}
906 }
907 }
908}
909
910impl<T, E> MaybeUndefined<Result<T, E>> {
911 #[inline]
925 pub fn transpose(self) -> Result<MaybeUndefined<T>, E> {
926 match self {
927 MaybeUndefined::Undefined => Ok(MaybeUndefined::Undefined),
928 MaybeUndefined::Null => Ok(MaybeUndefined::Null),
929 MaybeUndefined::Value(Ok(v)) => Ok(MaybeUndefined::Value(v)),
930 MaybeUndefined::Value(Err(e)) => Err(e),
931 }
932 }
933}
934
935impl<T: Serialize> Serialize for MaybeUndefined<T> {
936 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
937 match self {
938 MaybeUndefined::Value(value) => value.serialize(serializer),
939 MaybeUndefined::Null => serializer.serialize_none(),
940 MaybeUndefined::Undefined => serializer.serialize_unit(),
941 }
942 }
943}
944
945impl<'de, T> Deserialize<'de> for MaybeUndefined<T>
946where
947 T: Deserialize<'de>,
948{
949 fn deserialize<D>(deserializer: D) -> Result<MaybeUndefined<T>, D::Error>
950 where
951 D: Deserializer<'de>,
952 {
953 Option::<T>::deserialize(deserializer).map(|value| match value {
954 Some(value) => MaybeUndefined::Value(value),
955 None => MaybeUndefined::Null,
956 })
957 }
958}
959
960impl<T> From<MaybeUndefined<T>> for Option<Option<T>> {
961 fn from(maybe_undefined: MaybeUndefined<T>) -> Self {
962 match maybe_undefined {
963 MaybeUndefined::Undefined => None,
964 MaybeUndefined::Null => Some(None),
965 MaybeUndefined::Value(value) => Some(Some(value)),
966 }
967 }
968}
969
970impl<T> From<Option<Option<T>>> for MaybeUndefined<T> {
971 fn from(value: Option<Option<T>>) -> Self {
972 match value {
973 Some(Some(value)) => Self::Value(value),
974 Some(None) => Self::Null,
975 None => Self::Undefined,
976 }
977 }
978}
979
980impl<'de, T, TAs> DeserializeAs<'de, MaybeUndefined<T>> for MaybeUndefined<TAs>
981where
982 TAs: DeserializeAs<'de, T>,
983{
984 fn deserialize_as<D>(deserializer: D) -> Result<MaybeUndefined<T>, D::Error>
985 where
986 D: Deserializer<'de>,
987 {
988 Option::<DeserializeAsWrap<T, TAs>>::deserialize(deserializer).map(|value| match value {
989 Some(value) => MaybeUndefined::Value(value.into_inner()),
990 None => MaybeUndefined::Null,
991 })
992 }
993}
994
995pub trait IntoMaybeUndefined<T> {
999 fn into_maybe_undefined(self) -> MaybeUndefined<T>;
1001}
1002
1003impl<T> IntoMaybeUndefined<T> for T {
1004 fn into_maybe_undefined(self) -> MaybeUndefined<T> {
1005 MaybeUndefined::Value(self)
1006 }
1007}
1008
1009impl<T> IntoMaybeUndefined<T> for Option<T> {
1010 fn into_maybe_undefined(self) -> MaybeUndefined<T> {
1011 match self {
1012 Some(value) => MaybeUndefined::Value(value),
1013 None => MaybeUndefined::Null,
1014 }
1015 }
1016}
1017
1018impl<T> IntoMaybeUndefined<T> for MaybeUndefined<T> {
1019 fn into_maybe_undefined(self) -> MaybeUndefined<T> {
1020 self
1021 }
1022}
1023
1024impl IntoMaybeUndefined<String> for &str {
1025 fn into_maybe_undefined(self) -> MaybeUndefined<String> {
1026 MaybeUndefined::Value(self.into())
1027 }
1028}
1029
1030impl IntoMaybeUndefined<String> for &mut str {
1031 fn into_maybe_undefined(self) -> MaybeUndefined<String> {
1032 MaybeUndefined::Value(self.into())
1033 }
1034}
1035
1036impl IntoMaybeUndefined<String> for &String {
1037 fn into_maybe_undefined(self) -> MaybeUndefined<String> {
1038 MaybeUndefined::Value(self.into())
1039 }
1040}
1041
1042impl IntoMaybeUndefined<String> for Box<str> {
1043 fn into_maybe_undefined(self) -> MaybeUndefined<String> {
1044 MaybeUndefined::Value(self.into())
1045 }
1046}
1047
1048impl IntoMaybeUndefined<String> for Cow<'_, str> {
1049 fn into_maybe_undefined(self) -> MaybeUndefined<String> {
1050 MaybeUndefined::Value(self.into())
1051 }
1052}
1053
1054impl IntoMaybeUndefined<String> for Arc<str> {
1055 fn into_maybe_undefined(self) -> MaybeUndefined<String> {
1056 MaybeUndefined::Value(self.to_string())
1057 }
1058}
1059
1060impl<T: ?Sized + AsRef<OsStr>> IntoMaybeUndefined<PathBuf> for &T {
1061 fn into_maybe_undefined(self) -> MaybeUndefined<PathBuf> {
1062 MaybeUndefined::Value(self.into())
1063 }
1064}
1065
1066impl IntoMaybeUndefined<PathBuf> for Box<Path> {
1067 fn into_maybe_undefined(self) -> MaybeUndefined<PathBuf> {
1068 MaybeUndefined::Value(self.into())
1069 }
1070}
1071
1072impl IntoMaybeUndefined<PathBuf> for Cow<'_, Path> {
1073 fn into_maybe_undefined(self) -> MaybeUndefined<PathBuf> {
1074 MaybeUndefined::Value(self.into())
1075 }
1076}
1077
1078impl IntoMaybeUndefined<serde_json::Value> for &str {
1079 fn into_maybe_undefined(self) -> MaybeUndefined<serde_json::Value> {
1080 MaybeUndefined::Value(self.into())
1081 }
1082}
1083
1084impl IntoMaybeUndefined<serde_json::Value> for String {
1085 fn into_maybe_undefined(self) -> MaybeUndefined<serde_json::Value> {
1086 MaybeUndefined::Value(self.into())
1087 }
1088}
1089
1090impl IntoMaybeUndefined<serde_json::Value> for Cow<'_, str> {
1091 fn into_maybe_undefined(self) -> MaybeUndefined<serde_json::Value> {
1092 MaybeUndefined::Value(self.into())
1093 }
1094}
1095
1096#[cfg(test)]
1097mod tests {
1098 use serde::{Deserialize, Serialize};
1099 use serde_json::{from_value, json, to_value};
1100
1101 use super::*;
1102
1103 #[test]
1104 fn test_maybe_undefined_serde() {
1105 #[derive(Serialize, Deserialize, Eq, PartialEq, Debug)]
1106 struct A {
1107 #[serde(default, skip_serializing_if = "MaybeUndefined::is_undefined")]
1108 a: MaybeUndefined<i32>,
1109 }
1110
1111 assert_eq!(to_value(MaybeUndefined::Value(100i32)).unwrap(), json!(100));
1112
1113 assert_eq!(
1114 from_value::<MaybeUndefined<i32>>(json!(100)).unwrap(),
1115 MaybeUndefined::Value(100)
1116 );
1117 assert_eq!(
1118 from_value::<MaybeUndefined<i32>>(json!(null)).unwrap(),
1119 MaybeUndefined::Null
1120 );
1121
1122 assert_eq!(
1123 to_value(&A {
1124 a: MaybeUndefined::Value(100i32)
1125 })
1126 .unwrap(),
1127 json!({"a": 100})
1128 );
1129
1130 assert_eq!(
1131 to_value(&A {
1132 a: MaybeUndefined::Null,
1133 })
1134 .unwrap(),
1135 json!({ "a": null })
1136 );
1137
1138 assert_eq!(
1139 to_value(&A {
1140 a: MaybeUndefined::Undefined,
1141 })
1142 .unwrap(),
1143 json!({})
1144 );
1145
1146 assert_eq!(
1147 from_value::<A>(json!({"a": 100})).unwrap(),
1148 A {
1149 a: MaybeUndefined::Value(100i32)
1150 }
1151 );
1152
1153 assert_eq!(
1154 from_value::<A>(json!({ "a": null })).unwrap(),
1155 A {
1156 a: MaybeUndefined::Null
1157 }
1158 );
1159
1160 assert_eq!(
1161 from_value::<A>(json!({})).unwrap(),
1162 A {
1163 a: MaybeUndefined::Undefined
1164 }
1165 );
1166 }
1167
1168 #[test]
1169 fn test_maybe_undefined_to_nested_option() {
1170 assert_eq!(Option::<Option<i32>>::from(MaybeUndefined::Undefined), None);
1171
1172 assert_eq!(
1173 Option::<Option<i32>>::from(MaybeUndefined::Null),
1174 Some(None)
1175 );
1176
1177 assert_eq!(
1178 Option::<Option<i32>>::from(MaybeUndefined::Value(42)),
1179 Some(Some(42))
1180 );
1181 }
1182
1183 #[test]
1184 fn test_as_opt_ref() {
1185 let value = MaybeUndefined::<String>::Undefined;
1186 let r = value.as_opt_ref();
1187 assert_eq!(r, None);
1188
1189 let value = MaybeUndefined::<String>::Null;
1190 let r = value.as_opt_ref();
1191 assert_eq!(r, Some(None));
1192
1193 let value = MaybeUndefined::<String>::Value("abc".to_string());
1194 let r = value.as_opt_ref();
1195 assert_eq!(r, Some(Some(&"abc".to_string())));
1196 }
1197
1198 #[test]
1199 fn test_as_opt_deref() {
1200 let value = MaybeUndefined::<String>::Undefined;
1201 let r = value.as_opt_deref();
1202 assert_eq!(r, None);
1203
1204 let value = MaybeUndefined::<String>::Null;
1205 let r = value.as_opt_deref();
1206 assert_eq!(r, Some(None));
1207
1208 let value = MaybeUndefined::<String>::Value("abc".to_string());
1209 let r = value.as_opt_deref();
1210 assert_eq!(r, Some(Some("abc")));
1211 }
1212
1213 #[test]
1214 fn test_contains_value() {
1215 let test = "abc";
1216
1217 let mut value: MaybeUndefined<String> = MaybeUndefined::Undefined;
1218 assert!(!value.contains_value(&test));
1219
1220 value = MaybeUndefined::Null;
1221 assert!(!value.contains_value(&test));
1222
1223 value = MaybeUndefined::Value("abc".to_string());
1224 assert!(value.contains_value(&test));
1225 }
1226
1227 #[test]
1228 fn test_contains() {
1229 let test = Some("abc");
1230 let none: Option<&str> = None;
1231
1232 let mut value: MaybeUndefined<String> = MaybeUndefined::Undefined;
1233 assert!(!value.contains(test.as_ref()));
1234 assert!(!value.contains(none.as_ref()));
1235
1236 value = MaybeUndefined::Null;
1237 assert!(!value.contains(test.as_ref()));
1238 assert!(value.contains(none.as_ref()));
1239
1240 value = MaybeUndefined::Value("abc".to_string());
1241 assert!(value.contains(test.as_ref()));
1242 assert!(!value.contains(none.as_ref()));
1243 }
1244
1245 #[test]
1246 fn test_map_value() {
1247 let mut value: MaybeUndefined<i32> = MaybeUndefined::Undefined;
1248 assert_eq!(value.map_value(|v| v > 2), MaybeUndefined::Undefined);
1249
1250 value = MaybeUndefined::Null;
1251 assert_eq!(value.map_value(|v| v > 2), MaybeUndefined::Null);
1252
1253 value = MaybeUndefined::Value(5);
1254 assert_eq!(value.map_value(|v| v > 2), MaybeUndefined::Value(true));
1255 }
1256
1257 #[test]
1258 fn test_map() {
1259 let mut value: MaybeUndefined<i32> = MaybeUndefined::Undefined;
1260 assert_eq!(value.map(|v| Some(v.is_some())), MaybeUndefined::Undefined);
1261
1262 value = MaybeUndefined::Null;
1263 assert_eq!(
1264 value.map(|v| Some(v.is_some())),
1265 MaybeUndefined::Value(false)
1266 );
1267
1268 value = MaybeUndefined::Value(5);
1269 assert_eq!(
1270 value.map(|v| Some(v.is_some())),
1271 MaybeUndefined::Value(true)
1272 );
1273 }
1274
1275 #[test]
1276 fn test_transpose() {
1277 let mut value: MaybeUndefined<Result<i32, &'static str>> = MaybeUndefined::Undefined;
1278 assert_eq!(value.transpose(), Ok(MaybeUndefined::Undefined));
1279
1280 value = MaybeUndefined::Null;
1281 assert_eq!(value.transpose(), Ok(MaybeUndefined::Null));
1282
1283 value = MaybeUndefined::Value(Ok(5));
1284 assert_eq!(value.transpose(), Ok(MaybeUndefined::Value(5)));
1285
1286 value = MaybeUndefined::Value(Err("error"));
1287 assert_eq!(value.transpose(), Err("error"));
1288 }
1289}