From 11be36d36e5a1beec06991742cdab0854cfb2d9b Mon Sep 17 00:00:00 2001 From: Luca Trevisani Date: Sat, 24 Feb 2024 23:03:43 +0100 Subject: [PATCH 1/5] add helper functions to deal with invalid fields --- Cargo.toml | 1 + src/field_attributes.rs | 161 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 162 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 4868ca4..bf292f4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ documentation = "https://docs.rs/serde-aux" [dependencies] serde = { version = "1", features = ["derive"] } +serde-value = "0.7.0" serde_json = "1" [dependencies.chrono] diff --git a/src/field_attributes.rs b/src/field_attributes.rs index 47dcef1..ce311ad 100644 --- a/src/field_attributes.rs +++ b/src/field_attributes.rs @@ -828,6 +828,167 @@ where StringOrVecToVec::default().into_deserializer()(deserializer) } +/// Deserialize to primary or fallback type. +/// +/// This helper function will attempt to deserialize to the primary type first, +/// and if that fails it will attempt to deserialize to the fallback type. If +/// both fail, it will return an error. +/// +/// # Example: +/// +/// ```rust +/// use serde_aux::prelude::*; +/// +/// #[derive(serde::Deserialize, Debug)] +/// struct MyStruct { +/// #[serde(deserialize_with = "deserialize_to_type_or_fallback")] +/// i64_or_f64: Result, +/// #[serde(deserialize_with = "deserialize_to_type_or_fallback")] +/// f64_or_string: Result, +/// } +/// +/// let s = r#" { "i64_or_f64": 1, "f64_or_string": 1 } "#; +/// let a: MyStruct = serde_json::from_str(s).unwrap(); +/// assert_eq!(a.i64_or_f64, Ok(1)); +/// assert_eq!(a.f64_or_string, Ok(1.0)); +/// +/// let s = r#" { "i64_or_f64": 1.0, "f64_or_string": 1.0 } "#; +/// let a: MyStruct = serde_json::from_str(s).unwrap(); +/// assert_eq!(a.i64_or_f64, Err(1.0)); +/// assert_eq!(a.f64_or_string, Ok(1.0)); +/// +/// let s = r#" { "i64_or_f64": 1.0, "f64_or_string": "foo" } "#; +/// let a: MyStruct = serde_json::from_str(s).unwrap(); +/// assert_eq!(a.i64_or_f64, Err(1.0)); +/// assert_eq!(a.f64_or_string, Err(String::from("foo"))); +/// +/// let s = r#" { "i64_or_f64": "foo", "f64_or_string": "foo" } "#; +/// assert!(serde_json::from_str::(s).is_err()); +/// ``` +pub fn deserialize_to_type_or_fallback<'de, D, T, F>( + deserializer: D, +) -> Result, D::Error> +where + D: Deserializer<'de>, + T: Deserialize<'de>, + F: Deserialize<'de>, +{ + #[derive(Deserialize)] + #[serde(untagged)] + enum DeEither { + Type(T), + Fallback(F), + } + + DeEither::::deserialize(deserializer).map(|de| match de { + DeEither::Type(t) => Ok(t), + DeEither::Fallback(f) => Err(f), + }) +} + +/// Deserialize to a given type, while ignoring any invalid fields. +/// +/// This helper function will attempt to deserialize to the given type, and if +/// it fails it will return `None`, therefore never failing. This is different +/// from deserializing directly to `Option`, because this would return `None` +/// only for empty fields, while it would return an error for invalid fields. +/// +/// # Example: +/// +/// ```rust +/// use serde_aux::prelude::*; +/// +/// #[derive(serde::Deserialize, Debug)] +/// struct MyStruct { +/// #[serde(deserialize_with = "deserialize_to_type_or_none")] +/// opt_f64: Option, +/// } +/// +/// let s = r#" { "opt_f64": 1 } "#; +/// let a: MyStruct = serde_json::from_str(s).unwrap(); +/// assert_eq!(a.opt_f64, Some(1.0)); +/// +/// let s = r#" { "opt_f64": 1.0 } "#; +/// let a: MyStruct = serde_json::from_str(s).unwrap(); +/// assert_eq!(a.opt_f64, Some(1.0)); +/// +/// let s = r#" { "opt_f64": "foo" } "#; +/// let a: MyStruct = serde_json::from_str(s).unwrap(); +/// assert_eq!(a.opt_f64, None); +/// ``` +pub fn deserialize_to_type_or_none<'de, D, T>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, + Option: Deserialize<'de>, +{ + Option::::deserialize(deserializer).or_else(|_| Ok(None)) +} + +/// Deserialize to a given type, while returning invalid fields as `String`. +/// +/// This helper function will attempt to deserialize to the given type, and if +/// it fails it will return the invalid field lossily converted to `String`, +/// therefore never failing. +/// +/// # Example: +/// +/// ```rust +/// use serde_aux::prelude::*; +/// +/// #[derive(serde::Deserialize, Debug)] +/// struct MyStruct { +/// #[serde(deserialize_with = "deserialize_to_type_or_string_lossy")] +/// f64_or_string_lossy: Result, +/// } +/// +/// let s = r#" { "f64_or_string_lossy": 1 } "#; +/// let a: MyStruct = serde_json::from_str(s).unwrap(); +/// assert_eq!(a.f64_or_string_lossy, Ok(1.0)); +/// +/// let s = r#" { "f64_or_string_lossy": 1.0 } "#; +/// let a: MyStruct = serde_json::from_str(s).unwrap(); +/// assert_eq!(a.f64_or_string_lossy, Ok(1.0)); +/// +/// let s = r#" { "f64_or_string_lossy": "foo" } "#; +/// let a: MyStruct = serde_json::from_str(s).unwrap(); +/// assert_eq!(a.f64_or_string_lossy, Err(String::from("foo"))); +/// ``` +pub fn deserialize_to_type_or_string_lossy<'de, D, T>( + deserializer: D, +) -> Result, D::Error> +where + D: Deserializer<'de>, + T: Deserialize<'de>, +{ + let value = serde_value::Value::deserialize(deserializer)?; + let result = T::deserialize(value.clone()).map_err(|_| match value { + serde_value::Value::Bool(b) => b.to_string(), + serde_value::Value::U8(u) => u.to_string(), + serde_value::Value::U16(u) => u.to_string(), + serde_value::Value::U32(u) => u.to_string(), + serde_value::Value::U64(u) => u.to_string(), + serde_value::Value::I8(i) => i.to_string(), + serde_value::Value::I16(i) => i.to_string(), + serde_value::Value::I32(i) => i.to_string(), + serde_value::Value::I64(i) => i.to_string(), + serde_value::Value::F32(f) => f.to_string(), + serde_value::Value::F64(f) => f.to_string(), + serde_value::Value::Char(c) => c.to_string(), + serde_value::Value::String(s) => s, + serde_value::Value::Unit => String::new(), + serde_value::Value::Option(opt) => { + format!("{:?}", opt) + } + serde_value::Value::Newtype(nt) => { + format!("{:?}", nt) + } + serde_value::Value::Seq(seq) => format!("{:?}", seq), + serde_value::Value::Map(map) => format!("{:?}", map), + serde_value::Value::Bytes(v) => String::from_utf8_lossy(&v).into_owned(), + }); + Ok(result) +} + /// Create a parser quickly. /// /// ``` From 74c7e42ba217089bb572f1249a9f7d84d221a734 Mon Sep 17 00:00:00 2001 From: Luca Trevisani Date: Sat, 24 Feb 2024 23:06:33 +0100 Subject: [PATCH 2/5] remove unneeded serde traits --- src/field_attributes.rs | 66 ++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/src/field_attributes.rs b/src/field_attributes.rs index ce311ad..9e37571 100644 --- a/src/field_attributes.rs +++ b/src/field_attributes.rs @@ -252,7 +252,7 @@ where /// ```rust /// use serde_aux::prelude::*; /// -/// #[derive(serde::Serialize, serde::Deserialize, Debug)] +/// #[derive(serde::Deserialize, Debug)] /// struct MyStruct { /// #[serde(deserialize_with = "deserialize_number_from_string")] /// number_from_string: u64, @@ -277,7 +277,7 @@ where /// /// use serde_aux::prelude::*; /// -/// #[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq)] +/// #[derive(serde::Deserialize, Debug, PartialEq)] /// struct IntId(u64); /// /// impl FromStr for IntId { @@ -288,7 +288,7 @@ where /// } /// } /// -/// #[derive(serde::Serialize, serde::Deserialize, Debug)] +/// #[derive(serde::Deserialize, Debug)] /// struct MyStruct { /// #[serde(deserialize_with = "deserialize_number_from_string")] /// int_id: IntId, @@ -305,7 +305,7 @@ where pub fn deserialize_number_from_string<'de, T, D>(deserializer: D) -> Result where D: Deserializer<'de>, - T: FromStr + serde::Deserialize<'de>, + T: FromStr + Deserialize<'de>, ::Err: Display, { #[derive(Deserialize)] @@ -328,7 +328,7 @@ where /// ```rust /// use serde_aux::prelude::*; /// -/// #[derive(Debug, serde::Deserialize)] +/// #[derive(serde::Deserialize, Debug)] /// struct MyStruct { /// #[serde(deserialize_with = "deserialize_option_number_from_string")] /// option_num: Option, @@ -385,7 +385,7 @@ pub fn deserialize_option_number_from_string<'de, T, D>( ) -> Result, D::Error> where D: Deserializer<'de>, - T: FromStr + serde::Deserialize<'de>, + T: FromStr + Deserialize<'de>, ::Err: Display, { #[derive(Deserialize)] @@ -421,7 +421,7 @@ macro_rules! wrap_option_number_from_string_fn { pub fn $func<'de, T, D>(deserializer: D) -> Result<$res, D::Error> where D: Deserializer<'de>, - T: FromStr + serde::Deserialize<'de>, + T: FromStr + Deserialize<'de>, ::Err: Display, { #[derive(Deserialize)] @@ -461,7 +461,7 @@ wrap_option_number_from_string_fn!( /// use serde_aux::prelude::*; /// use std::cell::Cell; /// - /// #[derive(Debug, serde::Deserialize)] + /// #[derive(serde::Deserialize, Debug)] /// struct MyStruct { /// #[serde(deserialize_with = "deserialize_cell_option_number_from_string")] /// v: Cell> @@ -482,7 +482,7 @@ wrap_option_number_from_string_fn!( /// use serde_aux::prelude::*; /// use std::cell::RefCell; /// - /// #[derive(Debug, serde::Deserialize)] + /// #[derive(serde::Deserialize, Debug)] /// struct MyStruct { /// #[serde(default, deserialize_with = "deserialize_ref_cell_option_number_from_string")] /// v: RefCell> @@ -503,7 +503,7 @@ wrap_option_number_from_string_fn!( /// use serde_aux::prelude::*; /// use std::sync::Mutex; /// - /// #[derive(Debug, serde::Deserialize)] + /// #[derive(serde::Deserialize, Debug)] /// struct MyStruct { /// #[serde(default, deserialize_with = "deserialize_mutex_option_number_from_string")] /// v: Mutex> @@ -524,7 +524,7 @@ wrap_option_number_from_string_fn!( /// use serde_aux::prelude::*; /// use std::sync::RwLock; /// - /// #[derive(Debug, serde::Deserialize)] + /// #[derive(serde::Deserialize, Debug)] /// struct MyStruct { /// #[serde(default, deserialize_with = "deserialize_rw_lock_option_number_from_string")] /// v: RwLock> @@ -546,7 +546,7 @@ wrap_option_number_from_string_fn!( /// ```rust /// use serde_aux::prelude::*; /// -/// #[derive(serde::Serialize, serde::Deserialize, Debug)] +/// #[derive(serde::Deserialize, Debug)] /// struct MyStruct { /// #[serde(deserialize_with = "deserialize_bool_from_anything")] /// boolean: bool, @@ -669,7 +669,7 @@ where /// ```rust /// use serde_aux::prelude::*; /// -/// #[derive(serde::Serialize, serde::Deserialize, Debug)] +/// #[derive(serde::Deserialize, Debug)] /// struct MyStruct { /// #[serde(deserialize_with = "deserialize_string_from_number")] /// number_as_string: String, @@ -714,7 +714,7 @@ where /// ```rust /// use serde_aux::prelude::*; /// -/// #[derive(serde::Serialize, serde::Deserialize, Debug)] +/// #[derive(serde::Deserialize, Debug)] /// struct MyStruct { /// #[serde(deserialize_with = "deserialize_default_from_null")] /// null_as_default: u64, @@ -747,13 +747,13 @@ where /// ```rust /// use serde_aux::prelude::*; /// -/// #[derive(serde::Serialize, serde::Deserialize, Debug)] +/// #[derive(serde::Deserialize, Debug)] /// struct MyStruct { /// #[serde(deserialize_with = "deserialize_default_from_empty_object")] /// empty_as_default: Option, /// } /// -/// #[derive(serde::Serialize, serde::Deserialize, Debug)] +/// #[derive(serde::Deserialize, Debug)] /// struct MyInnerStruct { /// mandatory: u64, /// } @@ -805,7 +805,7 @@ where /// ```rust /// use serde_aux::prelude::*; /// -/// #[derive(serde::Serialize, serde::Deserialize, Debug)] +/// #[derive(serde::Deserialize, Debug)] /// struct MyStruct { /// #[serde(deserialize_with = "deserialize_vec_from_string_or_vec")] /// list: Vec, @@ -821,8 +821,8 @@ where /// ``` pub fn deserialize_vec_from_string_or_vec<'de, T, D>(deserializer: D) -> Result, D::Error> where - D: serde::Deserializer<'de>, - T: FromStr + serde::Deserialize<'de> + 'static, + D: Deserializer<'de>, + T: FromStr + Deserialize<'de> + 'static, ::Err: std::fmt::Display, { StringOrVecToVec::default().into_deserializer()(deserializer) @@ -997,7 +997,7 @@ where /// /// serde_aux::StringOrVecToVecParser!(parse_between_commas, |c| { c == ',' }, true); /// -/// #[derive(serde::Serialize, serde::Deserialize, Debug)] +/// #[derive(serde::Deserialize, Debug)] /// struct MyStruct { /// #[serde(deserialize_with = "parse_between_commas")] /// list: Vec, @@ -1014,7 +1014,7 @@ where /// /// serde_aux::StringOrVecToVecParser!(u8, parse_hex_with_spaces, ' ', |s| { u8::from_str_radix(s, 16) }, true); /// -/// #[derive(serde::Serialize, serde::Deserialize, Debug)] +/// #[derive(serde::Deserialize, Debug)] /// struct MyStructHex { /// #[serde(deserialize_with = "parse_hex_with_spaces")] /// list: Vec, @@ -1071,7 +1071,7 @@ macro_rules! StringOrVecToVecParser { /// StringOrVecToVec::default().into_deserializer()(deserializer) /// } /// -/// #[derive(serde::Serialize, serde::Deserialize, Debug)] +/// #[derive(serde::Deserialize, Debug)] /// struct MyStruct { /// #[serde(deserialize_with = "parser")] /// list: Vec, @@ -1119,7 +1119,7 @@ pub enum Pattern<'a> { /// StringOrVecToVec::with_separator(vec![Pattern::Char('+'), Pattern::Char('-')]).into_deserializer()(deserializer) /// } /// - /// #[derive(serde::Serialize, serde::Deserialize, Debug)] + /// #[derive(serde::Deserialize, Debug)] /// struct MyStruct { /// #[serde(deserialize_with = "parser")] /// list: Vec, @@ -1169,7 +1169,7 @@ impl<'a> From>> for Pattern<'a> { /// StringOrVecToVec::with_separator(vec!['-', '+'].into_iter().collect::()).into_deserializer()(deserializer) /// } /// -/// #[derive(serde::Serialize, serde::Deserialize, Debug)] +/// #[derive(serde::Deserialize, Debug)] /// struct MyStruct { /// #[serde(deserialize_with = "parser")] /// list: Vec, @@ -1221,7 +1221,7 @@ where impl<'a, 'de, T> Default for StringOrVecToVec<'a, T, T::Err> where - T: FromStr + serde::Deserialize<'de> + 'static, + T: FromStr + Deserialize<'de> + 'static, ::Err: std::fmt::Display, { fn default() -> Self { @@ -1231,7 +1231,7 @@ where impl<'a, 'de, T> StringOrVecToVec<'a, T, T::Err> where - T: FromStr + serde::Deserialize<'de> + 'static, + T: FromStr + Deserialize<'de> + 'static, ::Err: std::fmt::Display, { /// Create a `StringOrVecToVec` builder with a custom separator. `T::from_str` is used to parse @@ -1252,7 +1252,7 @@ where /// StringOrVecToVec::with_separator(|c| c == '-' || c == '+').into_deserializer()(deserializer) /// } /// - /// #[derive(serde::Serialize, serde::Deserialize, Debug)] + /// #[derive(serde::Deserialize, Debug)] /// struct MyStruct { /// #[serde(deserialize_with = "parser")] /// list: Vec, @@ -1287,7 +1287,7 @@ where /// parser.into_deserializer()(deserializer) /// } /// - /// #[derive(serde::Serialize, serde::Deserialize, Debug)] + /// #[derive(serde::Deserialize, Debug)] /// struct MyStructSkipEmpty { /// #[serde(deserialize_with = "parser_skip_empty")] /// list: Vec, @@ -1321,7 +1321,7 @@ impl<'a, T, E> StringOrVecToVec<'a, T, E> { /// StringOrVecToVec::new('-', |s| s.trim().parse(), false).into_deserializer()(deserializer) /// } /// - /// #[derive(serde::Serialize, serde::Deserialize, Debug)] + /// #[derive(serde::Deserialize, Debug)] /// struct MyStruct { /// #[serde(deserialize_with = "parser")] /// list: Vec, @@ -1365,7 +1365,7 @@ impl<'a, T, E> StringOrVecToVec<'a, T, E> { /// StringOrVecToVec::with_parser(|s| s.trim().parse()).into_deserializer()(deserializer) /// } /// - /// #[derive(serde::Serialize, serde::Deserialize, Debug)] + /// #[derive(serde::Deserialize, Debug)] /// struct MyStruct { /// #[serde(deserialize_with = "parser")] /// list: Vec, @@ -1386,11 +1386,11 @@ impl<'a, T, E> StringOrVecToVec<'a, T, E> { /// Creates the actual deserializer from this builder. pub fn into_deserializer<'de, D>( self, - ) -> impl FnMut(D) -> Result, >::Error> + ) -> impl FnMut(D) -> Result, >::Error> where 'a: 'de, - D: serde::Deserializer<'de>, - T: serde::Deserialize<'de>, + D: Deserializer<'de>, + T: Deserialize<'de>, E: std::fmt::Display, { #[derive(Deserialize)] From a99984aef03755fab54435f3389e7788ca03cc97 Mon Sep 17 00:00:00 2001 From: Luca Trevisani Date: Mon, 27 Jan 2025 12:54:12 +0100 Subject: [PATCH 3/5] fix clippy "doc list item without indentation" --- src/container_attributes.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/container_attributes.rs b/src/container_attributes.rs index d605c7c..0654e41 100644 --- a/src/container_attributes.rs +++ b/src/container_attributes.rs @@ -6,8 +6,8 @@ use serde::{Deserialize, Deserializer}; /// # **Notes** /// /// - The following deserializer is incompatible with serde's one. If you wish -/// to use `serde(rename)`, there is a high risk it won't work. Please see -/// for further information. +/// to use `serde(rename)`, there is a high risk it won't work. Please see +/// for further information. /// /// # Example: /// From 317188f5d2e010592c9d04e39d5a8fbc89c17d1e Mon Sep 17 00:00:00 2001 From: Luca Trevisani Date: Mon, 27 Jan 2025 12:55:06 +0100 Subject: [PATCH 4/5] fix clippy "usage of a legacy numeric constant" --- src/field_attributes.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/field_attributes.rs b/src/field_attributes.rs index 9e37571..bc4f3e7 100644 --- a/src/field_attributes.rs +++ b/src/field_attributes.rs @@ -603,8 +603,6 @@ pub fn deserialize_bool_from_anything<'de, D>(deserializer: D) -> Result, { - use std::f64::EPSILON; - #[derive(Deserialize)] #[serde(untagged)] enum AnythingOrBool { @@ -622,7 +620,7 @@ where _ => Err(serde::de::Error::custom("The number is neither 1 nor 0")), }, AnythingOrBool::Float(f) => { - if (f - 1.0f64).abs() < EPSILON { + if (f - 1.0f64).abs() < f64::EPSILON { Ok(true) } else if f == 0.0f64 { Ok(false) @@ -642,7 +640,7 @@ where _ => Err(serde::de::Error::custom("The number is neither 1 nor 0")), } } else if let Ok(f) = string.parse::() { - if (f - 1.0f64).abs() < EPSILON { + if (f - 1.0f64).abs() < f64::EPSILON { Ok(true) } else if f == 0.0f64 { Ok(false) From 70a2b9185bb651a4828b95847902df8af071c51f Mon Sep 17 00:00:00 2001 From: Luca Trevisani Date: Mon, 27 Jan 2025 12:55:46 +0100 Subject: [PATCH 5/5] fix clippy "the following explicit lifetimes could be elided: 'a" --- src/field_attributes.rs | 10 +++++----- src/serde_introspection.rs | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/field_attributes.rs b/src/field_attributes.rs index bc4f3e7..295f382 100644 --- a/src/field_attributes.rs +++ b/src/field_attributes.rs @@ -1134,7 +1134,7 @@ pub enum Pattern<'a> { Multiple(Vec>), } -impl<'a> From for Pattern<'a> { +impl From for Pattern<'_> { fn from(c: char) -> Self { Pattern::Char(c) } @@ -1190,7 +1190,7 @@ impl<'a> std::iter::FromIterator> for Pattern<'a> { } } -impl<'a> std::iter::FromIterator for Pattern<'a> { +impl std::iter::FromIterator for Pattern<'_> { fn from_iter(iter: I) -> Self where I: IntoIterator, @@ -1208,7 +1208,7 @@ impl<'a> std::iter::FromIterator<&'a str> for Pattern<'a> { } } -impl<'a, P> From

for Pattern<'a> +impl

From

for Pattern<'_> where P: Fn(char) -> bool + 'static, { @@ -1217,7 +1217,7 @@ where } } -impl<'a, 'de, T> Default for StringOrVecToVec<'a, T, T::Err> +impl<'de, T> Default for StringOrVecToVec<'_, T, T::Err> where T: FromStr + Deserialize<'de> + 'static, ::Err: std::fmt::Display, @@ -1422,7 +1422,7 @@ impl<'a, T, E> StringOrVecToVec<'a, T, E> { } } -impl<'a> Pattern<'a> { +impl Pattern<'_> { fn split<'b>(&self, input: &'b str) -> Vec<&'b str> { match self { Pattern::Char(c) => input.split(*c).collect(), diff --git a/src/serde_introspection.rs b/src/serde_introspection.rs index bce964d..0ef86bf 100644 --- a/src/serde_introspection.rs +++ b/src/serde_introspection.rs @@ -40,7 +40,7 @@ where fields: &'a mut Option<&'static [&'static str]>, } - impl<'de, 'a> Deserializer<'de> for StructFieldsDeserializer<'a> { + impl<'de> Deserializer<'de> for StructFieldsDeserializer<'_> { type Error = serde::de::value::Error; fn deserialize_any(self, _visitor: V) -> Result @@ -74,7 +74,7 @@ where variants: &'a mut Option<&'static [&'static str]>, } - impl<'de, 'a> Deserializer<'de> for EnumVariantsDeserializer<'a> { + impl<'de> Deserializer<'de> for EnumVariantsDeserializer<'_> { type Error = serde::de::value::Error; fn deserialize_any(self, _visitor: V) -> Result