1use std::{
16 borrow::Cow,
17 ffi::OsStr,
18 ops::Deref,
19 path::{Path, PathBuf},
20 sync::Arc,
21};
22
23use schemars::JsonSchema;
24use serde::{Deserialize, Deserializer, Serialize, Serializer};
25use serde_with::{DeserializeAs, de::DeserializeAsWrap};
26
27#[cfg(feature = "tracing")]
40#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
41#[non_exhaustive]
42pub struct SkipListener;
43
44#[cfg(feature = "tracing")]
45impl serde_with::InspectError for SkipListener {
46 fn inspect_error(error: impl serde::de::Error) {
47 tracing::warn!(
48 %error,
49 "skipped malformed list entry during deserialization",
50 );
51 }
52}
53
54#[cfg(not(feature = "tracing"))]
58pub type SkipListener = ();
59
60#[cfg(test)]
61mod skip_listener_tests {
62 use std::cell::Cell;
63
64 use serde::{Deserialize, Serialize};
65 use serde_json::json;
66 use serde_with::{DefaultOnError, VecSkipError, serde_as};
67
68 thread_local! {
69 static SKIP_COUNT: Cell<u32> = const { Cell::new(0) };
70 }
71
72 struct CountingListener;
74
75 impl serde_with::InspectError for CountingListener {
76 fn inspect_error(_error: impl serde::de::Error) {
77 SKIP_COUNT.with(|c| c.set(c.get() + 1));
78 }
79 }
80
81 #[serde_as]
82 #[derive(Serialize, Deserialize, Debug, PartialEq)]
83 struct Wrapper {
84 #[serde_as(deserialize_as = "VecSkipError<_, CountingListener>")]
85 values: Vec<u32>,
86 }
87
88 #[test]
89 fn inspector_runs_for_each_skipped_entry() {
90 SKIP_COUNT.with(|c| c.set(0));
91
92 let input = json!({"values": [1, "oops", 2, {}, 3]});
93 let wrapper: Wrapper = serde_json::from_value(input).unwrap();
94
95 assert_eq!(wrapper.values, vec![1, 2, 3]);
96 assert_eq!(SKIP_COUNT.with(Cell::get), 2);
97 }
98
99 #[serde_as]
104 #[derive(Deserialize, Debug, PartialEq)]
105 struct ResilientVec {
106 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, CountingListener>>")]
107 #[serde(default)]
108 values: Vec<u32>,
109 }
110
111 #[test]
112 fn resilient_vec_tolerates_missing_null_and_wrong_type() {
113 let r: ResilientVec = serde_json::from_value(json!({})).unwrap();
115 assert_eq!(r.values, Vec::<u32>::new());
116
117 let r: ResilientVec = serde_json::from_value(json!({"values": null})).unwrap();
119 assert_eq!(r.values, Vec::<u32>::new());
120
121 let r: ResilientVec = serde_json::from_value(json!({"values": "oops"})).unwrap();
123 assert_eq!(r.values, Vec::<u32>::new());
124
125 let r: ResilientVec = serde_json::from_value(json!({"values": {"k": 1}})).unwrap();
127 assert_eq!(r.values, Vec::<u32>::new());
128
129 SKIP_COUNT.with(|c| c.set(0));
131 let r: ResilientVec =
132 serde_json::from_value(json!({"values": [1, "oops", 2, {}, 3]})).unwrap();
133 assert_eq!(r.values, vec![1, 2, 3]);
134 assert_eq!(SKIP_COUNT.with(Cell::get), 2);
135 }
136
137 #[test]
138 fn resilient_vec_does_not_invoke_inspector_on_outer_failure() {
139 SKIP_COUNT.with(|c| c.set(0));
140
141 let _r: ResilientVec = serde_json::from_value(json!({"values": null})).unwrap();
144 let _r: ResilientVec = serde_json::from_value(json!({"values": "oops"})).unwrap();
145 let _r: ResilientVec = serde_json::from_value(json!({"values": {}})).unwrap();
146
147 assert_eq!(SKIP_COUNT.with(Cell::get), 0);
148 }
149
150 #[serde_as]
155 #[derive(Deserialize, Debug, PartialEq)]
156 struct ResilientOptionVec {
157 #[serde_as(deserialize_as = "DefaultOnError<Option<VecSkipError<_, CountingListener>>>")]
158 #[serde(default)]
159 values: Option<Vec<u32>>,
160 }
161
162 #[test]
163 fn resilient_option_vec_tolerates_missing_null_and_wrong_type() {
164 let r: ResilientOptionVec = serde_json::from_value(json!({})).unwrap();
166 assert_eq!(r.values, None);
167
168 let r: ResilientOptionVec = serde_json::from_value(json!({"values": null})).unwrap();
170 assert_eq!(r.values, None);
171
172 let r: ResilientOptionVec = serde_json::from_value(json!({"values": []})).unwrap();
174 assert_eq!(r.values, Some(Vec::<u32>::new()));
175
176 let r: ResilientOptionVec = serde_json::from_value(json!({"values": [1, 2, 3]})).unwrap();
178 assert_eq!(r.values, Some(vec![1, 2, 3]));
179
180 let r: ResilientOptionVec = serde_json::from_value(json!({"values": "oops"})).unwrap();
182 assert_eq!(r.values, None);
183
184 let r: ResilientOptionVec = serde_json::from_value(json!({"values": {"k": 1}})).unwrap();
186 assert_eq!(r.values, None);
187
188 SKIP_COUNT.with(|c| c.set(0));
190 let r: ResilientOptionVec =
191 serde_json::from_value(json!({"values": [1, "oops", 2, {}, 3]})).unwrap();
192 assert_eq!(r.values, Some(vec![1, 2, 3]));
193 assert_eq!(SKIP_COUNT.with(Cell::get), 2);
194 }
195}
196
197pub trait IntoOption<T> {
203 fn into_option(self) -> Option<T>;
204}
205
206impl<T> IntoOption<T> for Option<T> {
207 fn into_option(self) -> Option<T> {
208 self
209 }
210}
211
212impl<T> IntoOption<T> for T {
213 fn into_option(self) -> Option<T> {
214 Some(self)
215 }
216}
217
218impl IntoOption<String> for &str {
219 fn into_option(self) -> Option<String> {
220 Some(self.into())
221 }
222}
223
224impl IntoOption<String> for &mut str {
225 fn into_option(self) -> Option<String> {
226 Some(self.into())
227 }
228}
229
230impl IntoOption<String> for &String {
231 fn into_option(self) -> Option<String> {
232 Some(self.into())
233 }
234}
235
236impl IntoOption<String> for Box<str> {
237 fn into_option(self) -> Option<String> {
238 Some(self.into())
239 }
240}
241
242impl IntoOption<String> for Cow<'_, str> {
243 fn into_option(self) -> Option<String> {
244 Some(self.into())
245 }
246}
247
248impl IntoOption<String> for Arc<str> {
249 fn into_option(self) -> Option<String> {
250 Some(self.to_string())
251 }
252}
253
254impl<T: ?Sized + AsRef<OsStr>> IntoOption<PathBuf> for &T {
255 fn into_option(self) -> Option<PathBuf> {
256 Some(self.into())
257 }
258}
259
260impl IntoOption<PathBuf> for Box<Path> {
261 fn into_option(self) -> Option<PathBuf> {
262 Some(self.into())
263 }
264}
265
266impl IntoOption<PathBuf> for Cow<'_, Path> {
267 fn into_option(self) -> Option<PathBuf> {
268 Some(self.into())
269 }
270}
271
272impl IntoOption<serde_json::Value> for &str {
273 fn into_option(self) -> Option<serde_json::Value> {
274 Some(self.into())
275 }
276}
277
278impl IntoOption<serde_json::Value> for String {
279 fn into_option(self) -> Option<serde_json::Value> {
280 Some(self.into())
281 }
282}
283
284impl IntoOption<serde_json::Value> for Cow<'_, str> {
285 fn into_option(self) -> Option<serde_json::Value> {
286 Some(self.into())
287 }
288}
289
290#[derive(Copy, Clone, Default, PartialEq, PartialOrd, Eq, Ord, Debug, Hash, JsonSchema)]
310#[schemars(with = "Option<Option<T>>", inline)]
311#[expect(clippy::exhaustive_enums)]
312pub enum MaybeUndefined<T> {
313 #[default]
314 Undefined,
315 Null,
316 Value(T),
317}
318
319impl<T> MaybeUndefined<T> {
320 #[inline]
322 pub const fn is_undefined(&self) -> bool {
323 matches!(self, MaybeUndefined::Undefined)
324 }
325
326 #[inline]
328 pub const fn is_null(&self) -> bool {
329 matches!(self, MaybeUndefined::Null)
330 }
331
332 #[inline]
334 pub const fn is_value(&self) -> bool {
335 matches!(self, MaybeUndefined::Value(_))
336 }
337
338 #[inline]
341 pub const fn value(&self) -> Option<&T> {
342 match self {
343 MaybeUndefined::Value(value) => Some(value),
344 _ => None,
345 }
346 }
347
348 #[inline]
350 pub fn take(self) -> Option<T> {
351 match self {
352 MaybeUndefined::Value(value) => Some(value),
353 _ => None,
354 }
355 }
356
357 #[inline]
359 pub const fn as_opt_ref(&self) -> Option<Option<&T>> {
360 match self {
361 MaybeUndefined::Undefined => None,
362 MaybeUndefined::Null => Some(None),
363 MaybeUndefined::Value(value) => Some(Some(value)),
364 }
365 }
366
367 #[inline]
369 pub fn as_opt_deref<U>(&self) -> Option<Option<&U>>
370 where
371 U: ?Sized,
372 T: Deref<Target = U>,
373 {
374 match self {
375 MaybeUndefined::Undefined => None,
376 MaybeUndefined::Null => Some(None),
377 MaybeUndefined::Value(value) => Some(Some(&**value)),
378 }
379 }
380
381 #[inline]
383 pub fn contains_value<U>(&self, x: &U) -> bool
384 where
385 U: PartialEq<T>,
386 {
387 match self {
388 MaybeUndefined::Value(y) => x == y,
389 _ => false,
390 }
391 }
392
393 #[inline]
396 pub fn contains<U>(&self, x: Option<&U>) -> bool
397 where
398 U: PartialEq<T>,
399 {
400 match self {
401 MaybeUndefined::Value(y) => matches!(x, Some(v) if v == y),
402 MaybeUndefined::Null => x.is_none(),
403 MaybeUndefined::Undefined => false,
404 }
405 }
406
407 #[inline]
410 pub fn map<U, F: FnOnce(Option<T>) -> Option<U>>(self, f: F) -> MaybeUndefined<U> {
411 match self {
412 MaybeUndefined::Value(v) => match f(Some(v)) {
413 Some(v) => MaybeUndefined::Value(v),
414 None => MaybeUndefined::Null,
415 },
416 MaybeUndefined::Null => match f(None) {
417 Some(v) => MaybeUndefined::Value(v),
418 None => MaybeUndefined::Null,
419 },
420 MaybeUndefined::Undefined => MaybeUndefined::Undefined,
421 }
422 }
423
424 #[inline]
427 pub fn map_value<U, F: FnOnce(T) -> U>(self, f: F) -> MaybeUndefined<U> {
428 match self {
429 MaybeUndefined::Value(v) => MaybeUndefined::Value(f(v)),
430 MaybeUndefined::Null => MaybeUndefined::Null,
431 MaybeUndefined::Undefined => MaybeUndefined::Undefined,
432 }
433 }
434
435 pub fn update_to(self, value: &mut Option<T>) {
454 match self {
455 MaybeUndefined::Value(new) => *value = Some(new),
456 MaybeUndefined::Null => *value = None,
457 MaybeUndefined::Undefined => {}
458 }
459 }
460}
461
462impl<T, E> MaybeUndefined<Result<T, E>> {
463 #[inline]
477 pub fn transpose(self) -> Result<MaybeUndefined<T>, E> {
478 match self {
479 MaybeUndefined::Undefined => Ok(MaybeUndefined::Undefined),
480 MaybeUndefined::Null => Ok(MaybeUndefined::Null),
481 MaybeUndefined::Value(Ok(v)) => Ok(MaybeUndefined::Value(v)),
482 MaybeUndefined::Value(Err(e)) => Err(e),
483 }
484 }
485}
486
487impl<T: Serialize> Serialize for MaybeUndefined<T> {
488 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
489 match self {
490 MaybeUndefined::Value(value) => value.serialize(serializer),
491 MaybeUndefined::Null => serializer.serialize_none(),
492 MaybeUndefined::Undefined => serializer.serialize_unit(),
493 }
494 }
495}
496
497impl<'de, T> Deserialize<'de> for MaybeUndefined<T>
498where
499 T: Deserialize<'de>,
500{
501 fn deserialize<D>(deserializer: D) -> Result<MaybeUndefined<T>, D::Error>
502 where
503 D: Deserializer<'de>,
504 {
505 Option::<T>::deserialize(deserializer).map(|value| match value {
506 Some(value) => MaybeUndefined::Value(value),
507 None => MaybeUndefined::Null,
508 })
509 }
510}
511
512impl<T> From<MaybeUndefined<T>> for Option<Option<T>> {
513 fn from(maybe_undefined: MaybeUndefined<T>) -> Self {
514 match maybe_undefined {
515 MaybeUndefined::Undefined => None,
516 MaybeUndefined::Null => Some(None),
517 MaybeUndefined::Value(value) => Some(Some(value)),
518 }
519 }
520}
521
522impl<T> From<Option<Option<T>>> for MaybeUndefined<T> {
523 fn from(value: Option<Option<T>>) -> Self {
524 match value {
525 Some(Some(value)) => Self::Value(value),
526 Some(None) => Self::Null,
527 None => Self::Undefined,
528 }
529 }
530}
531
532impl<'de, T, TAs> DeserializeAs<'de, MaybeUndefined<T>> for MaybeUndefined<TAs>
533where
534 TAs: DeserializeAs<'de, T>,
535{
536 fn deserialize_as<D>(deserializer: D) -> Result<MaybeUndefined<T>, D::Error>
537 where
538 D: Deserializer<'de>,
539 {
540 Option::<DeserializeAsWrap<T, TAs>>::deserialize(deserializer).map(|value| match value {
541 Some(value) => MaybeUndefined::Value(value.into_inner()),
542 None => MaybeUndefined::Null,
543 })
544 }
545}
546
547pub trait IntoMaybeUndefined<T> {
551 fn into_maybe_undefined(self) -> MaybeUndefined<T>;
552}
553
554impl<T> IntoMaybeUndefined<T> for T {
555 fn into_maybe_undefined(self) -> MaybeUndefined<T> {
556 MaybeUndefined::Value(self)
557 }
558}
559
560impl<T> IntoMaybeUndefined<T> for Option<T> {
561 fn into_maybe_undefined(self) -> MaybeUndefined<T> {
562 match self {
563 Some(value) => MaybeUndefined::Value(value),
564 None => MaybeUndefined::Null,
565 }
566 }
567}
568
569impl<T> IntoMaybeUndefined<T> for MaybeUndefined<T> {
570 fn into_maybe_undefined(self) -> MaybeUndefined<T> {
571 self
572 }
573}
574
575impl IntoMaybeUndefined<String> for &str {
576 fn into_maybe_undefined(self) -> MaybeUndefined<String> {
577 MaybeUndefined::Value(self.into())
578 }
579}
580
581impl IntoMaybeUndefined<String> for &mut str {
582 fn into_maybe_undefined(self) -> MaybeUndefined<String> {
583 MaybeUndefined::Value(self.into())
584 }
585}
586
587impl IntoMaybeUndefined<String> for &String {
588 fn into_maybe_undefined(self) -> MaybeUndefined<String> {
589 MaybeUndefined::Value(self.into())
590 }
591}
592
593impl IntoMaybeUndefined<String> for Box<str> {
594 fn into_maybe_undefined(self) -> MaybeUndefined<String> {
595 MaybeUndefined::Value(self.into())
596 }
597}
598
599impl IntoMaybeUndefined<String> for Cow<'_, str> {
600 fn into_maybe_undefined(self) -> MaybeUndefined<String> {
601 MaybeUndefined::Value(self.into())
602 }
603}
604
605impl IntoMaybeUndefined<String> for Arc<str> {
606 fn into_maybe_undefined(self) -> MaybeUndefined<String> {
607 MaybeUndefined::Value(self.to_string())
608 }
609}
610
611impl<T: ?Sized + AsRef<OsStr>> IntoMaybeUndefined<PathBuf> for &T {
612 fn into_maybe_undefined(self) -> MaybeUndefined<PathBuf> {
613 MaybeUndefined::Value(self.into())
614 }
615}
616
617impl IntoMaybeUndefined<PathBuf> for Box<Path> {
618 fn into_maybe_undefined(self) -> MaybeUndefined<PathBuf> {
619 MaybeUndefined::Value(self.into())
620 }
621}
622
623impl IntoMaybeUndefined<PathBuf> for Cow<'_, Path> {
624 fn into_maybe_undefined(self) -> MaybeUndefined<PathBuf> {
625 MaybeUndefined::Value(self.into())
626 }
627}
628
629impl IntoMaybeUndefined<serde_json::Value> for &str {
630 fn into_maybe_undefined(self) -> MaybeUndefined<serde_json::Value> {
631 MaybeUndefined::Value(self.into())
632 }
633}
634
635impl IntoMaybeUndefined<serde_json::Value> for String {
636 fn into_maybe_undefined(self) -> MaybeUndefined<serde_json::Value> {
637 MaybeUndefined::Value(self.into())
638 }
639}
640
641impl IntoMaybeUndefined<serde_json::Value> for Cow<'_, str> {
642 fn into_maybe_undefined(self) -> MaybeUndefined<serde_json::Value> {
643 MaybeUndefined::Value(self.into())
644 }
645}
646
647#[cfg(test)]
648mod tests {
649 use serde::{Deserialize, Serialize};
650 use serde_json::{from_value, json, to_value};
651
652 use super::*;
653
654 #[test]
655 fn test_maybe_undefined_serde() {
656 #[derive(Serialize, Deserialize, Eq, PartialEq, Debug)]
657 struct A {
658 #[serde(default, skip_serializing_if = "MaybeUndefined::is_undefined")]
659 a: MaybeUndefined<i32>,
660 }
661
662 assert_eq!(to_value(MaybeUndefined::Value(100i32)).unwrap(), json!(100));
663
664 assert_eq!(
665 from_value::<MaybeUndefined<i32>>(json!(100)).unwrap(),
666 MaybeUndefined::Value(100)
667 );
668 assert_eq!(
669 from_value::<MaybeUndefined<i32>>(json!(null)).unwrap(),
670 MaybeUndefined::Null
671 );
672
673 assert_eq!(
674 to_value(&A {
675 a: MaybeUndefined::Value(100i32)
676 })
677 .unwrap(),
678 json!({"a": 100})
679 );
680
681 assert_eq!(
682 to_value(&A {
683 a: MaybeUndefined::Null,
684 })
685 .unwrap(),
686 json!({ "a": null })
687 );
688
689 assert_eq!(
690 to_value(&A {
691 a: MaybeUndefined::Undefined,
692 })
693 .unwrap(),
694 json!({})
695 );
696
697 assert_eq!(
698 from_value::<A>(json!({"a": 100})).unwrap(),
699 A {
700 a: MaybeUndefined::Value(100i32)
701 }
702 );
703
704 assert_eq!(
705 from_value::<A>(json!({ "a": null })).unwrap(),
706 A {
707 a: MaybeUndefined::Null
708 }
709 );
710
711 assert_eq!(
712 from_value::<A>(json!({})).unwrap(),
713 A {
714 a: MaybeUndefined::Undefined
715 }
716 );
717 }
718
719 #[test]
720 fn test_maybe_undefined_to_nested_option() {
721 assert_eq!(Option::<Option<i32>>::from(MaybeUndefined::Undefined), None);
722
723 assert_eq!(
724 Option::<Option<i32>>::from(MaybeUndefined::Null),
725 Some(None)
726 );
727
728 assert_eq!(
729 Option::<Option<i32>>::from(MaybeUndefined::Value(42)),
730 Some(Some(42))
731 );
732 }
733
734 #[test]
735 fn test_as_opt_ref() {
736 let value = MaybeUndefined::<String>::Undefined;
737 let r = value.as_opt_ref();
738 assert_eq!(r, None);
739
740 let value = MaybeUndefined::<String>::Null;
741 let r = value.as_opt_ref();
742 assert_eq!(r, Some(None));
743
744 let value = MaybeUndefined::<String>::Value("abc".to_string());
745 let r = value.as_opt_ref();
746 assert_eq!(r, Some(Some(&"abc".to_string())));
747 }
748
749 #[test]
750 fn test_as_opt_deref() {
751 let value = MaybeUndefined::<String>::Undefined;
752 let r = value.as_opt_deref();
753 assert_eq!(r, None);
754
755 let value = MaybeUndefined::<String>::Null;
756 let r = value.as_opt_deref();
757 assert_eq!(r, Some(None));
758
759 let value = MaybeUndefined::<String>::Value("abc".to_string());
760 let r = value.as_opt_deref();
761 assert_eq!(r, Some(Some("abc")));
762 }
763
764 #[test]
765 fn test_contains_value() {
766 let test = "abc";
767
768 let mut value: MaybeUndefined<String> = MaybeUndefined::Undefined;
769 assert!(!value.contains_value(&test));
770
771 value = MaybeUndefined::Null;
772 assert!(!value.contains_value(&test));
773
774 value = MaybeUndefined::Value("abc".to_string());
775 assert!(value.contains_value(&test));
776 }
777
778 #[test]
779 fn test_contains() {
780 let test = Some("abc");
781 let none: Option<&str> = None;
782
783 let mut value: MaybeUndefined<String> = MaybeUndefined::Undefined;
784 assert!(!value.contains(test.as_ref()));
785 assert!(!value.contains(none.as_ref()));
786
787 value = MaybeUndefined::Null;
788 assert!(!value.contains(test.as_ref()));
789 assert!(value.contains(none.as_ref()));
790
791 value = MaybeUndefined::Value("abc".to_string());
792 assert!(value.contains(test.as_ref()));
793 assert!(!value.contains(none.as_ref()));
794 }
795
796 #[test]
797 fn test_map_value() {
798 let mut value: MaybeUndefined<i32> = MaybeUndefined::Undefined;
799 assert_eq!(value.map_value(|v| v > 2), MaybeUndefined::Undefined);
800
801 value = MaybeUndefined::Null;
802 assert_eq!(value.map_value(|v| v > 2), MaybeUndefined::Null);
803
804 value = MaybeUndefined::Value(5);
805 assert_eq!(value.map_value(|v| v > 2), MaybeUndefined::Value(true));
806 }
807
808 #[test]
809 fn test_map() {
810 let mut value: MaybeUndefined<i32> = MaybeUndefined::Undefined;
811 assert_eq!(value.map(|v| Some(v.is_some())), MaybeUndefined::Undefined);
812
813 value = MaybeUndefined::Null;
814 assert_eq!(
815 value.map(|v| Some(v.is_some())),
816 MaybeUndefined::Value(false)
817 );
818
819 value = MaybeUndefined::Value(5);
820 assert_eq!(
821 value.map(|v| Some(v.is_some())),
822 MaybeUndefined::Value(true)
823 );
824 }
825
826 #[test]
827 fn test_transpose() {
828 let mut value: MaybeUndefined<Result<i32, &'static str>> = MaybeUndefined::Undefined;
829 assert_eq!(value.transpose(), Ok(MaybeUndefined::Undefined));
830
831 value = MaybeUndefined::Null;
832 assert_eq!(value.transpose(), Ok(MaybeUndefined::Null));
833
834 value = MaybeUndefined::Value(Ok(5));
835 assert_eq!(value.transpose(), Ok(MaybeUndefined::Value(5)));
836
837 value = MaybeUndefined::Value(Err("error"));
838 assert_eq!(value.transpose(), Err("error"));
839 }
840}