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 (
41 $(#[$attribute:meta])*
42 $visibility:vis struct $payload:ident {
43 $(
44 $(#[$field_attribute:meta])*
45 $field_visibility:vis $field:ident: $field_type:ty
46 ),* $(,)?
47 }
48 ) => {
49 $(#[$attribute])*
50 $visibility struct $payload {
51 $(
52 $(#[$field_attribute])*
53 $field_visibility $field: $field_type,
54 )*
55 }
56
57 impl<'de> serde::Deserialize<'de> for $payload {
58 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
59 $(#[$attribute])*
60 #[derive(serde::Deserialize)]
61 struct Wire {
62 $(
63 $(#[$field_attribute])*
64 $field_visibility $field: $field_type,
65 )*
66 }
67
68 struct NonNull;
69
70 impl<'de> serde_with::DeserializeAs<'de, $payload> for NonNull {
71 fn deserialize_as<D: serde::Deserializer<'de>>(
72 deserializer: D,
73 ) -> Result<$payload, D::Error> {
74 let wire = <Wire as serde::Deserialize>::deserialize(deserializer)?;
75 Ok($payload {
76 $($field: wire.$field,)*
77 })
78 }
79 }
80
81 <serde_with::DefaultOnNull<NonNull> as serde_with::DeserializeAs<
82 'de,
83 Self,
84 >>::deserialize_as(deserializer)
85 }
86 }
87 };
88}
89
90pub(crate) use default_on_null;
91
92#[cfg(test)]
93mod default_on_null_tests {
94 use serde::{Deserialize, Serialize, de::DeserializeOwned};
95 use serde_json::{Value, json};
96
97 use crate::{
98 MaybeUndefined,
99 rpc::{JsonRpcMessage, Request, Response},
100 v1::{
101 self, LoadSessionResponse, NewSessionResponse, PromptResponse, ReadTextFileResponse,
102 RequestPermissionResponse, WaitForTerminalExitResponse, WriteTextFileResponse,
103 },
104 };
105
106 macro_rules! for_each_defaultable_payload {
110 ($check:ident) => {
111 $check::<v1::AuthenticateResponse>();
112 $check::<v1::LogoutRequest>();
113 $check::<v1::LogoutResponse>();
114 $check::<v1::LoadSessionResponse>();
115 $check::<v1::ResumeSessionResponse>();
116 $check::<v1::CloseSessionResponse>();
117 $check::<v1::ListSessionsRequest>();
118 $check::<v1::DeleteSessionResponse>();
119 $check::<v1::SetSessionModeResponse>();
120 $check::<v1::WriteTextFileResponse>();
121 $check::<v1::ReleaseTerminalResponse>();
122 $check::<v1::KillTerminalResponse>();
123 $check::<v1::WaitForTerminalExitResponse>();
124
125 #[cfg(feature = "unstable_llm_providers")]
126 {
127 $check::<v1::ListProvidersRequest>();
128 $check::<v1::SetProviderResponse>();
129 $check::<v1::DisableProviderResponse>();
130 }
131 #[cfg(feature = "unstable_nes")]
132 {
133 $check::<v1::StartNesRequest>();
134 $check::<v1::CloseNesResponse>();
135 }
136
137 #[cfg(feature = "unstable_protocol_v2")]
138 {
139 use crate::v2;
140
141 $check::<v2::LoginAuthResponse>();
142 $check::<v2::LogoutAuthRequest>();
143 $check::<v2::LogoutAuthResponse>();
144 $check::<v2::ResumeSessionResponse>();
145 $check::<v2::CloseSessionResponse>();
146 $check::<v2::ListSessionsRequest>();
147 $check::<v2::DeleteSessionResponse>();
148
149 #[cfg(feature = "unstable_llm_providers")]
150 {
151 $check::<v2::ListProvidersRequest>();
152 $check::<v2::SetProviderResponse>();
153 $check::<v2::DisableProviderResponse>();
154 }
155 #[cfg(feature = "unstable_nes")]
156 {
157 $check::<v2::StartNesRequest>();
158 $check::<v2::CloseNesResponse>();
159 }
160 }
161 };
162 }
163
164 fn assert_defaultable_payload<T>()
165 where
166 T: Default + DeserializeOwned + Serialize + PartialEq + std::fmt::Debug,
167 {
168 let name = std::any::type_name::<T>();
169 for value in [Value::Null, json!({})] {
170 assert_eq!(
171 serde_json::from_value::<T>(value).unwrap(),
172 T::default(),
173 "{name}",
174 );
175 }
176 assert_eq!(
177 serde_json::from_str::<T>("null").unwrap(),
178 T::default(),
179 "{name}",
180 );
181 assert_eq!(
182 serde_json::to_value(T::default()).unwrap(),
183 json!({}),
184 "{name}"
185 );
186
187 let metadata = json!({"_meta": {"example.com/key": ["preserve", 1, null]}});
188 let payload: T = serde_json::from_value(metadata.clone()).unwrap();
189 assert_eq!(serde_json::to_value(payload).unwrap(), metadata, "{name}");
190
191 for value in [json!(false), json!(42), json!("invalid")] {
192 assert!(serde_json::from_value::<T>(value).is_err(), "{name}");
193 }
194
195 assert_eq!(
197 serde_json::from_value::<Option<T>>(Value::Null).unwrap(),
198 None,
199 "{name}",
200 );
201 assert_eq!(
202 serde_json::from_value::<MaybeUndefined<T>>(Value::Null).unwrap(),
203 MaybeUndefined::Null,
204 "{name}",
205 );
206 }
207
208 #[test]
209 fn defaultable_payloads_accept_null_without_losing_information() {
210 for_each_defaultable_payload!(assert_defaultable_payload);
211 }
212
213 #[test]
214 fn inherent_methods_do_not_bypass_null_handling() {
215 assert_eq!(
217 WriteTextFileResponse::deserialize(Value::Null).unwrap(),
218 WriteTextFileResponse::default(),
219 );
220 assert_eq!(
221 LoadSessionResponse::deserialize(Value::Null).unwrap(),
222 LoadSessionResponse::default(),
223 );
224 }
225
226 #[test]
227 fn optional_payload_fields_are_preserved() {
228 let load = json!({
229 "modes": {
230 "currentModeId": "ask",
231 "availableModes": [{"id": "ask", "name": "Ask"}]
232 },
233 "configOptions": [],
234 "_meta": {"example.com/key": "value"}
235 });
236 let response: LoadSessionResponse = serde_json::from_value(load.clone()).unwrap();
237 assert_eq!(serde_json::to_value(response).unwrap(), load);
238
239 let list = json!({"cwd": "/workspace", "cursor": "next-page"});
240 let request: v1::ListSessionsRequest = serde_json::from_value(list.clone()).unwrap();
241 assert_eq!(serde_json::to_value(request).unwrap(), list);
242
243 #[cfg(feature = "unstable_protocol_v2")]
244 {
245 let request: crate::v2::ListSessionsRequest =
246 serde_json::from_value(list.clone()).unwrap();
247 assert_eq!(serde_json::to_value(request).unwrap(), list);
248 }
249 }
250
251 #[test]
252 fn default_terminal_exit_status_is_unknown_not_success() {
253 let response: WaitForTerminalExitResponse = serde_json::from_value(Value::Null).unwrap();
254 assert_eq!(response.exit_status.exit_code, None);
255 assert_eq!(response.exit_status.signal, None);
256 assert_eq!(response, WaitForTerminalExitResponse::default());
257
258 for value in [
259 json!({"exitCode": 0}),
260 json!({"exitCode": 17}),
261 json!({"signal": "SIGTERM"}),
262 ] {
263 let response: WaitForTerminalExitResponse =
264 serde_json::from_value(value.clone()).unwrap();
265 assert_eq!(serde_json::to_value(response).unwrap(), value);
266 }
267 }
268
269 #[test]
270 fn non_null_payloads_keep_the_derived_deserialization_behavior() {
271 super::default_on_null! {
274 #[derive(Default, Debug, Serialize, PartialEq)]
275 struct RequiredField {
276 count: u32,
277 }
278 }
279 #[derive(Deserialize)]
280 struct BaselineWrite {
281 #[serde(
282 default,
283 rename = "_meta",
284 with = "serde_with::As::<serde_with::DefaultOnError>"
285 )]
286 meta: Option<serde_json::Map<String, Value>>,
287 }
288
289 assert_eq!(
290 serde_json::from_value::<RequiredField>(Value::Null).unwrap(),
291 RequiredField::default(),
292 );
293 assert!(serde_json::from_value::<RequiredField>(json!({})).is_err());
294 assert!(serde_json::from_value::<RequiredField>(json!({"count": "invalid"})).is_err());
295
296 for value in [
297 json!({}),
298 json!({"_meta": {"example.com/key": true}}),
299 json!({"_meta": 42}),
300 json!({"modes": "invalid", "configOptions": "invalid"}),
301 json!([]),
302 json!([null]),
303 json!(false),
304 json!(42),
305 json!("invalid"),
306 ] {
307 assert_eq!(
308 serde_json::from_value::<WriteTextFileResponse>(value.clone())
309 .map(|response| response.meta)
310 .map_err(|_| ()),
311 serde_json::from_value::<BaselineWrite>(value)
312 .map(|response| response.meta)
313 .map_err(|_| ()),
314 );
315 }
316 }
317
318 #[test]
319 fn payloads_with_required_fields_still_reject_null() {
320 assert!(serde_json::from_value::<ReadTextFileResponse>(Value::Null).is_err());
321 assert!(serde_json::from_value::<RequestPermissionResponse>(Value::Null).is_err());
322 assert!(serde_json::from_value::<NewSessionResponse>(Value::Null).is_err());
323 assert!(serde_json::from_value::<PromptResponse>(Value::Null).is_err());
324 assert!(serde_json::from_value::<v1::InitializeResponse>(Value::Null).is_err());
325 assert!(serde_json::from_value::<v1::CreateTerminalResponse>(Value::Null).is_err());
326 assert!(serde_json::from_value::<v1::TerminalOutputResponse>(Value::Null).is_err());
327 assert!(serde_json::from_value::<v1::CreateElicitationResponse>(Value::Null).is_err());
328
329 #[cfg(feature = "unstable_protocol_v2")]
330 {
331 use crate::v2;
332
333 assert!(serde_json::from_value::<v2::InitializeResponse>(Value::Null).is_err());
334 assert!(serde_json::from_value::<v2::NewSessionResponse>(Value::Null).is_err());
335 assert!(serde_json::from_value::<v2::PromptResponse>(Value::Null).is_err());
336 assert!(serde_json::from_value::<v2::RequestPermissionResponse>(Value::Null).is_err());
337 assert!(serde_json::from_value::<v2::CreateElicitationResponse>(Value::Null).is_err());
338 }
339 }
340
341 #[test]
342 fn raw_response_nulls_are_not_rewritten() {
343 let extension: v1::ExtResponse = serde_json::from_value(Value::Null).unwrap();
344 assert_eq!(serde_json::to_value(extension).unwrap(), Value::Null);
345
346 #[cfg(feature = "unstable_mcp_over_acp")]
347 {
348 let mcp: v1::MessageMcpResponse =
349 serde_json::from_value(json!({"result": null})).unwrap();
350 assert_eq!(serde_json::to_value(mcp).unwrap(), json!({"result": null}));
351 }
352
353 #[cfg(feature = "unstable_protocol_v2")]
354 {
355 let extension: crate::v2::ExtResponse = serde_json::from_value(Value::Null).unwrap();
356 assert_eq!(serde_json::to_value(extension).unwrap(), Value::Null);
357
358 #[cfg(feature = "unstable_mcp_over_acp")]
359 {
360 let mcp: crate::v2::MessageMcpResponse =
361 serde_json::from_value(json!({"result": null})).unwrap();
362 assert_eq!(serde_json::to_value(mcp).unwrap(), json!({"result": null}));
363 }
364 }
365 }
366
367 #[test]
368 fn optional_request_parameters_keep_their_existing_meaning() {
369 type LogoutRequest = Request<v1::LogoutRequest>;
370 for value in [
371 json!({"id": 1, "method": "logout"}),
372 json!({"id": 1, "method": "logout", "params": null}),
373 ] {
374 let request: LogoutRequest = serde_json::from_value(value).unwrap();
375 assert_eq!(request.params, None);
376 }
377 let request: LogoutRequest =
378 serde_json::from_value(json!({"id": 1, "method": "logout", "params": {}})).unwrap();
379 assert_eq!(request.params, Some(v1::LogoutRequest::default()));
380 }
381
382 #[test]
383 fn nullable_fields_keep_their_existing_meaning() {
384 #[derive(Debug, Deserialize, PartialEq)]
385 struct Container {
386 optional: Option<WriteTextFileResponse>,
387 #[serde(default)]
388 patch: MaybeUndefined<WriteTextFileResponse>,
389 }
390
391 assert_eq!(
392 serde_json::from_value::<Option<WriteTextFileResponse>>(Value::Null).unwrap(),
393 None,
394 );
395 assert_eq!(
396 serde_json::from_value::<MaybeUndefined<WriteTextFileResponse>>(Value::Null).unwrap(),
397 MaybeUndefined::Null,
398 );
399 assert_eq!(
400 serde_json::from_value::<Container>(json!({})).unwrap(),
401 Container {
402 optional: None,
403 patch: MaybeUndefined::Undefined,
404 },
405 );
406 assert_eq!(
407 serde_json::from_value::<Container>(json!({"optional": null, "patch": null})).unwrap(),
408 Container {
409 optional: None,
410 patch: MaybeUndefined::Null,
411 },
412 );
413 assert_eq!(
414 serde_json::from_value::<Container>(json!({"optional": {}, "patch": {}})).unwrap(),
415 Container {
416 optional: Some(WriteTextFileResponse::default()),
417 patch: MaybeUndefined::Value(WriteTextFileResponse::default()),
418 },
419 );
420 }
421
422 #[test]
423 fn response_result_is_required_even_when_its_payload_accepts_null() {
424 type WriteResponse = Response<WriteTextFileResponse, Value>;
425 let response: WriteResponse =
426 serde_json::from_value(json!({"id": 1, "result": null})).unwrap();
427 assert_eq!(
428 response,
429 Response::new(1, Ok(WriteTextFileResponse::default())),
430 );
431 assert!(serde_json::from_value::<WriteResponse>(json!({"id": 1})).is_err());
432 assert!(
433 serde_json::from_value::<JsonRpcMessage<WriteResponse>>(
434 json!({"jsonrpc": "2.0", "id": 1})
435 )
436 .is_err()
437 );
438
439 let error = json!({"code": -32603, "message": "Internal error"});
440 let response: JsonRpcMessage<WriteResponse> = serde_json::from_value(json!({
441 "jsonrpc": "2.0",
442 "id": 1,
443 "error": error.clone()
444 }))
445 .unwrap();
446 assert_eq!(response.into_inner(), Response::new(1, Err(error)));
447 }
448
449 #[cfg(feature = "schemars")]
450 #[test]
451 fn defaultable_payload_schemas_still_require_objects() {
452 fn assert_object_schema<T: schemars::JsonSchema>() {
453 let schema = serde_json::to_value(schemars::schema_for!(T)).unwrap();
454 assert_eq!(schema["type"], "object");
455 assert!(schema.get("anyOf").is_none());
456 }
457 for_each_defaultable_payload!(assert_object_schema);
458 }
459}
460
461pub trait IntoOption<T> {
467 fn into_option(self) -> Option<T>;
469}
470
471impl<T> IntoOption<T> for Option<T> {
472 fn into_option(self) -> Option<T> {
473 self
474 }
475}
476
477impl<T> IntoOption<T> for T {
478 fn into_option(self) -> Option<T> {
479 Some(self)
480 }
481}
482
483impl IntoOption<String> for &str {
484 fn into_option(self) -> Option<String> {
485 Some(self.into())
486 }
487}
488
489impl IntoOption<String> for &mut str {
490 fn into_option(self) -> Option<String> {
491 Some(self.into())
492 }
493}
494
495impl IntoOption<String> for &String {
496 fn into_option(self) -> Option<String> {
497 Some(self.into())
498 }
499}
500
501impl IntoOption<String> for Box<str> {
502 fn into_option(self) -> Option<String> {
503 Some(self.into())
504 }
505}
506
507impl IntoOption<String> for Cow<'_, str> {
508 fn into_option(self) -> Option<String> {
509 Some(self.into())
510 }
511}
512
513impl IntoOption<String> for Arc<str> {
514 fn into_option(self) -> Option<String> {
515 Some(self.to_string())
516 }
517}
518
519impl<T: ?Sized + AsRef<OsStr>> IntoOption<PathBuf> for &T {
520 fn into_option(self) -> Option<PathBuf> {
521 Some(self.into())
522 }
523}
524
525impl IntoOption<PathBuf> for Box<Path> {
526 fn into_option(self) -> Option<PathBuf> {
527 Some(self.into())
528 }
529}
530
531impl IntoOption<PathBuf> for Cow<'_, Path> {
532 fn into_option(self) -> Option<PathBuf> {
533 Some(self.into())
534 }
535}
536
537impl IntoOption<serde_json::Value> for &str {
538 fn into_option(self) -> Option<serde_json::Value> {
539 Some(self.into())
540 }
541}
542
543impl IntoOption<serde_json::Value> for String {
544 fn into_option(self) -> Option<serde_json::Value> {
545 Some(self.into())
546 }
547}
548
549impl IntoOption<serde_json::Value> for Cow<'_, str> {
550 fn into_option(self) -> Option<serde_json::Value> {
551 Some(self.into())
552 }
553}
554
555#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
575#[derive(Copy, Clone, Default, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
576#[cfg_attr(feature = "schemars", schemars(with = "Option<Option<T>>", inline))]
577#[expect(clippy::exhaustive_enums)]
578pub enum MaybeUndefined<T> {
579 #[default]
581 Undefined,
582 Null,
584 Value(T),
586}
587
588impl<T> MaybeUndefined<T> {
589 #[inline]
591 pub const fn is_undefined(&self) -> bool {
592 matches!(self, MaybeUndefined::Undefined)
593 }
594
595 #[inline]
597 pub const fn is_null(&self) -> bool {
598 matches!(self, MaybeUndefined::Null)
599 }
600
601 #[inline]
603 pub const fn is_value(&self) -> bool {
604 matches!(self, MaybeUndefined::Value(_))
605 }
606
607 #[inline]
610 pub const fn value(&self) -> Option<&T> {
611 match self {
612 MaybeUndefined::Value(value) => Some(value),
613 _ => None,
614 }
615 }
616
617 #[inline]
619 pub fn take(self) -> Option<T> {
620 match self {
621 MaybeUndefined::Value(value) => Some(value),
622 _ => None,
623 }
624 }
625
626 #[inline]
628 pub const fn as_opt_ref(&self) -> Option<Option<&T>> {
629 match self {
630 MaybeUndefined::Undefined => None,
631 MaybeUndefined::Null => Some(None),
632 MaybeUndefined::Value(value) => Some(Some(value)),
633 }
634 }
635
636 #[inline]
638 pub fn as_opt_deref<U>(&self) -> Option<Option<&U>>
639 where
640 U: ?Sized,
641 T: Deref<Target = U>,
642 {
643 match self {
644 MaybeUndefined::Undefined => None,
645 MaybeUndefined::Null => Some(None),
646 MaybeUndefined::Value(value) => Some(Some(&**value)),
647 }
648 }
649
650 #[inline]
652 pub fn contains_value<U>(&self, x: &U) -> bool
653 where
654 U: PartialEq<T>,
655 {
656 match self {
657 MaybeUndefined::Value(y) => x == y,
658 _ => false,
659 }
660 }
661
662 #[inline]
665 pub fn contains<U>(&self, x: Option<&U>) -> bool
666 where
667 U: PartialEq<T>,
668 {
669 match self {
670 MaybeUndefined::Value(y) => matches!(x, Some(v) if v == y),
671 MaybeUndefined::Null => x.is_none(),
672 MaybeUndefined::Undefined => false,
673 }
674 }
675
676 #[inline]
679 pub fn map<U, F: FnOnce(Option<T>) -> Option<U>>(self, f: F) -> MaybeUndefined<U> {
680 match self {
681 MaybeUndefined::Value(v) => match f(Some(v)) {
682 Some(v) => MaybeUndefined::Value(v),
683 None => MaybeUndefined::Null,
684 },
685 MaybeUndefined::Null => match f(None) {
686 Some(v) => MaybeUndefined::Value(v),
687 None => MaybeUndefined::Null,
688 },
689 MaybeUndefined::Undefined => MaybeUndefined::Undefined,
690 }
691 }
692
693 #[inline]
696 pub fn map_value<U, F: FnOnce(T) -> U>(self, f: F) -> MaybeUndefined<U> {
697 match self {
698 MaybeUndefined::Value(v) => MaybeUndefined::Value(f(v)),
699 MaybeUndefined::Null => MaybeUndefined::Null,
700 MaybeUndefined::Undefined => MaybeUndefined::Undefined,
701 }
702 }
703
704 pub fn update_to(self, value: &mut Option<T>) {
723 match self {
724 MaybeUndefined::Value(new) => *value = Some(new),
725 MaybeUndefined::Null => *value = None,
726 MaybeUndefined::Undefined => {}
727 }
728 }
729}
730
731impl<T, E> MaybeUndefined<Result<T, E>> {
732 #[inline]
746 pub fn transpose(self) -> Result<MaybeUndefined<T>, E> {
747 match self {
748 MaybeUndefined::Undefined => Ok(MaybeUndefined::Undefined),
749 MaybeUndefined::Null => Ok(MaybeUndefined::Null),
750 MaybeUndefined::Value(Ok(v)) => Ok(MaybeUndefined::Value(v)),
751 MaybeUndefined::Value(Err(e)) => Err(e),
752 }
753 }
754}
755
756impl<T: Serialize> Serialize for MaybeUndefined<T> {
757 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
758 match self {
759 MaybeUndefined::Value(value) => value.serialize(serializer),
760 MaybeUndefined::Null => serializer.serialize_none(),
761 MaybeUndefined::Undefined => serializer.serialize_unit(),
762 }
763 }
764}
765
766impl<'de, T> Deserialize<'de> for MaybeUndefined<T>
767where
768 T: Deserialize<'de>,
769{
770 fn deserialize<D>(deserializer: D) -> Result<MaybeUndefined<T>, D::Error>
771 where
772 D: Deserializer<'de>,
773 {
774 Option::<T>::deserialize(deserializer).map(|value| match value {
775 Some(value) => MaybeUndefined::Value(value),
776 None => MaybeUndefined::Null,
777 })
778 }
779}
780
781impl<T> From<MaybeUndefined<T>> for Option<Option<T>> {
782 fn from(maybe_undefined: MaybeUndefined<T>) -> Self {
783 match maybe_undefined {
784 MaybeUndefined::Undefined => None,
785 MaybeUndefined::Null => Some(None),
786 MaybeUndefined::Value(value) => Some(Some(value)),
787 }
788 }
789}
790
791impl<T> From<Option<Option<T>>> for MaybeUndefined<T> {
792 fn from(value: Option<Option<T>>) -> Self {
793 match value {
794 Some(Some(value)) => Self::Value(value),
795 Some(None) => Self::Null,
796 None => Self::Undefined,
797 }
798 }
799}
800
801impl<'de, T, TAs> DeserializeAs<'de, MaybeUndefined<T>> for MaybeUndefined<TAs>
802where
803 TAs: DeserializeAs<'de, T>,
804{
805 fn deserialize_as<D>(deserializer: D) -> Result<MaybeUndefined<T>, D::Error>
806 where
807 D: Deserializer<'de>,
808 {
809 Option::<DeserializeAsWrap<T, TAs>>::deserialize(deserializer).map(|value| match value {
810 Some(value) => MaybeUndefined::Value(value.into_inner()),
811 None => MaybeUndefined::Null,
812 })
813 }
814}
815
816pub trait IntoMaybeUndefined<T> {
820 fn into_maybe_undefined(self) -> MaybeUndefined<T>;
822}
823
824impl<T> IntoMaybeUndefined<T> for T {
825 fn into_maybe_undefined(self) -> MaybeUndefined<T> {
826 MaybeUndefined::Value(self)
827 }
828}
829
830impl<T> IntoMaybeUndefined<T> for Option<T> {
831 fn into_maybe_undefined(self) -> MaybeUndefined<T> {
832 match self {
833 Some(value) => MaybeUndefined::Value(value),
834 None => MaybeUndefined::Null,
835 }
836 }
837}
838
839impl<T> IntoMaybeUndefined<T> for MaybeUndefined<T> {
840 fn into_maybe_undefined(self) -> MaybeUndefined<T> {
841 self
842 }
843}
844
845impl IntoMaybeUndefined<String> for &str {
846 fn into_maybe_undefined(self) -> MaybeUndefined<String> {
847 MaybeUndefined::Value(self.into())
848 }
849}
850
851impl IntoMaybeUndefined<String> for &mut str {
852 fn into_maybe_undefined(self) -> MaybeUndefined<String> {
853 MaybeUndefined::Value(self.into())
854 }
855}
856
857impl IntoMaybeUndefined<String> for &String {
858 fn into_maybe_undefined(self) -> MaybeUndefined<String> {
859 MaybeUndefined::Value(self.into())
860 }
861}
862
863impl IntoMaybeUndefined<String> for Box<str> {
864 fn into_maybe_undefined(self) -> MaybeUndefined<String> {
865 MaybeUndefined::Value(self.into())
866 }
867}
868
869impl IntoMaybeUndefined<String> for Cow<'_, str> {
870 fn into_maybe_undefined(self) -> MaybeUndefined<String> {
871 MaybeUndefined::Value(self.into())
872 }
873}
874
875impl IntoMaybeUndefined<String> for Arc<str> {
876 fn into_maybe_undefined(self) -> MaybeUndefined<String> {
877 MaybeUndefined::Value(self.to_string())
878 }
879}
880
881impl<T: ?Sized + AsRef<OsStr>> IntoMaybeUndefined<PathBuf> for &T {
882 fn into_maybe_undefined(self) -> MaybeUndefined<PathBuf> {
883 MaybeUndefined::Value(self.into())
884 }
885}
886
887impl IntoMaybeUndefined<PathBuf> for Box<Path> {
888 fn into_maybe_undefined(self) -> MaybeUndefined<PathBuf> {
889 MaybeUndefined::Value(self.into())
890 }
891}
892
893impl IntoMaybeUndefined<PathBuf> for Cow<'_, Path> {
894 fn into_maybe_undefined(self) -> MaybeUndefined<PathBuf> {
895 MaybeUndefined::Value(self.into())
896 }
897}
898
899impl IntoMaybeUndefined<serde_json::Value> for &str {
900 fn into_maybe_undefined(self) -> MaybeUndefined<serde_json::Value> {
901 MaybeUndefined::Value(self.into())
902 }
903}
904
905impl IntoMaybeUndefined<serde_json::Value> for String {
906 fn into_maybe_undefined(self) -> MaybeUndefined<serde_json::Value> {
907 MaybeUndefined::Value(self.into())
908 }
909}
910
911impl IntoMaybeUndefined<serde_json::Value> for Cow<'_, str> {
912 fn into_maybe_undefined(self) -> MaybeUndefined<serde_json::Value> {
913 MaybeUndefined::Value(self.into())
914 }
915}
916
917#[cfg(test)]
918mod tests {
919 use serde::{Deserialize, Serialize};
920 use serde_json::{from_value, json, to_value};
921
922 use super::*;
923
924 #[test]
925 fn test_maybe_undefined_serde() {
926 #[derive(Serialize, Deserialize, Eq, PartialEq, Debug)]
927 struct A {
928 #[serde(default, skip_serializing_if = "MaybeUndefined::is_undefined")]
929 a: MaybeUndefined<i32>,
930 }
931
932 assert_eq!(to_value(MaybeUndefined::Value(100i32)).unwrap(), json!(100));
933
934 assert_eq!(
935 from_value::<MaybeUndefined<i32>>(json!(100)).unwrap(),
936 MaybeUndefined::Value(100)
937 );
938 assert_eq!(
939 from_value::<MaybeUndefined<i32>>(json!(null)).unwrap(),
940 MaybeUndefined::Null
941 );
942
943 assert_eq!(
944 to_value(&A {
945 a: MaybeUndefined::Value(100i32)
946 })
947 .unwrap(),
948 json!({"a": 100})
949 );
950
951 assert_eq!(
952 to_value(&A {
953 a: MaybeUndefined::Null,
954 })
955 .unwrap(),
956 json!({ "a": null })
957 );
958
959 assert_eq!(
960 to_value(&A {
961 a: MaybeUndefined::Undefined,
962 })
963 .unwrap(),
964 json!({})
965 );
966
967 assert_eq!(
968 from_value::<A>(json!({"a": 100})).unwrap(),
969 A {
970 a: MaybeUndefined::Value(100i32)
971 }
972 );
973
974 assert_eq!(
975 from_value::<A>(json!({ "a": null })).unwrap(),
976 A {
977 a: MaybeUndefined::Null
978 }
979 );
980
981 assert_eq!(
982 from_value::<A>(json!({})).unwrap(),
983 A {
984 a: MaybeUndefined::Undefined
985 }
986 );
987 }
988
989 #[test]
990 fn test_maybe_undefined_to_nested_option() {
991 assert_eq!(Option::<Option<i32>>::from(MaybeUndefined::Undefined), None);
992
993 assert_eq!(
994 Option::<Option<i32>>::from(MaybeUndefined::Null),
995 Some(None)
996 );
997
998 assert_eq!(
999 Option::<Option<i32>>::from(MaybeUndefined::Value(42)),
1000 Some(Some(42))
1001 );
1002 }
1003
1004 #[test]
1005 fn test_as_opt_ref() {
1006 let value = MaybeUndefined::<String>::Undefined;
1007 let r = value.as_opt_ref();
1008 assert_eq!(r, None);
1009
1010 let value = MaybeUndefined::<String>::Null;
1011 let r = value.as_opt_ref();
1012 assert_eq!(r, Some(None));
1013
1014 let value = MaybeUndefined::<String>::Value("abc".to_string());
1015 let r = value.as_opt_ref();
1016 assert_eq!(r, Some(Some(&"abc".to_string())));
1017 }
1018
1019 #[test]
1020 fn test_as_opt_deref() {
1021 let value = MaybeUndefined::<String>::Undefined;
1022 let r = value.as_opt_deref();
1023 assert_eq!(r, None);
1024
1025 let value = MaybeUndefined::<String>::Null;
1026 let r = value.as_opt_deref();
1027 assert_eq!(r, Some(None));
1028
1029 let value = MaybeUndefined::<String>::Value("abc".to_string());
1030 let r = value.as_opt_deref();
1031 assert_eq!(r, Some(Some("abc")));
1032 }
1033
1034 #[test]
1035 fn test_contains_value() {
1036 let test = "abc";
1037
1038 let mut value: MaybeUndefined<String> = MaybeUndefined::Undefined;
1039 assert!(!value.contains_value(&test));
1040
1041 value = MaybeUndefined::Null;
1042 assert!(!value.contains_value(&test));
1043
1044 value = MaybeUndefined::Value("abc".to_string());
1045 assert!(value.contains_value(&test));
1046 }
1047
1048 #[test]
1049 fn test_contains() {
1050 let test = Some("abc");
1051 let none: Option<&str> = None;
1052
1053 let mut value: MaybeUndefined<String> = MaybeUndefined::Undefined;
1054 assert!(!value.contains(test.as_ref()));
1055 assert!(!value.contains(none.as_ref()));
1056
1057 value = MaybeUndefined::Null;
1058 assert!(!value.contains(test.as_ref()));
1059 assert!(value.contains(none.as_ref()));
1060
1061 value = MaybeUndefined::Value("abc".to_string());
1062 assert!(value.contains(test.as_ref()));
1063 assert!(!value.contains(none.as_ref()));
1064 }
1065
1066 #[test]
1067 fn test_map_value() {
1068 let mut value: MaybeUndefined<i32> = MaybeUndefined::Undefined;
1069 assert_eq!(value.map_value(|v| v > 2), MaybeUndefined::Undefined);
1070
1071 value = MaybeUndefined::Null;
1072 assert_eq!(value.map_value(|v| v > 2), MaybeUndefined::Null);
1073
1074 value = MaybeUndefined::Value(5);
1075 assert_eq!(value.map_value(|v| v > 2), MaybeUndefined::Value(true));
1076 }
1077
1078 #[test]
1079 fn test_map() {
1080 let mut value: MaybeUndefined<i32> = MaybeUndefined::Undefined;
1081 assert_eq!(value.map(|v| Some(v.is_some())), MaybeUndefined::Undefined);
1082
1083 value = MaybeUndefined::Null;
1084 assert_eq!(
1085 value.map(|v| Some(v.is_some())),
1086 MaybeUndefined::Value(false)
1087 );
1088
1089 value = MaybeUndefined::Value(5);
1090 assert_eq!(
1091 value.map(|v| Some(v.is_some())),
1092 MaybeUndefined::Value(true)
1093 );
1094 }
1095
1096 #[test]
1097 fn test_transpose() {
1098 let mut value: MaybeUndefined<Result<i32, &'static str>> = MaybeUndefined::Undefined;
1099 assert_eq!(value.transpose(), Ok(MaybeUndefined::Undefined));
1100
1101 value = MaybeUndefined::Null;
1102 assert_eq!(value.transpose(), Ok(MaybeUndefined::Null));
1103
1104 value = MaybeUndefined::Value(Ok(5));
1105 assert_eq!(value.transpose(), Ok(MaybeUndefined::Value(5)));
1106
1107 value = MaybeUndefined::Value(Err("error"));
1108 assert_eq!(value.transpose(), Err("error"));
1109 }
1110}