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