Skip to content

Commit

Permalink
Use TryInto for more permissive deserialization for integers
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
kesyog committed Jun 28, 2022
1 parent 8b41015 commit a1dd5c7
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 22 deletions.
8 changes: 4 additions & 4 deletions src/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
37 changes: 19 additions & 18 deletions src/value.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::convert::TryInto;
use std::fmt;
use std::fmt::Display;

Expand Down Expand Up @@ -269,21 +270,21 @@ impl Value {
pub fn into_int(self) -> Result<i64> {
match self.kind {
ValueKind::I64(value) => Ok(value),
ValueKind::I128(value) => Err(ConfigError::invalid_type(
ValueKind::I128(value) => value.try_into().or(Err(ConfigError::invalid_type(
self.origin,
Unexpected::I128(value),
"an signed 64 bit or less integer",
)),
ValueKind::U64(value) => Err(ConfigError::invalid_type(
))),
ValueKind::U64(value) => value.try_into().or(Err(ConfigError::invalid_type(
self.origin,
Unexpected::U64(value),
"an signed 64 bit or less integer",
)),
ValueKind::U128(value) => Err(ConfigError::invalid_type(
))),
ValueKind::U128(value) => value.try_into().or(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() {
Expand Down Expand Up @@ -330,11 +331,11 @@ 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(
ValueKind::U128(value) => value.try_into().or(Err(ConfigError::invalid_type(
self.origin,
Unexpected::U128(value),
"an signed 128 bit integer",
)),
))),

ValueKind::String(ref s) => {
match s.to_lowercase().as_ref() {
Expand Down Expand Up @@ -380,21 +381,21 @@ impl Value {
pub fn into_uint(self) -> Result<u64> {
match self.kind {
ValueKind::U64(value) => Ok(value),
ValueKind::U128(value) => Err(ConfigError::invalid_type(
ValueKind::U128(value) => value.try_into().or(Err(ConfigError::invalid_type(
self.origin,
Unexpected::U128(value),
"an unsigned 64 bit or less integer",
)),
ValueKind::I64(value) => Err(ConfigError::invalid_type(
))),
ValueKind::I64(value) => value.try_into().or(Err(ConfigError::invalid_type(
self.origin,
Unexpected::I64(value),
"an unsigned 64 bit or less integer",
)),
ValueKind::I128(value) => Err(ConfigError::invalid_type(
))),
ValueKind::I128(value) => value.try_into().or(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() {
Expand Down Expand Up @@ -440,16 +441,16 @@ impl Value {
match self.kind {
ValueKind::U64(value) => Ok(value.into()),
ValueKind::U128(value) => Ok(value),
ValueKind::I64(value) => Err(ConfigError::invalid_type(
ValueKind::I64(value) => value.try_into().or(Err(ConfigError::invalid_type(
self.origin,
Unexpected::I64(value),
"an unsigned 128 bit or less integer",
)),
ValueKind::I128(value) => Err(ConfigError::invalid_type(
))),
ValueKind::I128(value) => value.try_into().or(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() {
Expand Down

0 comments on commit a1dd5c7

Please sign in to comment.