Skip to content

Commit

Permalink
Fix int parsing
Browse files Browse the repository at this point in the history
Signed-off-by: Heinz N. Gies <heinz@licenser.net>
  • Loading branch information
Licenser committed Aug 23, 2024
1 parent 3230c99 commit 0a58930
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 9 deletions.
18 changes: 10 additions & 8 deletions src/numberparse/correct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,11 +191,12 @@ impl<'de> Deserializer<'de> {
} else if is_structural_or_whitespace(get!(buf, idx)) == 0 {
err!(idx, get!(buf, idx))
} else {
Ok(StaticNode::I64(if negative {
unsafe { static_cast_i64!(num.wrapping_neg()) } // -(num as i64)
Ok(if negative {
StaticNode::I64(unsafe { static_cast_i64!(num.wrapping_neg()) })
// -(num as i64)
} else {
num as i64
}))
StaticNode::U64(num)
})
}
}
}
Expand Down Expand Up @@ -238,7 +239,6 @@ fn parse_large_integer(
(true, 9_223_372_036_854_775_808) => Ok(StaticNode::I64(i64::MIN)),
(true, 9_223_372_036_854_775_809..=u64::MAX) => err!(idx, get!(buf, idx)),
(true, 0..=9_223_372_036_854_775_807) => Ok(StaticNode::I64(-(num as i64))),
(false, 0..=9_223_372_036_854_775_807) => Ok(StaticNode::I64(num as i64)),
(false, _) => Ok(StaticNode::U64(num)),
}
}
Expand Down Expand Up @@ -404,6 +404,7 @@ mod test {
use crate::value::owned::Value::Static;
use value_trait::StaticNode::F64;
use value_trait::StaticNode::I64;
use value_trait::StaticNode::U64;

fn to_value_from_str(buf: &str) -> Result<Value, Error> {
let mut val = String::from(buf);
Expand Down Expand Up @@ -530,7 +531,7 @@ mod test {

#[test]
fn zero_int() -> Result<(), crate::Error> {
assert_eq!(to_value_from_str("0")?, Static(I64(0)));
assert_eq!(to_value_from_str("0")?, Static(U64(0)));
Ok(())
}

Expand All @@ -545,8 +546,9 @@ mod test {

#[test]
fn int() -> Result<(), crate::Error> {
assert_eq!(to_value_from_str("1")?, Static(I64(1)));
assert_eq!(to_value_from_str("257")?, Static(I64(257)));
assert!(matches!(to_value_from_str("1")?, Static(U64(1))));
assert_eq!(to_value_from_str("-1")?, Static(I64(-1)));
assert_eq!(to_value_from_str("257")?, Static(U64(257)));
Ok(())
}

Expand Down
4 changes: 3 additions & 1 deletion src/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ where
{
let mut deserializer = stry!(Deserializer::from_slice(s.as_bytes_mut()));

dbg!(&deserializer.tape);
T::deserialize(&mut deserializer)
}

Expand Down Expand Up @@ -222,7 +223,8 @@ impl<'de> Deserializer<'de> {
#[cfg_attr(not(feature = "no-inline"), inline)]
#[allow(clippy::cast_sign_loss)]
fn parse_u16(&mut self) -> Result<u16> {
match unsafe { self.next_() } {
let next = unsafe { self.next_() };
match next {
Node::Static(s) => s
.as_u16()
.ok_or_else(|| Self::error(ErrorType::ExpectedUnsigned)),
Expand Down
3 changes: 3 additions & 0 deletions src/serde/de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -576,3 +576,6 @@ impl<'de, 'a> de::Deserializer<'de> for MapKey<'de, 'a> {
struct identifier ignored_any
}
}

#[cfg(test)]
mod tests;
109 changes: 109 additions & 0 deletions src/serde/de/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
use serde::{
de::{Error as DeError, Visitor},
Deserialize, Deserializer,
};
use std::fmt::{Formatter, Result as FmtResult};

const JSON: &str = r#"{
"channels": [
{
"default_auto_archive_duration": 10080
}
],
"unavailable": false
}"#;

#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
#[serde(untagged)]
pub enum GuildCreate {
Available(Guild),
Unavailable(UnavailableGuild),
}

#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq)]
pub struct UnavailableGuild {
pub unavailable: bool,
}

#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Hash)]
pub struct Guild {
pub channels: Vec<Channel>,
}

#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq)]
pub struct Channel {
pub default_auto_archive_duration: Option<AutoArchiveDuration>,
}

#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
// Fix 1: Uncomment this and comment the handwritten deser impl.
// #[derive(Deserialize)]
// #[serde(from = "u16")]
pub enum AutoArchiveDuration {
Hour,
Day,
ThreeDays,
Week,
Unknown { value: u16 },
}

impl From<u16> for AutoArchiveDuration {
fn from(value: u16) -> Self {
match value {
60 => Self::Hour,
1440 => Self::Day,
4320 => Self::ThreeDays,
10080 => Self::Week,
value => Self::Unknown { value },
}
}
}

impl<'de> Deserialize<'de> for AutoArchiveDuration {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_u16(U16EnumVisitor).map(u16::into)
}
}

pub struct U16EnumVisitor;

impl<'de> Visitor<'de> for U16EnumVisitor {
type Value = u16;

fn expecting(&self, f: &mut Formatter<'_>) -> FmtResult {
f.write_str("u16")
}

fn visit_u16<E: DeError>(self, value: u16) -> Result<Self::Value, E> {
Ok(value)
}

fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
where
E: DeError,
{
v.try_into().map_err(DeError::custom)
}

// Fix 2: Uncomment this
// fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
// where
// E: DeError,
// {
// v.try_into().map_err(DeError::custom)
// }
}

#[test]
fn test_deser_u16() -> Result<(), Box<dyn std::error::Error>> {
unsafe {
let mut json = JSON.to_string();
let a = dbg!(crate::from_str::<Guild>(&mut json)?);
let b = dbg!(crate::from_str::<UnavailableGuild>(&mut json)?);
let c = dbg!(crate::from_str::<GuildCreate>(&mut json)?);
assert_eq!(a, serde_json::from_str::<Guild>(&json)?);
assert_eq!(b, serde_json::from_str::<UnavailableGuild>(&json)?);
assert_eq!(c, serde_json::from_str::<GuildCreate>(&json)?);
};
Ok(())
}

0 comments on commit 0a58930

Please sign in to comment.