From d0b64cd64a18a6116267fa09a837d62c19cced42 Mon Sep 17 00:00:00 2001 From: Zeyla Hellyer Date: Sat, 10 Jun 2017 21:55:33 -0700 Subject: Fix negative nonces failing to deserialize Negative message nonces caused deserialization errors, as serde would not deserialize integers into strings. To fix this, change `Message::nonce` into an `Option` from an `Option`. This new `Snowflake` is a wrapper around an `i64`. Use a new `I64Visitor` to deserialize i64s, u64s, and strs into the wanted i64. --- src/model/channel/message.rs | 2 +- src/model/mod.rs | 53 +++++++++++++++++++++++++++++++++-- src/model/utils.rs | 31 ++++++++++++++++++++ tests/resources/message_create_2.json | 23 +++++++++++++++ tests/test_deser.rs | 4 +++ 5 files changed, 110 insertions(+), 3 deletions(-) create mode 100644 tests/resources/message_create_2.json diff --git a/src/model/channel/message.rs b/src/model/channel/message.rs index e092703..930c046 100644 --- a/src/model/channel/message.rs +++ b/src/model/channel/message.rs @@ -48,7 +48,7 @@ pub struct Message { /// Array of users mentioned in the message. pub mentions: Vec, /// Non-repeating number used for ensuring message order. - pub nonce: Option, + pub nonce: Option, /// Indicator of whether the message is pinned. pub pinned: bool, /// Array of reactions performed on the message. diff --git a/src/model/mod.rs b/src/model/mod.rs index bc414c7..ace5969 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -53,7 +53,51 @@ use ::utils::Colour; fn default_true() -> bool { true } -macro_rules! id { +macro_rules! id_i64 { + ($(#[$attr:meta] $name:ident;)*) => { + $( + #[$attr] + #[derive(Copy, Clone, Debug, Eq, Hash, PartialOrd, Ord, Serialize)] + #[allow(derive_hash_xor_eq)] + pub struct $name(pub i64); + + impl $name { + /// Retrieves the time that the Id was created at. + pub fn created_at(&self) -> NaiveDateTime { + let offset = (self.0 >> 22) / 1000; + + NaiveDateTime::from_timestamp(1420070400 + offset, 0) + } + } + + impl From for $name { + fn from(v: i64) -> $name { + $name(v) + } + } + + impl PartialEq for $name { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } + } + + impl PartialEq for $name { + fn eq(&self, u: &i64) -> bool { + self.0 == *u + } + } + + impl<'de> Deserialize<'de> for $name { + fn deserialize>(deserializer: D) -> StdResult { + deserializer.deserialize_i64(I64Visitor).map($name) + } + } + )* + }; +} + +macro_rules! id_u64 { ($(#[$attr:meta] $name:ident;)*) => { $( #[$attr] @@ -97,7 +141,7 @@ macro_rules! id { } } -id! { +id_u64! { /// An identifier for a Channel ChannelId; /// An identifier for an Emoji @@ -116,6 +160,11 @@ id! { WebhookId; } +id_i64! { + /// An identifier for a general-purpose signed snowflake. + Snowflake; +} + /// A container for guilds. /// /// This is used to differentiate whether a guild itself can be used or whether diff --git a/src/model/utils.rs b/src/model/utils.rs index f88ecf5..6363fb6 100644 --- a/src/model/utils.rs +++ b/src/model/utils.rs @@ -240,3 +240,34 @@ impl<'de> Visitor<'de> for U64Visitor { Ok(v) } } + +pub struct I64Visitor; + +impl<'de> Visitor<'de> for I64Visitor { + type Value = i64; + + fn expecting(&self, formatter: &mut Formatter) -> FmtResult { + formatter.write_str("identifier") + } + + fn visit_str(self, v: &str) -> StdResult { + match v.parse::() { + Ok(v) => Ok(v), + Err(_) => { + let mut s = String::new(); + s.push_str("Unknown i64 value: "); + s.push_str(v); + + Err(DeError::custom(s)) + }, + } + } + + fn visit_i64(self, v: i64) -> StdResult { + Ok(v) + } + + fn visit_u64(self, v: u64) -> StdResult { + Ok(v as i64) + } +} diff --git a/tests/resources/message_create_2.json b/tests/resources/message_create_2.json new file mode 100644 index 0000000..f999397 --- /dev/null +++ b/tests/resources/message_create_2.json @@ -0,0 +1,23 @@ +{ + "type": 0, + "tts": false, + "timestamp": "2017-01-01T01:01:01.100000+00:00", + "pinned": false, + "nonce": -6000000000000000, + "mentions": [], + "mention_roles": [], + "mention_everyone": false, + "id": "300000000000000000", + "embeds": [], + "edited_timestamp": null, + "content": "fake", + "channel_id": "100000000000000000", + "author": { + "username": "fake", + "id": "300000000000000000", + "discriminator": "1234", + "bot": true, + "avatar": "f133549aac3208319a9fbc3c12345678" + }, + "attachments": [] +} diff --git a/tests/test_deser.rs b/tests/test_deser.rs index f4387bc..1024492 100644 --- a/tests/test_deser.rs +++ b/tests/test_deser.rs @@ -111,7 +111,11 @@ fn guild_update() { #[test] fn message_create() { + // standard p!(MessageCreateEvent, "message_create_1"); + + // negative nonce + p!(MessageCreateEvent, "message_create_2"); } #[test] -- cgit v1.2.3