diff options
author | Kesavan Yogeswaran <hikes@google.com> | 2022-06-28 00:18:48 -0400 |
---|---|---|
committer | Matthias Beyer <mail@beyermatthias.de> | 2022-08-02 16:32:29 +0200 |
commit | 013273775eb217e4ae535047e6e261d0e89895ff (patch) | |
tree | 418ce3a3a330e2e77986556c8ff3096e04716089 /src | |
parent | c375cad9ceec5667de4f6924b50e900184d1c17b (diff) |
Use TryInto for more permissive deserialization for integers
* Attempt to convert between integer types using `TryInto`-based
conversions rather than blanket failing for some source and
destination types.
* Use `into_uint` instead of `into_int` in `Value` Deserialize
implementations for unsigned integer types. Previously, we were
converting from signed types to unsigned types using `as`, which can
lead to surprise integer values conversions (#93).
Fixes #352 and #93
(cherry picked from commit 7db2e8bfb46d9364ddc3419d3186b150141cc890)
Signed-off-by: Matthias Beyer <mail@beyermatthias.de>
Diffstat (limited to 'src')
-rw-r--r-- | src/de.rs | 8 | ||||
-rw-r--r-- | src/value.rs | 109 |
2 files changed, 68 insertions, 49 deletions
@@ -62,25 +62,25 @@ impl<'de> de::Deserializer<'de> for Value { #[inline] fn deserialize_u8<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { // FIXME: This should *fail* if the value does not fit in the requets integer type - visitor.visit_u8(self.into_int()? as u8) + visitor.visit_u8(self.into_uint()? as u8) } #[inline] fn deserialize_u16<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { // FIXME: This should *fail* if the value does not fit in the requets integer type - visitor.visit_u16(self.into_int()? as u16) + visitor.visit_u16(self.into_uint()? as u16) } #[inline] fn deserialize_u32<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { // FIXME: This should *fail* if the value does not fit in the requets integer type - visitor.visit_u32(self.into_int()? as u32) + visitor.visit_u32(self.into_uint()? as u32) } #[inline] fn deserialize_u64<V: de::Visitor<'de>>(self, visitor: V) -> Result<V::Value> { // FIXME: This should *fail* if the value does not fit in the requets integer type - visitor.visit_u64(self.into_int()? as u64) + visitor.visit_u64(self.into_uint()? as u64) } #[inline] diff --git a/src/value.rs b/src/value.rs index c3dad10..6ab8ddd 100644 --- a/src/value.rs +++ b/src/value.rs @@ -1,3 +1,4 @@ +use std::convert::TryInto; use std::fmt; use std::fmt::Display; @@ -264,21 +265,27 @@ impl Value { pub fn into_int(self) -> Result<i64> { match self.kind { ValueKind::I64(value) => Ok(value), - ValueKind::I128(value) => Err(ConfigError::invalid_type( - self.origin, - Unexpected::I128(value), - "an signed 64 bit or less integer", - )), - ValueKind::U64(value) => Err(ConfigError::invalid_type( - self.origin, - Unexpected::U64(value), - "an signed 64 bit or less integer", - )), - ValueKind::U128(value) => Err(ConfigError::invalid_type( - self.origin, - Unexpected::U128(value), - "an signed 64 bit or less integer", - )), + ValueKind::I128(value) => value.try_into().map_err(|_| { + ConfigError::invalid_type( + self.origin, + Unexpected::I128(value), + "an signed 64 bit or less integer", + ) + }), + ValueKind::U64(value) => value.try_into().map_err(|_| { + ConfigError::invalid_type( + self.origin, + Unexpected::U64(value), + "an signed 64 bit or less integer", + ) + }), + ValueKind::U128(value) => value.try_into().map_err(|_| { + ConfigError::invalid_type( + self.origin, + Unexpected::U128(value), + "an signed 64 bit or less integer", + ) + }), ValueKind::String(ref s) => { match s.to_lowercase().as_ref() { @@ -325,11 +332,13 @@ impl Value { ValueKind::I64(value) => Ok(value.into()), ValueKind::I128(value) => Ok(value), ValueKind::U64(value) => Ok(value.into()), - ValueKind::U128(value) => Err(ConfigError::invalid_type( - self.origin, - Unexpected::U128(value), - "an signed 128 bit integer", - )), + ValueKind::U128(value) => value.try_into().map_err(|_| { + ConfigError::invalid_type( + self.origin, + Unexpected::U128(value), + "an signed 128 bit integer", + ) + }), ValueKind::String(ref s) => { match s.to_lowercase().as_ref() { @@ -375,21 +384,27 @@ impl Value { pub fn into_uint(self) -> Result<u64> { match self.kind { ValueKind::U64(value) => Ok(value), - ValueKind::U128(value) => Err(ConfigError::invalid_type( - self.origin, - Unexpected::U128(value), - "an unsigned 64 bit or less integer", - )), - ValueKind::I64(value) => Err(ConfigError::invalid_type( - self.origin, - Unexpected::I64(value), - "an unsigned 64 bit or less integer", - )), - ValueKind::I128(value) => Err(ConfigError::invalid_type( - self.origin, - Unexpected::I128(value), - "an unsigned 64 bit or less integer", - )), + ValueKind::U128(value) => value.try_into().map_err(|_| { + ConfigError::invalid_type( + self.origin, + Unexpected::U128(value), + "an unsigned 64 bit or less integer", + ) + }), + ValueKind::I64(value) => value.try_into().map_err(|_| { + ConfigError::invalid_type( + self.origin, + Unexpected::I64(value), + "an unsigned 64 bit or less integer", + ) + }), + ValueKind::I128(value) => value.try_into().map_err(|_| { + ConfigError::invalid_type( + self.origin, + Unexpected::I128(value), + "an unsigned 64 bit or less integer", + ) + }), ValueKind::String(ref s) => { match s.to_lowercase().as_ref() { @@ -435,16 +450,20 @@ impl Value { match self.kind { ValueKind::U64(value) => Ok(value.into()), ValueKind::U128(value) => Ok(value), - ValueKind::I64(value) => Err(ConfigError::invalid_type( - self.origin, - Unexpected::I64(value), - "an unsigned 128 bit or less integer", - )), - ValueKind::I128(value) => Err(ConfigError::invalid_type( - self.origin, - Unexpected::I128(value), - "an unsigned 128 bit or less integer", - )), + ValueKind::I64(value) => value.try_into().map_err(|_| { + ConfigError::invalid_type( + self.origin, + Unexpected::I64(value), + "an unsigned 128 bit or less integer", + ) + }), + ValueKind::I128(value) => value.try_into().map_err(|_| { + ConfigError::invalid_type( + self.origin, + Unexpected::I128(value), + "an unsigned 128 bit or less integer", + ) + }), ValueKind::String(ref s) => { match s.to_lowercase().as_ref() { |