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};
25
26#[cfg(feature = "tracing")]
39#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
40#[non_exhaustive]
41pub struct SkipListener;
42
43#[cfg(feature = "tracing")]
44impl serde_with::InspectError for SkipListener {
45 fn inspect_error(error: impl serde::de::Error) {
46 tracing::warn!(
47 %error,
48 "skipped malformed list entry during deserialization",
49 );
50 }
51}
52
53#[cfg(not(feature = "tracing"))]
57pub type SkipListener = ();
58
59#[cfg(test)]
60mod skip_listener_tests {
61 use std::cell::Cell;
62
63 use serde::{Deserialize, Serialize};
64 use serde_json::json;
65 use serde_with::{DefaultOnError, VecSkipError, serde_as};
66
67 thread_local! {
68 static SKIP_COUNT: Cell<u32> = const { Cell::new(0) };
69 }
70
71 struct CountingListener;
73
74 impl serde_with::InspectError for CountingListener {
75 fn inspect_error(_error: impl serde::de::Error) {
76 SKIP_COUNT.with(|c| c.set(c.get() + 1));
77 }
78 }
79
80 #[serde_as]
81 #[derive(Serialize, Deserialize, Debug, PartialEq)]
82 struct Wrapper {
83 #[serde_as(deserialize_as = "VecSkipError<_, CountingListener>")]
84 values: Vec<u32>,
85 }
86
87 #[test]
88 fn inspector_runs_for_each_skipped_entry() {
89 SKIP_COUNT.with(|c| c.set(0));
90
91 let input = json!({"values": [1, "oops", 2, {}, 3]});
92 let wrapper: Wrapper = serde_json::from_value(input).unwrap();
93
94 assert_eq!(wrapper.values, vec![1, 2, 3]);
95 assert_eq!(SKIP_COUNT.with(Cell::get), 2);
96 }
97
98 #[serde_as]
103 #[derive(Deserialize, Debug, PartialEq)]
104 struct ResilientVec {
105 #[serde_as(deserialize_as = "DefaultOnError<VecSkipError<_, CountingListener>>")]
106 #[serde(default)]
107 values: Vec<u32>,
108 }
109
110 #[test]
111 fn resilient_vec_tolerates_missing_null_and_wrong_type() {
112 let r: ResilientVec = serde_json::from_value(json!({})).unwrap();
114 assert_eq!(r.values, Vec::<u32>::new());
115
116 let r: ResilientVec = serde_json::from_value(json!({"values": null})).unwrap();
118 assert_eq!(r.values, Vec::<u32>::new());
119
120 let r: ResilientVec = serde_json::from_value(json!({"values": "oops"})).unwrap();
122 assert_eq!(r.values, Vec::<u32>::new());
123
124 let r: ResilientVec = serde_json::from_value(json!({"values": {"k": 1}})).unwrap();
126 assert_eq!(r.values, Vec::<u32>::new());
127
128 SKIP_COUNT.with(|c| c.set(0));
130 let r: ResilientVec =
131 serde_json::from_value(json!({"values": [1, "oops", 2, {}, 3]})).unwrap();
132 assert_eq!(r.values, vec![1, 2, 3]);
133 assert_eq!(SKIP_COUNT.with(Cell::get), 2);
134 }
135
136 #[test]
137 fn resilient_vec_does_not_invoke_inspector_on_outer_failure() {
138 SKIP_COUNT.with(|c| c.set(0));
139
140 let _r: ResilientVec = serde_json::from_value(json!({"values": null})).unwrap();
143 let _r: ResilientVec = serde_json::from_value(json!({"values": "oops"})).unwrap();
144 let _r: ResilientVec = serde_json::from_value(json!({"values": {}})).unwrap();
145
146 assert_eq!(SKIP_COUNT.with(Cell::get), 0);
147 }
148
149 #[serde_as]
154 #[derive(Deserialize, Debug, PartialEq)]
155 struct ResilientOptionVec {
156 #[serde_as(deserialize_as = "DefaultOnError<Option<VecSkipError<_, CountingListener>>>")]
157 #[serde(default)]
158 values: Option<Vec<u32>>,
159 }
160
161 #[test]
162 fn resilient_option_vec_tolerates_missing_null_and_wrong_type() {
163 let r: ResilientOptionVec = serde_json::from_value(json!({})).unwrap();
165 assert_eq!(r.values, None);
166
167 let r: ResilientOptionVec = serde_json::from_value(json!({"values": null})).unwrap();
169 assert_eq!(r.values, None);
170
171 let r: ResilientOptionVec = serde_json::from_value(json!({"values": []})).unwrap();
173 assert_eq!(r.values, Some(Vec::<u32>::new()));
174
175 let r: ResilientOptionVec = serde_json::from_value(json!({"values": [1, 2, 3]})).unwrap();
177 assert_eq!(r.values, Some(vec![1, 2, 3]));
178
179 let r: ResilientOptionVec = serde_json::from_value(json!({"values": "oops"})).unwrap();
181 assert_eq!(r.values, None);
182
183 let r: ResilientOptionVec = serde_json::from_value(json!({"values": {"k": 1}})).unwrap();
185 assert_eq!(r.values, None);
186
187 SKIP_COUNT.with(|c| c.set(0));
189 let r: ResilientOptionVec =
190 serde_json::from_value(json!({"values": [1, "oops", 2, {}, 3]})).unwrap();
191 assert_eq!(r.values, Some(vec![1, 2, 3]));
192 assert_eq!(SKIP_COUNT.with(Cell::get), 2);
193 }
194}
195
196pub trait IntoOption<T> {
202 fn into_option(self) -> Option<T>;
203}
204
205impl<T> IntoOption<T> for Option<T> {
206 fn into_option(self) -> Option<T> {
207 self
208 }
209}
210
211impl<T> IntoOption<T> for T {
212 fn into_option(self) -> Option<T> {
213 Some(self)
214 }
215}
216
217impl IntoOption<String> for &str {
218 fn into_option(self) -> Option<String> {
219 Some(self.into())
220 }
221}
222
223impl IntoOption<String> for &mut str {
224 fn into_option(self) -> Option<String> {
225 Some(self.into())
226 }
227}
228
229impl IntoOption<String> for &String {
230 fn into_option(self) -> Option<String> {
231 Some(self.into())
232 }
233}
234
235impl IntoOption<String> for Box<str> {
236 fn into_option(self) -> Option<String> {
237 Some(self.into())
238 }
239}
240
241impl IntoOption<String> for Cow<'_, str> {
242 fn into_option(self) -> Option<String> {
243 Some(self.into())
244 }
245}
246
247impl IntoOption<String> for Arc<str> {
248 fn into_option(self) -> Option<String> {
249 Some(self.to_string())
250 }
251}
252
253impl<T: ?Sized + AsRef<OsStr>> IntoOption<PathBuf> for &T {
254 fn into_option(self) -> Option<PathBuf> {
255 Some(self.into())
256 }
257}
258
259impl IntoOption<PathBuf> for Box<Path> {
260 fn into_option(self) -> Option<PathBuf> {
261 Some(self.into())
262 }
263}
264
265impl IntoOption<PathBuf> for Cow<'_, Path> {
266 fn into_option(self) -> Option<PathBuf> {
267 Some(self.into())
268 }
269}
270
271impl IntoOption<serde_json::Value> for &str {
272 fn into_option(self) -> Option<serde_json::Value> {
273 Some(self.into())
274 }
275}
276
277impl IntoOption<serde_json::Value> for String {
278 fn into_option(self) -> Option<serde_json::Value> {
279 Some(self.into())
280 }
281}
282
283impl IntoOption<serde_json::Value> for Cow<'_, str> {
284 fn into_option(self) -> Option<serde_json::Value> {
285 Some(self.into())
286 }
287}
288
289#[allow(missing_docs)]
309#[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
532pub trait IntoMaybeUndefined<T> {
536 fn into_maybe_undefined(self) -> MaybeUndefined<T>;
537}
538
539impl<T> IntoMaybeUndefined<T> for T {
540 fn into_maybe_undefined(self) -> MaybeUndefined<T> {
541 MaybeUndefined::Value(self)
542 }
543}
544
545impl<T> IntoMaybeUndefined<T> for Option<T> {
546 fn into_maybe_undefined(self) -> MaybeUndefined<T> {
547 match self {
548 Some(value) => MaybeUndefined::Value(value),
549 None => MaybeUndefined::Null,
550 }
551 }
552}
553
554impl<T> IntoMaybeUndefined<T> for MaybeUndefined<T> {
555 fn into_maybe_undefined(self) -> MaybeUndefined<T> {
556 self
557 }
558}
559
560impl IntoMaybeUndefined<String> for &str {
561 fn into_maybe_undefined(self) -> MaybeUndefined<String> {
562 MaybeUndefined::Value(self.into())
563 }
564}
565
566impl IntoMaybeUndefined<String> for &mut str {
567 fn into_maybe_undefined(self) -> MaybeUndefined<String> {
568 MaybeUndefined::Value(self.into())
569 }
570}
571
572impl IntoMaybeUndefined<String> for &String {
573 fn into_maybe_undefined(self) -> MaybeUndefined<String> {
574 MaybeUndefined::Value(self.into())
575 }
576}
577
578impl IntoMaybeUndefined<String> for Box<str> {
579 fn into_maybe_undefined(self) -> MaybeUndefined<String> {
580 MaybeUndefined::Value(self.into())
581 }
582}
583
584impl IntoMaybeUndefined<String> for Cow<'_, str> {
585 fn into_maybe_undefined(self) -> MaybeUndefined<String> {
586 MaybeUndefined::Value(self.into())
587 }
588}
589
590impl IntoMaybeUndefined<String> for Arc<str> {
591 fn into_maybe_undefined(self) -> MaybeUndefined<String> {
592 MaybeUndefined::Value(self.to_string())
593 }
594}
595
596impl<T: ?Sized + AsRef<OsStr>> IntoMaybeUndefined<PathBuf> for &T {
597 fn into_maybe_undefined(self) -> MaybeUndefined<PathBuf> {
598 MaybeUndefined::Value(self.into())
599 }
600}
601
602impl IntoMaybeUndefined<PathBuf> for Box<Path> {
603 fn into_maybe_undefined(self) -> MaybeUndefined<PathBuf> {
604 MaybeUndefined::Value(self.into())
605 }
606}
607
608impl IntoMaybeUndefined<PathBuf> for Cow<'_, Path> {
609 fn into_maybe_undefined(self) -> MaybeUndefined<PathBuf> {
610 MaybeUndefined::Value(self.into())
611 }
612}
613
614impl IntoMaybeUndefined<serde_json::Value> for &str {
615 fn into_maybe_undefined(self) -> MaybeUndefined<serde_json::Value> {
616 MaybeUndefined::Value(self.into())
617 }
618}
619
620impl IntoMaybeUndefined<serde_json::Value> for String {
621 fn into_maybe_undefined(self) -> MaybeUndefined<serde_json::Value> {
622 MaybeUndefined::Value(self.into())
623 }
624}
625
626impl IntoMaybeUndefined<serde_json::Value> for Cow<'_, str> {
627 fn into_maybe_undefined(self) -> MaybeUndefined<serde_json::Value> {
628 MaybeUndefined::Value(self.into())
629 }
630}
631
632#[cfg(test)]
633mod tests {
634 use serde::{Deserialize, Serialize};
635 use serde_json::{from_value, json, to_value};
636
637 use super::*;
638
639 #[test]
640 fn test_maybe_undefined_serde() {
641 #[derive(Serialize, Deserialize, Eq, PartialEq, Debug)]
642 struct A {
643 #[serde(default, skip_serializing_if = "MaybeUndefined::is_undefined")]
644 a: MaybeUndefined<i32>,
645 }
646
647 assert_eq!(to_value(MaybeUndefined::Value(100i32)).unwrap(), json!(100));
648
649 assert_eq!(
650 from_value::<MaybeUndefined<i32>>(json!(100)).unwrap(),
651 MaybeUndefined::Value(100)
652 );
653 assert_eq!(
654 from_value::<MaybeUndefined<i32>>(json!(null)).unwrap(),
655 MaybeUndefined::Null
656 );
657
658 assert_eq!(
659 to_value(&A {
660 a: MaybeUndefined::Value(100i32)
661 })
662 .unwrap(),
663 json!({"a": 100})
664 );
665
666 assert_eq!(
667 to_value(&A {
668 a: MaybeUndefined::Null,
669 })
670 .unwrap(),
671 json!({ "a": null })
672 );
673
674 assert_eq!(
675 to_value(&A {
676 a: MaybeUndefined::Undefined,
677 })
678 .unwrap(),
679 json!({})
680 );
681
682 assert_eq!(
683 from_value::<A>(json!({"a": 100})).unwrap(),
684 A {
685 a: MaybeUndefined::Value(100i32)
686 }
687 );
688
689 assert_eq!(
690 from_value::<A>(json!({ "a": null })).unwrap(),
691 A {
692 a: MaybeUndefined::Null
693 }
694 );
695
696 assert_eq!(
697 from_value::<A>(json!({})).unwrap(),
698 A {
699 a: MaybeUndefined::Undefined
700 }
701 );
702 }
703
704 #[test]
705 fn test_maybe_undefined_to_nested_option() {
706 assert_eq!(Option::<Option<i32>>::from(MaybeUndefined::Undefined), None);
707
708 assert_eq!(
709 Option::<Option<i32>>::from(MaybeUndefined::Null),
710 Some(None)
711 );
712
713 assert_eq!(
714 Option::<Option<i32>>::from(MaybeUndefined::Value(42)),
715 Some(Some(42))
716 );
717 }
718
719 #[test]
720 fn test_as_opt_ref() {
721 let value = MaybeUndefined::<String>::Undefined;
722 let r = value.as_opt_ref();
723 assert_eq!(r, None);
724
725 let value = MaybeUndefined::<String>::Null;
726 let r = value.as_opt_ref();
727 assert_eq!(r, Some(None));
728
729 let value = MaybeUndefined::<String>::Value("abc".to_string());
730 let r = value.as_opt_ref();
731 assert_eq!(r, Some(Some(&"abc".to_string())));
732 }
733
734 #[test]
735 fn test_as_opt_deref() {
736 let value = MaybeUndefined::<String>::Undefined;
737 let r = value.as_opt_deref();
738 assert_eq!(r, None);
739
740 let value = MaybeUndefined::<String>::Null;
741 let r = value.as_opt_deref();
742 assert_eq!(r, Some(None));
743
744 let value = MaybeUndefined::<String>::Value("abc".to_string());
745 let r = value.as_opt_deref();
746 assert_eq!(r, Some(Some("abc")));
747 }
748
749 #[test]
750 fn test_contains_value() {
751 let test = "abc";
752
753 let mut value: MaybeUndefined<String> = MaybeUndefined::Undefined;
754 assert!(!value.contains_value(&test));
755
756 value = MaybeUndefined::Null;
757 assert!(!value.contains_value(&test));
758
759 value = MaybeUndefined::Value("abc".to_string());
760 assert!(value.contains_value(&test));
761 }
762
763 #[test]
764 fn test_contains() {
765 let test = Some("abc");
766 let none: Option<&str> = None;
767
768 let mut value: MaybeUndefined<String> = MaybeUndefined::Undefined;
769 assert!(!value.contains(test.as_ref()));
770 assert!(!value.contains(none.as_ref()));
771
772 value = MaybeUndefined::Null;
773 assert!(!value.contains(test.as_ref()));
774 assert!(value.contains(none.as_ref()));
775
776 value = MaybeUndefined::Value("abc".to_string());
777 assert!(value.contains(test.as_ref()));
778 assert!(!value.contains(none.as_ref()));
779 }
780
781 #[test]
782 fn test_map_value() {
783 let mut value: MaybeUndefined<i32> = MaybeUndefined::Undefined;
784 assert_eq!(value.map_value(|v| v > 2), MaybeUndefined::Undefined);
785
786 value = MaybeUndefined::Null;
787 assert_eq!(value.map_value(|v| v > 2), MaybeUndefined::Null);
788
789 value = MaybeUndefined::Value(5);
790 assert_eq!(value.map_value(|v| v > 2), MaybeUndefined::Value(true));
791 }
792
793 #[test]
794 fn test_map() {
795 let mut value: MaybeUndefined<i32> = MaybeUndefined::Undefined;
796 assert_eq!(value.map(|v| Some(v.is_some())), MaybeUndefined::Undefined);
797
798 value = MaybeUndefined::Null;
799 assert_eq!(
800 value.map(|v| Some(v.is_some())),
801 MaybeUndefined::Value(false)
802 );
803
804 value = MaybeUndefined::Value(5);
805 assert_eq!(
806 value.map(|v| Some(v.is_some())),
807 MaybeUndefined::Value(true)
808 );
809 }
810
811 #[test]
812 fn test_transpose() {
813 let mut value: MaybeUndefined<Result<i32, &'static str>> = MaybeUndefined::Undefined;
814 assert_eq!(value.transpose(), Ok(MaybeUndefined::Undefined));
815
816 value = MaybeUndefined::Null;
817 assert_eq!(value.transpose(), Ok(MaybeUndefined::Null));
818
819 value = MaybeUndefined::Value(Ok(5));
820 assert_eq!(value.transpose(), Ok(MaybeUndefined::Value(5)));
821
822 value = MaybeUndefined::Value(Err("error"));
823 assert_eq!(value.transpose(), Err("error"));
824 }
825}