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