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(crate) 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(crate) 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>;
205}
206
207impl<T> IntoOption<T> for Option<T> {
208 fn into_option(self) -> Option<T> {
209 self
210 }
211}
212
213impl<T> IntoOption<T> for T {
214 fn into_option(self) -> Option<T> {
215 Some(self)
216 }
217}
218
219impl IntoOption<String> for &str {
220 fn into_option(self) -> Option<String> {
221 Some(self.into())
222 }
223}
224
225impl IntoOption<String> for &mut str {
226 fn into_option(self) -> Option<String> {
227 Some(self.into())
228 }
229}
230
231impl IntoOption<String> for &String {
232 fn into_option(self) -> Option<String> {
233 Some(self.into())
234 }
235}
236
237impl IntoOption<String> for Box<str> {
238 fn into_option(self) -> Option<String> {
239 Some(self.into())
240 }
241}
242
243impl IntoOption<String> for Cow<'_, str> {
244 fn into_option(self) -> Option<String> {
245 Some(self.into())
246 }
247}
248
249impl IntoOption<String> for Arc<str> {
250 fn into_option(self) -> Option<String> {
251 Some(self.to_string())
252 }
253}
254
255impl<T: ?Sized + AsRef<OsStr>> IntoOption<PathBuf> for &T {
256 fn into_option(self) -> Option<PathBuf> {
257 Some(self.into())
258 }
259}
260
261impl IntoOption<PathBuf> for Box<Path> {
262 fn into_option(self) -> Option<PathBuf> {
263 Some(self.into())
264 }
265}
266
267impl IntoOption<PathBuf> for Cow<'_, Path> {
268 fn into_option(self) -> Option<PathBuf> {
269 Some(self.into())
270 }
271}
272
273impl IntoOption<serde_json::Value> for &str {
274 fn into_option(self) -> Option<serde_json::Value> {
275 Some(self.into())
276 }
277}
278
279impl IntoOption<serde_json::Value> for String {
280 fn into_option(self) -> Option<serde_json::Value> {
281 Some(self.into())
282 }
283}
284
285impl IntoOption<serde_json::Value> for Cow<'_, str> {
286 fn into_option(self) -> Option<serde_json::Value> {
287 Some(self.into())
288 }
289}
290
291#[derive(Copy, Clone, Default, PartialEq, PartialOrd, Eq, Ord, Debug, Hash, JsonSchema)]
311#[schemars(with = "Option<Option<T>>", inline)]
312#[expect(clippy::exhaustive_enums)]
313pub enum MaybeUndefined<T> {
314 #[default]
316 Undefined,
317 Null,
319 Value(T),
321}
322
323impl<T> MaybeUndefined<T> {
324 #[inline]
326 pub const fn is_undefined(&self) -> bool {
327 matches!(self, MaybeUndefined::Undefined)
328 }
329
330 #[inline]
332 pub const fn is_null(&self) -> bool {
333 matches!(self, MaybeUndefined::Null)
334 }
335
336 #[inline]
338 pub const fn is_value(&self) -> bool {
339 matches!(self, MaybeUndefined::Value(_))
340 }
341
342 #[inline]
345 pub const fn value(&self) -> Option<&T> {
346 match self {
347 MaybeUndefined::Value(value) => Some(value),
348 _ => None,
349 }
350 }
351
352 #[inline]
354 pub fn take(self) -> Option<T> {
355 match self {
356 MaybeUndefined::Value(value) => Some(value),
357 _ => None,
358 }
359 }
360
361 #[inline]
363 pub const fn as_opt_ref(&self) -> Option<Option<&T>> {
364 match self {
365 MaybeUndefined::Undefined => None,
366 MaybeUndefined::Null => Some(None),
367 MaybeUndefined::Value(value) => Some(Some(value)),
368 }
369 }
370
371 #[inline]
373 pub fn as_opt_deref<U>(&self) -> Option<Option<&U>>
374 where
375 U: ?Sized,
376 T: Deref<Target = U>,
377 {
378 match self {
379 MaybeUndefined::Undefined => None,
380 MaybeUndefined::Null => Some(None),
381 MaybeUndefined::Value(value) => Some(Some(&**value)),
382 }
383 }
384
385 #[inline]
387 pub fn contains_value<U>(&self, x: &U) -> bool
388 where
389 U: PartialEq<T>,
390 {
391 match self {
392 MaybeUndefined::Value(y) => x == y,
393 _ => false,
394 }
395 }
396
397 #[inline]
400 pub fn contains<U>(&self, x: Option<&U>) -> bool
401 where
402 U: PartialEq<T>,
403 {
404 match self {
405 MaybeUndefined::Value(y) => matches!(x, Some(v) if v == y),
406 MaybeUndefined::Null => x.is_none(),
407 MaybeUndefined::Undefined => false,
408 }
409 }
410
411 #[inline]
414 pub fn map<U, F: FnOnce(Option<T>) -> Option<U>>(self, f: F) -> MaybeUndefined<U> {
415 match self {
416 MaybeUndefined::Value(v) => match f(Some(v)) {
417 Some(v) => MaybeUndefined::Value(v),
418 None => MaybeUndefined::Null,
419 },
420 MaybeUndefined::Null => match f(None) {
421 Some(v) => MaybeUndefined::Value(v),
422 None => MaybeUndefined::Null,
423 },
424 MaybeUndefined::Undefined => MaybeUndefined::Undefined,
425 }
426 }
427
428 #[inline]
431 pub fn map_value<U, F: FnOnce(T) -> U>(self, f: F) -> MaybeUndefined<U> {
432 match self {
433 MaybeUndefined::Value(v) => MaybeUndefined::Value(f(v)),
434 MaybeUndefined::Null => MaybeUndefined::Null,
435 MaybeUndefined::Undefined => MaybeUndefined::Undefined,
436 }
437 }
438
439 pub fn update_to(self, value: &mut Option<T>) {
458 match self {
459 MaybeUndefined::Value(new) => *value = Some(new),
460 MaybeUndefined::Null => *value = None,
461 MaybeUndefined::Undefined => {}
462 }
463 }
464}
465
466impl<T, E> MaybeUndefined<Result<T, E>> {
467 #[inline]
481 pub fn transpose(self) -> Result<MaybeUndefined<T>, E> {
482 match self {
483 MaybeUndefined::Undefined => Ok(MaybeUndefined::Undefined),
484 MaybeUndefined::Null => Ok(MaybeUndefined::Null),
485 MaybeUndefined::Value(Ok(v)) => Ok(MaybeUndefined::Value(v)),
486 MaybeUndefined::Value(Err(e)) => Err(e),
487 }
488 }
489}
490
491impl<T: Serialize> Serialize for MaybeUndefined<T> {
492 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
493 match self {
494 MaybeUndefined::Value(value) => value.serialize(serializer),
495 MaybeUndefined::Null => serializer.serialize_none(),
496 MaybeUndefined::Undefined => serializer.serialize_unit(),
497 }
498 }
499}
500
501impl<'de, T> Deserialize<'de> for MaybeUndefined<T>
502where
503 T: Deserialize<'de>,
504{
505 fn deserialize<D>(deserializer: D) -> Result<MaybeUndefined<T>, D::Error>
506 where
507 D: Deserializer<'de>,
508 {
509 Option::<T>::deserialize(deserializer).map(|value| match value {
510 Some(value) => MaybeUndefined::Value(value),
511 None => MaybeUndefined::Null,
512 })
513 }
514}
515
516impl<T> From<MaybeUndefined<T>> for Option<Option<T>> {
517 fn from(maybe_undefined: MaybeUndefined<T>) -> Self {
518 match maybe_undefined {
519 MaybeUndefined::Undefined => None,
520 MaybeUndefined::Null => Some(None),
521 MaybeUndefined::Value(value) => Some(Some(value)),
522 }
523 }
524}
525
526impl<T> From<Option<Option<T>>> for MaybeUndefined<T> {
527 fn from(value: Option<Option<T>>) -> Self {
528 match value {
529 Some(Some(value)) => Self::Value(value),
530 Some(None) => Self::Null,
531 None => Self::Undefined,
532 }
533 }
534}
535
536impl<'de, T, TAs> DeserializeAs<'de, MaybeUndefined<T>> for MaybeUndefined<TAs>
537where
538 TAs: DeserializeAs<'de, T>,
539{
540 fn deserialize_as<D>(deserializer: D) -> Result<MaybeUndefined<T>, D::Error>
541 where
542 D: Deserializer<'de>,
543 {
544 Option::<DeserializeAsWrap<T, TAs>>::deserialize(deserializer).map(|value| match value {
545 Some(value) => MaybeUndefined::Value(value.into_inner()),
546 None => MaybeUndefined::Null,
547 })
548 }
549}
550
551pub trait IntoMaybeUndefined<T> {
555 fn into_maybe_undefined(self) -> MaybeUndefined<T>;
557}
558
559impl<T> IntoMaybeUndefined<T> for T {
560 fn into_maybe_undefined(self) -> MaybeUndefined<T> {
561 MaybeUndefined::Value(self)
562 }
563}
564
565impl<T> IntoMaybeUndefined<T> for Option<T> {
566 fn into_maybe_undefined(self) -> MaybeUndefined<T> {
567 match self {
568 Some(value) => MaybeUndefined::Value(value),
569 None => MaybeUndefined::Null,
570 }
571 }
572}
573
574impl<T> IntoMaybeUndefined<T> for MaybeUndefined<T> {
575 fn into_maybe_undefined(self) -> MaybeUndefined<T> {
576 self
577 }
578}
579
580impl IntoMaybeUndefined<String> for &str {
581 fn into_maybe_undefined(self) -> MaybeUndefined<String> {
582 MaybeUndefined::Value(self.into())
583 }
584}
585
586impl IntoMaybeUndefined<String> for &mut str {
587 fn into_maybe_undefined(self) -> MaybeUndefined<String> {
588 MaybeUndefined::Value(self.into())
589 }
590}
591
592impl IntoMaybeUndefined<String> for &String {
593 fn into_maybe_undefined(self) -> MaybeUndefined<String> {
594 MaybeUndefined::Value(self.into())
595 }
596}
597
598impl IntoMaybeUndefined<String> for Box<str> {
599 fn into_maybe_undefined(self) -> MaybeUndefined<String> {
600 MaybeUndefined::Value(self.into())
601 }
602}
603
604impl IntoMaybeUndefined<String> for Cow<'_, str> {
605 fn into_maybe_undefined(self) -> MaybeUndefined<String> {
606 MaybeUndefined::Value(self.into())
607 }
608}
609
610impl IntoMaybeUndefined<String> for Arc<str> {
611 fn into_maybe_undefined(self) -> MaybeUndefined<String> {
612 MaybeUndefined::Value(self.to_string())
613 }
614}
615
616impl<T: ?Sized + AsRef<OsStr>> IntoMaybeUndefined<PathBuf> for &T {
617 fn into_maybe_undefined(self) -> MaybeUndefined<PathBuf> {
618 MaybeUndefined::Value(self.into())
619 }
620}
621
622impl IntoMaybeUndefined<PathBuf> for Box<Path> {
623 fn into_maybe_undefined(self) -> MaybeUndefined<PathBuf> {
624 MaybeUndefined::Value(self.into())
625 }
626}
627
628impl IntoMaybeUndefined<PathBuf> for Cow<'_, Path> {
629 fn into_maybe_undefined(self) -> MaybeUndefined<PathBuf> {
630 MaybeUndefined::Value(self.into())
631 }
632}
633
634impl IntoMaybeUndefined<serde_json::Value> for &str {
635 fn into_maybe_undefined(self) -> MaybeUndefined<serde_json::Value> {
636 MaybeUndefined::Value(self.into())
637 }
638}
639
640impl IntoMaybeUndefined<serde_json::Value> for String {
641 fn into_maybe_undefined(self) -> MaybeUndefined<serde_json::Value> {
642 MaybeUndefined::Value(self.into())
643 }
644}
645
646impl IntoMaybeUndefined<serde_json::Value> for Cow<'_, str> {
647 fn into_maybe_undefined(self) -> MaybeUndefined<serde_json::Value> {
648 MaybeUndefined::Value(self.into())
649 }
650}
651
652#[cfg(test)]
653mod tests {
654 use serde::{Deserialize, Serialize};
655 use serde_json::{from_value, json, to_value};
656
657 use super::*;
658
659 #[test]
660 fn test_maybe_undefined_serde() {
661 #[derive(Serialize, Deserialize, Eq, PartialEq, Debug)]
662 struct A {
663 #[serde(default, skip_serializing_if = "MaybeUndefined::is_undefined")]
664 a: MaybeUndefined<i32>,
665 }
666
667 assert_eq!(to_value(MaybeUndefined::Value(100i32)).unwrap(), json!(100));
668
669 assert_eq!(
670 from_value::<MaybeUndefined<i32>>(json!(100)).unwrap(),
671 MaybeUndefined::Value(100)
672 );
673 assert_eq!(
674 from_value::<MaybeUndefined<i32>>(json!(null)).unwrap(),
675 MaybeUndefined::Null
676 );
677
678 assert_eq!(
679 to_value(&A {
680 a: MaybeUndefined::Value(100i32)
681 })
682 .unwrap(),
683 json!({"a": 100})
684 );
685
686 assert_eq!(
687 to_value(&A {
688 a: MaybeUndefined::Null,
689 })
690 .unwrap(),
691 json!({ "a": null })
692 );
693
694 assert_eq!(
695 to_value(&A {
696 a: MaybeUndefined::Undefined,
697 })
698 .unwrap(),
699 json!({})
700 );
701
702 assert_eq!(
703 from_value::<A>(json!({"a": 100})).unwrap(),
704 A {
705 a: MaybeUndefined::Value(100i32)
706 }
707 );
708
709 assert_eq!(
710 from_value::<A>(json!({ "a": null })).unwrap(),
711 A {
712 a: MaybeUndefined::Null
713 }
714 );
715
716 assert_eq!(
717 from_value::<A>(json!({})).unwrap(),
718 A {
719 a: MaybeUndefined::Undefined
720 }
721 );
722 }
723
724 #[test]
725 fn test_maybe_undefined_to_nested_option() {
726 assert_eq!(Option::<Option<i32>>::from(MaybeUndefined::Undefined), None);
727
728 assert_eq!(
729 Option::<Option<i32>>::from(MaybeUndefined::Null),
730 Some(None)
731 );
732
733 assert_eq!(
734 Option::<Option<i32>>::from(MaybeUndefined::Value(42)),
735 Some(Some(42))
736 );
737 }
738
739 #[test]
740 fn test_as_opt_ref() {
741 let value = MaybeUndefined::<String>::Undefined;
742 let r = value.as_opt_ref();
743 assert_eq!(r, None);
744
745 let value = MaybeUndefined::<String>::Null;
746 let r = value.as_opt_ref();
747 assert_eq!(r, Some(None));
748
749 let value = MaybeUndefined::<String>::Value("abc".to_string());
750 let r = value.as_opt_ref();
751 assert_eq!(r, Some(Some(&"abc".to_string())));
752 }
753
754 #[test]
755 fn test_as_opt_deref() {
756 let value = MaybeUndefined::<String>::Undefined;
757 let r = value.as_opt_deref();
758 assert_eq!(r, None);
759
760 let value = MaybeUndefined::<String>::Null;
761 let r = value.as_opt_deref();
762 assert_eq!(r, Some(None));
763
764 let value = MaybeUndefined::<String>::Value("abc".to_string());
765 let r = value.as_opt_deref();
766 assert_eq!(r, Some(Some("abc")));
767 }
768
769 #[test]
770 fn test_contains_value() {
771 let test = "abc";
772
773 let mut value: MaybeUndefined<String> = MaybeUndefined::Undefined;
774 assert!(!value.contains_value(&test));
775
776 value = MaybeUndefined::Null;
777 assert!(!value.contains_value(&test));
778
779 value = MaybeUndefined::Value("abc".to_string());
780 assert!(value.contains_value(&test));
781 }
782
783 #[test]
784 fn test_contains() {
785 let test = Some("abc");
786 let none: Option<&str> = None;
787
788 let mut value: MaybeUndefined<String> = MaybeUndefined::Undefined;
789 assert!(!value.contains(test.as_ref()));
790 assert!(!value.contains(none.as_ref()));
791
792 value = MaybeUndefined::Null;
793 assert!(!value.contains(test.as_ref()));
794 assert!(value.contains(none.as_ref()));
795
796 value = MaybeUndefined::Value("abc".to_string());
797 assert!(value.contains(test.as_ref()));
798 assert!(!value.contains(none.as_ref()));
799 }
800
801 #[test]
802 fn test_map_value() {
803 let mut value: MaybeUndefined<i32> = MaybeUndefined::Undefined;
804 assert_eq!(value.map_value(|v| v > 2), MaybeUndefined::Undefined);
805
806 value = MaybeUndefined::Null;
807 assert_eq!(value.map_value(|v| v > 2), MaybeUndefined::Null);
808
809 value = MaybeUndefined::Value(5);
810 assert_eq!(value.map_value(|v| v > 2), MaybeUndefined::Value(true));
811 }
812
813 #[test]
814 fn test_map() {
815 let mut value: MaybeUndefined<i32> = MaybeUndefined::Undefined;
816 assert_eq!(value.map(|v| Some(v.is_some())), MaybeUndefined::Undefined);
817
818 value = MaybeUndefined::Null;
819 assert_eq!(
820 value.map(|v| Some(v.is_some())),
821 MaybeUndefined::Value(false)
822 );
823
824 value = MaybeUndefined::Value(5);
825 assert_eq!(
826 value.map(|v| Some(v.is_some())),
827 MaybeUndefined::Value(true)
828 );
829 }
830
831 #[test]
832 fn test_transpose() {
833 let mut value: MaybeUndefined<Result<i32, &'static str>> = MaybeUndefined::Undefined;
834 assert_eq!(value.transpose(), Ok(MaybeUndefined::Undefined));
835
836 value = MaybeUndefined::Null;
837 assert_eq!(value.transpose(), Ok(MaybeUndefined::Null));
838
839 value = MaybeUndefined::Value(Ok(5));
840 assert_eq!(value.transpose(), Ok(MaybeUndefined::Value(5)));
841
842 value = MaybeUndefined::Value(Err("error"));
843 assert_eq!(value.transpose(), Err("error"));
844 }
845}