twilight-model 0.15.0-rc.1

Discord API models for the Twilight ecosystem.
Documentation
use crate::id::{marker::EmojiMarker, Id};
use serde::{Deserialize, Serialize};

/// Reaction below a message.
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct Reaction {
    /// Amount of reactions this emoji has.
    pub count: u64,
    /// Emoji of this reaction.
    pub emoji: ReactionType,
    /// Whether the current user has reacted with this emoji.
    pub me: bool,
}

/// Type of [`Reaction`].
#[derive(Clone, Debug, Eq, Hash, PartialEq, Deserialize, Serialize)]
#[serde(untagged)]
pub enum ReactionType {
    /// Custom [`Emoji`].
    ///
    /// [`Emoji`]: crate::guild::Emoji
    Custom {
        /// Whether the emoji is animated.
        #[serde(default)]
        animated: bool,
        /// Emoji identifier.
        // Even though it says that the id can be nil in the docs,
        // it is a bit misleading as that should only happen when
        // the reaction is a unicode emoji and then it is caught by
        // the other variant.
        id: Id<EmojiMarker>,
        /// Emoji name.
        // Name is nil if the emoji data is no longer available, for
        // example if the emoji have been deleted off the guild.
        name: Option<String>,
    },
    /// Standard [Unicode] emoji.
    ///
    /// [Unicode]: https://unicode.org/emoji/
    Unicode {
        /// Unicode name identifier.
        name: String,
    },
}

#[cfg(test)]
mod tests {
    use super::{Reaction, ReactionType};
    use crate::id::Id;
    use serde_test::Token;

    #[test]
    fn message_reaction_unicode() {
        let value = Reaction {
            count: 7,
            emoji: ReactionType::Unicode {
                name: "a".to_owned(),
            },
            me: true,
        };

        serde_test::assert_tokens(
            &value,
            &[
                Token::Struct {
                    name: "Reaction",
                    len: 3,
                },
                Token::Str("count"),
                Token::U64(7),
                Token::Str("emoji"),
                Token::Struct {
                    name: "ReactionType",
                    len: 1,
                },
                Token::Str("name"),
                Token::Str("a"),
                Token::StructEnd,
                Token::Str("me"),
                Token::Bool(true),
                Token::StructEnd,
            ],
        );
    }

    #[test]
    fn custom() {
        let value = ReactionType::Custom {
            animated: false,
            id: Id::new(1337),
            name: Some("foo".to_owned()),
        };

        serde_test::assert_tokens(
            &value,
            &[
                Token::Struct {
                    name: "ReactionType",
                    len: 3,
                },
                Token::Str("animated"),
                Token::Bool(false),
                Token::Str("id"),
                Token::NewtypeStruct { name: "Id" },
                Token::Str("1337"),
                Token::Str("name"),
                Token::Some,
                Token::Str("foo"),
                Token::StructEnd,
            ],
        );

        // When `animated` isn't present in the payload it should default to
        // `false`.
        serde_test::assert_de_tokens(
            &value,
            &[
                Token::Struct {
                    name: "ReactionType",
                    len: 2,
                },
                Token::Str("id"),
                Token::NewtypeStruct { name: "Id" },
                Token::Str("1337"),
                Token::Str("name"),
                Token::Some,
                Token::Str("foo"),
                Token::StructEnd,
            ],
        );
    }

    #[test]
    fn unicode() {
        let value = ReactionType::Unicode {
            name: "\u{1f643}".to_owned(),
        };

        serde_test::assert_tokens(
            &value,
            &[
                Token::Struct {
                    name: "ReactionType",
                    len: 1,
                },
                Token::Str("name"),
                Token::Str("\u{1f643}"),
                Token::StructEnd,
            ],
        );
    }
}