twilight_model/channel/message/
mention.rs

1use crate::{
2    guild::PartialMember,
3    id::{Id, marker::UserMarker},
4    user::{self, DiscriminatorDisplay, UserFlags},
5    util::image_hash::ImageHash,
6};
7use serde::{Deserialize, Serialize};
8
9/// Mention of a user in a message.
10#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
11pub struct Mention {
12    /// Hash of the user's avatar, if any.
13    pub avatar: Option<ImageHash>,
14    /// Whether the user is a bot.
15    #[serde(default)]
16    pub bot: bool,
17    /// Discriminator used to differentiate people with the same username.
18    ///
19    /// # serde
20    ///
21    /// The discriminator field can be deserialized from either a string or an
22    /// integer. The field will always serialize into a string due to that being
23    /// the type Discord's API uses.
24    #[serde(with = "user::discriminator")]
25    pub discriminator: u16,
26    /// Unique ID of the user.
27    pub id: Id<UserMarker>,
28    /// Member object for the user in the guild, if available.
29    #[serde(skip_serializing_if = "Option::is_none")]
30    pub member: Option<PartialMember>,
31    #[serde(rename = "username")]
32    /// Username of the user.
33    pub name: String,
34    /// Public flags on the user's account.
35    pub public_flags: UserFlags,
36}
37
38impl Mention {
39    /// Create a [`Display`] formatter for a user discriminator.
40    ///
41    /// [`Display`]: core::fmt::Display
42    pub const fn discriminator(&self) -> DiscriminatorDisplay {
43        DiscriminatorDisplay::new(self.discriminator)
44    }
45}
46
47#[cfg(test)]
48mod tests {
49    use super::{Mention, PartialMember, UserFlags};
50    use crate::{
51        guild::MemberFlags,
52        id::Id,
53        util::datetime::{Timestamp, TimestampParseError},
54    };
55    use serde_test::Token;
56    use std::str::FromStr;
57
58    #[test]
59    fn mention_without_member() {
60        let value = Mention {
61            avatar: None,
62            bot: false,
63            discriminator: 1,
64            id: Id::new(1),
65            member: None,
66            name: "foo".to_owned(),
67            public_flags: UserFlags::empty(),
68        };
69
70        serde_test::assert_tokens(
71            &value,
72            &[
73                Token::Struct {
74                    name: "Mention",
75                    len: 6,
76                },
77                Token::Str("avatar"),
78                Token::None,
79                Token::Str("bot"),
80                Token::Bool(false),
81                Token::Str("discriminator"),
82                Token::Str("0001"),
83                Token::Str("id"),
84                Token::NewtypeStruct { name: "Id" },
85                Token::Str("1"),
86                Token::Str("username"),
87                Token::Str("foo"),
88                Token::Str("public_flags"),
89                Token::U64(0),
90                Token::StructEnd,
91            ],
92        );
93    }
94
95    #[test]
96    fn mention_with_member() -> Result<(), TimestampParseError> {
97        let joined_at = Some(Timestamp::from_str("2015-04-26T06:26:56.936000+00:00")?);
98        let flags = MemberFlags::BYPASSES_VERIFICATION | MemberFlags::DID_REJOIN;
99
100        let value = Mention {
101            avatar: None,
102            bot: false,
103            discriminator: 1,
104            id: Id::new(1),
105            member: Some(PartialMember {
106                avatar: None,
107                avatar_decoration_data: None,
108                banner: None,
109                communication_disabled_until: None,
110                deaf: false,
111                flags,
112                joined_at,
113                mute: true,
114                nick: Some("bar".to_owned()),
115                permissions: None,
116                premium_since: None,
117                roles: Vec::new(),
118                user: None,
119            }),
120            name: "foo".to_owned(),
121            public_flags: UserFlags::empty(),
122        };
123
124        serde_test::assert_tokens(
125            &value,
126            &[
127                Token::Struct {
128                    name: "Mention",
129                    len: 7,
130                },
131                Token::Str("avatar"),
132                Token::None,
133                Token::Str("bot"),
134                Token::Bool(false),
135                Token::Str("discriminator"),
136                Token::Str("0001"),
137                Token::Str("id"),
138                Token::NewtypeStruct { name: "Id" },
139                Token::Str("1"),
140                Token::Str("member"),
141                Token::Some,
142                Token::Struct {
143                    name: "PartialMember",
144                    len: 8,
145                },
146                Token::Str("communication_disabled_until"),
147                Token::None,
148                Token::Str("deaf"),
149                Token::Bool(false),
150                Token::Str("flags"),
151                Token::U64(flags.bits()),
152                Token::Str("joined_at"),
153                Token::Some,
154                Token::Str("2015-04-26T06:26:56.936000+00:00"),
155                Token::Str("mute"),
156                Token::Bool(true),
157                Token::Str("nick"),
158                Token::Some,
159                Token::Str("bar"),
160                Token::Str("roles"),
161                Token::Seq { len: Some(0) },
162                Token::SeqEnd,
163                Token::Str("user"),
164                Token::None,
165                Token::StructEnd,
166                Token::Str("username"),
167                Token::Str("foo"),
168                Token::Str("public_flags"),
169                Token::U64(0),
170                Token::StructEnd,
171            ],
172        );
173        Ok(())
174    }
175}