From e8d2e02aedfa442868292fd5389a7f1dbe93a7d1 Mon Sep 17 00:00:00 2001 From: Jonas Bushart Date: Sun, 21 Jul 2024 20:29:14 +0200 Subject: [PATCH] Generate better error messages when encountering unexpected i128/u128 values during deserialization --- serde_with/src/content/de.rs | 59 ++++++++++++++++++++++++++++-------- serde_with/src/de/impls.rs | 22 +++++++++----- serde_with/src/utils.rs | 44 +++++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 21 deletions(-) diff --git a/serde_with/src/content/de.rs b/serde_with/src/content/de.rs index b87cd480..d6ca76f9 100644 --- a/serde_with/src/content/de.rs +++ b/serde_with/src/content/de.rs @@ -14,6 +14,7 @@ //! In the future this can hopefully be replaced by a public type in `serde` itself. //! +use self::utils::{get_unexpected_i128, get_unexpected_u128}; use crate::{ prelude::*, utils::{size_hint_cautious, size_hint_from_bounds}, @@ -59,21 +60,19 @@ pub(crate) enum Content<'de> { impl Content<'_> { #[cold] - fn unexpected(&self) -> Unexpected<'_> { + fn unexpected<'a>(&'a self, buf: &'a mut [u8; 58]) -> Unexpected<'a> { match *self { Content::Bool(b) => Unexpected::Bool(b), Content::U8(n) => Unexpected::Unsigned(u64::from(n)), Content::U16(n) => Unexpected::Unsigned(u64::from(n)), Content::U32(n) => Unexpected::Unsigned(u64::from(n)), Content::U64(n) => Unexpected::Unsigned(n), - // TODO generate better unexpected error - Content::U128(_) => Unexpected::Other("u128"), + Content::U128(n) => get_unexpected_u128(n, buf), Content::I8(n) => Unexpected::Signed(i64::from(n)), Content::I16(n) => Unexpected::Signed(i64::from(n)), Content::I32(n) => Unexpected::Signed(i64::from(n)), Content::I64(n) => Unexpected::Signed(n), - // TODO generate better unexpected error - Content::I128(_) => Unexpected::Other("i128"), + Content::I128(n) => get_unexpected_i128(n, buf), Content::F32(f) => Unexpected::Float(f64::from(f)), Content::F64(f) => Unexpected::Float(f), Content::Char(c) => Unexpected::Char(c), @@ -327,7 +326,8 @@ where { #[cold] fn invalid_type(self, exp: &dyn Expected) -> E { - DeError::invalid_type(self.content.unexpected(), exp) + let mut buf = [0; 58]; + DeError::invalid_type(self.content.unexpected(&mut buf), exp) } fn deserialize_integer(self, visitor: V) -> Result @@ -769,7 +769,11 @@ where } s @ Content::String(_) | s @ Content::Str(_) => (s, None), other => { - return Err(DeError::invalid_type(other.unexpected(), &"string or map")); + let mut buf = [0; 58]; + return Err(DeError::invalid_type( + other.unexpected(&mut buf), + &"string or map", + )); } }; @@ -915,7 +919,13 @@ where SeqDeserializer::new(v, self.is_human_readable), visitor, ), - Some(other) => Err(DeError::invalid_type(other.unexpected(), &"tuple variant")), + Some(other) => { + let mut buf = [0; 58]; + Err(DeError::invalid_type( + other.unexpected(&mut buf), + &"tuple variant", + )) + } None => Err(DeError::invalid_type( Unexpected::UnitVariant, &"tuple variant", @@ -940,7 +950,13 @@ where SeqDeserializer::new(v, self.is_human_readable), visitor, ), - Some(other) => Err(DeError::invalid_type(other.unexpected(), &"struct variant")), + Some(other) => { + let mut buf = [0; 58]; + Err(DeError::invalid_type( + other.unexpected(&mut buf), + &"struct variant", + )) + } None => Err(DeError::invalid_type( Unexpected::UnitVariant, &"struct variant", @@ -1131,7 +1147,8 @@ where { #[cold] fn invalid_type(self, exp: &dyn Expected) -> E { - DeError::invalid_type(self.content.unexpected(), exp) + let mut buf = [0; 58]; + DeError::invalid_type(self.content.unexpected(&mut buf), exp) } fn deserialize_integer(self, visitor: V) -> Result @@ -1544,7 +1561,11 @@ where } ref s @ Content::String(_) | ref s @ Content::Str(_) => (s, None), ref other => { - return Err(DeError::invalid_type(other.unexpected(), &"string or map")); + let mut buf = [0; 58]; + return Err(DeError::invalid_type( + other.unexpected(&mut buf), + &"string or map", + )); } }; @@ -1672,7 +1693,13 @@ where SeqRefDeserializer::new(v, self.is_human_readable), visitor, ), - Some(other) => Err(DeError::invalid_type(other.unexpected(), &"tuple variant")), + Some(other) => { + let mut buf = [0; 58]; + Err(DeError::invalid_type( + other.unexpected(&mut buf), + &"tuple variant", + )) + } None => Err(DeError::invalid_type( Unexpected::UnitVariant, &"tuple variant", @@ -1697,7 +1724,13 @@ where SeqRefDeserializer::new(v, self.is_human_readable), visitor, ), - Some(other) => Err(DeError::invalid_type(other.unexpected(), &"struct variant")), + Some(other) => { + let mut buf = [0; 58]; + Err(DeError::invalid_type( + other.unexpected(&mut buf), + &"struct variant", + )) + } None => Err(DeError::invalid_type( Unexpected::UnitVariant, &"struct variant", diff --git a/serde_with/src/de/impls.rs b/serde_with/src/de/impls.rs index 2819ca3a..4d0de784 100644 --- a/serde_with/src/de/impls.rs +++ b/serde_with/src/de/impls.rs @@ -1891,10 +1891,13 @@ impl<'de> DeserializeAs<'de, bool> for BoolFromInt { match v { 0 => Ok(false), 1 => Ok(true), - unexp => Err(DeError::invalid_value( - Unexpected::Unsigned(unexp as u64), - &"0 or 1", - )), + unexp => { + let mut buf: [u8; 58] = [0u8; 58]; + Err(DeError::invalid_value( + crate::utils::get_unexpected_u128(unexp, &mut buf), + &self, + )) + } } } @@ -1905,10 +1908,13 @@ impl<'de> DeserializeAs<'de, bool> for BoolFromInt { match v { 0 => Ok(false), 1 => Ok(true), - unexp => Err(DeError::invalid_value( - Unexpected::Signed(unexp as i64), - &"0 or 1", - )), + unexp => { + let mut buf: [u8; 58] = [0u8; 58]; + Err(DeError::invalid_value( + crate::utils::get_unexpected_i128(unexp, &mut buf), + &"0 or 1", + )) + } } } } diff --git a/serde_with/src/utils.rs b/serde_with/src/utils.rs index 367ce341..6d895ef7 100644 --- a/serde_with/src/utils.rs +++ b/serde_with/src/utils.rs @@ -196,3 +196,47 @@ where // https://github.com/rust-lang/rust/issues/61956 Ok(unsafe { core::mem::transmute_copy::<_, [T; N]>(&arr) }) } + +/// Writer that writes into a `&mut [u8]` while checking the length of the buffer +struct BufWriter<'a> { + bytes: &'a mut [u8], + offset: usize, +} + +impl<'a> BufWriter<'a> { + fn new(bytes: &'a mut [u8]) -> Self { + BufWriter { bytes, offset: 0 } + } + + fn into_str(self) -> &'a str { + let slice = &self.bytes[..self.offset]; + core::str::from_utf8(slice) + .unwrap_or("Failed to extract valid string from BufWriter. This should never happen.") + } +} + +impl core::fmt::Write for BufWriter<'_> { + fn write_str(&mut self, s: &str) -> fmt::Result { + if s.len() > self.bytes.len() - self.offset { + Err(fmt::Error) + } else { + self.bytes[self.offset..self.offset + s.len()].copy_from_slice(s.as_bytes()); + self.offset += s.len(); + Ok(()) + } + } +} + +// 58 chars is long enough for any i128 and u128 value +pub(crate) fn get_unexpected_i128(value: i128, buf: &mut [u8; 58]) -> Unexpected<'_> { + let mut writer = BufWriter::new(buf); + fmt::Write::write_fmt(&mut writer, format_args!("integer `{value}` as i128")).unwrap(); + Unexpected::Other(writer.into_str()) +} + +// 58 chars is long enough for any i128 and u128 value +pub(crate) fn get_unexpected_u128(value: u128, buf: &mut [u8; 58]) -> Unexpected<'_> { + let mut writer = BufWriter::new(buf); + fmt::Write::write_fmt(&mut writer, format_args!("integer `{value}` as u128")).unwrap(); + Unexpected::Other(writer.into_str()) +}