twilight_util/
snowflake.rs

1//! Provides the Snowflake trait for defining extractable information from a Discord Snowflake.
2
3use twilight_model::id::{
4    marker::{
5        ApplicationMarker, AttachmentMarker, AuditLogEntryMarker, ChannelMarker, CommandMarker,
6        CommandVersionMarker, EmojiMarker, GenericMarker, GuildMarker, IntegrationMarker,
7        InteractionMarker, MessageMarker, OauthSkuMarker, OauthTeamMarker, RoleMarker,
8        RoleSubscriptionSkuMarker, ScheduledEventEntityMarker, ScheduledEventMarker, StageMarker,
9        StickerMarker, StickerPackMarker, StickerPackSkuMarker, UserMarker, WebhookMarker,
10    },
11    Id,
12};
13
14/// Snowflake is a trait for defining extractable information from a Snowflake. A Snowflake is a
15/// u64 generated by Discord to uniquely identify a resource.
16pub trait Snowflake {
17    /// Returns the u64 backing the Snowflake.
18    fn id(&self) -> u64;
19
20    /// The Unix epoch of the Snowflake in milliseconds, indicating when it was generated.
21    ///
22    /// Derived from bits 22..63 of the id.
23    ///
24    /// # Examples
25    ///
26    /// See when a user was created using [`chrono`](https://docs.rs/chrono):
27    ///
28    /// ```
29    /// use chrono::{TimeZone, Utc};
30    /// use twilight_model::id::{marker::UserMarker, Id};
31    /// use twilight_util::snowflake::Snowflake;
32    ///
33    /// let id = Id::<UserMarker>::new(105484726235607040);
34    ///
35    /// assert_eq!(
36    ///     "2015-10-19T01:58:38.546+00:00",
37    ///     Utc.timestamp_millis(id.timestamp()).to_rfc3339()
38    /// );
39    /// ```
40    ///
41    /// See when a user was created using [`time`](https://docs.rs/time):
42    ///
43    /// ```
44    /// use time::{format_description::well_known::Rfc3339, Duration, OffsetDateTime};
45    /// use twilight_model::id::{marker::UserMarker, Id};
46    /// use twilight_util::snowflake::Snowflake;
47    ///
48    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
49    /// let id = Id::<UserMarker>::new(105484726235607040);
50    /// // Convert milliseconds to seconds or nanoseconds.
51    /// let dur = Duration::milliseconds(id.timestamp());
52    ///
53    /// let ts = OffsetDateTime::from_unix_timestamp(dur.whole_seconds())?;
54    /// let ts_milli = OffsetDateTime::from_unix_timestamp_nanos(dur.whole_nanoseconds())?;
55    ///
56    /// assert_eq!("2015-10-19T01:58:38Z", ts.format(&Rfc3339)?);
57    /// assert_eq!("2015-10-19T01:58:38.546Z", ts_milli.format(&Rfc3339)?);
58    /// # Ok(()) }
59    /// ```
60    #[allow(clippy::cast_possible_wrap)]
61    fn timestamp(&self) -> i64 {
62        // Discord's custom epoch, the unix time in milliseconds for the first second of 2015.
63        const DISCORD_EPOCH: u64 = 1_420_070_400_000;
64
65        ((self.id() >> 22) + DISCORD_EPOCH) as i64
66    }
67
68    /// The id of the internal worker that generated the Snowflake.
69    ///
70    /// Derived from bits 17..21 of the id.
71    #[allow(clippy::cast_possible_truncation)]
72    fn worker_id(&self) -> u8 {
73        ((self.id() & 0x003E_0000) >> 17) as u8
74    }
75
76    /// The id of the internal process that generated the Snowflake.
77    ///
78    /// Derived from bits 12..16 of the id.
79    #[allow(clippy::cast_possible_truncation)]
80    fn process_id(&self) -> u8 {
81        ((self.id() & 0x1F000) >> 12) as u8
82    }
83
84    /// The increment of the Snowflake. For every id that is generated on a process, this number is
85    /// incremented.
86    ///
87    /// Derived from bits 0..11 of the id.
88    #[allow(clippy::cast_possible_truncation)]
89    fn increment(&self) -> u16 {
90        (self.id() & 0xFFF) as u16
91    }
92}
93
94impl Snowflake for Id<ApplicationMarker> {
95    fn id(&self) -> u64 {
96        self.get()
97    }
98}
99
100impl Snowflake for Id<AttachmentMarker> {
101    fn id(&self) -> u64 {
102        self.get()
103    }
104}
105
106impl Snowflake for Id<AuditLogEntryMarker> {
107    fn id(&self) -> u64 {
108        self.get()
109    }
110}
111
112impl Snowflake for Id<ChannelMarker> {
113    fn id(&self) -> u64 {
114        self.get()
115    }
116}
117
118impl Snowflake for Id<CommandMarker> {
119    fn id(&self) -> u64 {
120        self.get()
121    }
122}
123
124impl Snowflake for Id<CommandVersionMarker> {
125    fn id(&self) -> u64 {
126        self.get()
127    }
128}
129
130impl Snowflake for Id<EmojiMarker> {
131    fn id(&self) -> u64 {
132        self.get()
133    }
134}
135
136impl Snowflake for Id<GenericMarker> {
137    fn id(&self) -> u64 {
138        self.get()
139    }
140}
141
142impl Snowflake for Id<GuildMarker> {
143    fn id(&self) -> u64 {
144        self.get()
145    }
146}
147
148impl Snowflake for Id<IntegrationMarker> {
149    fn id(&self) -> u64 {
150        self.get()
151    }
152}
153
154impl Snowflake for Id<InteractionMarker> {
155    fn id(&self) -> u64 {
156        self.get()
157    }
158}
159
160impl Snowflake for Id<MessageMarker> {
161    fn id(&self) -> u64 {
162        self.get()
163    }
164}
165
166impl Snowflake for Id<OauthSkuMarker> {
167    fn id(&self) -> u64 {
168        self.get()
169    }
170}
171
172impl Snowflake for Id<OauthTeamMarker> {
173    fn id(&self) -> u64 {
174        self.get()
175    }
176}
177
178impl Snowflake for Id<RoleMarker> {
179    fn id(&self) -> u64 {
180        self.get()
181    }
182}
183
184impl Snowflake for Id<RoleSubscriptionSkuMarker> {
185    fn id(&self) -> u64 {
186        self.get()
187    }
188}
189
190impl Snowflake for Id<ScheduledEventMarker> {
191    fn id(&self) -> u64 {
192        self.get()
193    }
194}
195
196impl Snowflake for Id<ScheduledEventEntityMarker> {
197    fn id(&self) -> u64 {
198        self.get()
199    }
200}
201
202impl Snowflake for Id<StageMarker> {
203    fn id(&self) -> u64 {
204        self.get()
205    }
206}
207
208impl Snowflake for Id<StickerMarker> {
209    fn id(&self) -> u64 {
210        self.get()
211    }
212}
213
214impl Snowflake for Id<StickerPackMarker> {
215    fn id(&self) -> u64 {
216        self.get()
217    }
218}
219
220impl Snowflake for Id<StickerPackSkuMarker> {
221    fn id(&self) -> u64 {
222        self.get()
223    }
224}
225
226impl Snowflake for Id<UserMarker> {
227    fn id(&self) -> u64 {
228        self.get()
229    }
230}
231
232impl Snowflake for Id<WebhookMarker> {
233    fn id(&self) -> u64 {
234        self.get()
235    }
236}
237
238#[cfg(test)]
239mod tests {
240    use super::*;
241    use static_assertions::{assert_impl_all, assert_obj_safe};
242
243    assert_impl_all!(Id<ApplicationMarker>: Snowflake);
244    assert_impl_all!(Id<AttachmentMarker>: Snowflake);
245    assert_impl_all!(Id<AuditLogEntryMarker>: Snowflake);
246    assert_impl_all!(Id<ChannelMarker>: Snowflake);
247    assert_impl_all!(Id<CommandMarker>: Snowflake);
248    assert_impl_all!(Id<CommandVersionMarker>: Snowflake);
249    assert_impl_all!(Id<EmojiMarker>: Snowflake);
250    assert_impl_all!(Id<GenericMarker>: Snowflake);
251    assert_impl_all!(Id<GuildMarker>: Snowflake);
252    assert_impl_all!(Id<IntegrationMarker>: Snowflake);
253    assert_impl_all!(Id<InteractionMarker>: Snowflake);
254    assert_impl_all!(Id<MessageMarker>: Snowflake);
255    assert_impl_all!(Id<OauthSkuMarker>: Snowflake);
256    assert_impl_all!(Id<OauthTeamMarker>: Snowflake);
257    assert_impl_all!(Id<RoleMarker>: Snowflake);
258    assert_impl_all!(Id<RoleSubscriptionSkuMarker>: Snowflake);
259    assert_impl_all!(Id<ScheduledEventMarker>: Snowflake);
260    assert_impl_all!(Id<ScheduledEventEntityMarker>: Snowflake);
261    assert_impl_all!(Id<StageMarker>: Snowflake);
262    assert_impl_all!(Id<StickerMarker>: Snowflake);
263    assert_impl_all!(Id<StickerPackMarker>: Snowflake);
264    assert_impl_all!(Id<StickerPackSkuMarker>: Snowflake);
265    assert_impl_all!(Id<UserMarker>: Snowflake);
266    assert_impl_all!(Id<WebhookMarker>: Snowflake);
267    assert_obj_safe!(Snowflake);
268
269    #[test]
270    fn timestamp() {
271        let expected: i64 = 1_445_219_918_546;
272        let id = Id::<GenericMarker>::new(105_484_726_235_607_040);
273
274        assert_eq!(expected, id.timestamp());
275    }
276
277    #[test]
278    fn worker_id() {
279        let expected: u8 = 8;
280        let id = Id::<GenericMarker>::new(762_022_344_856_174_632);
281
282        assert_eq!(expected, id.worker_id());
283    }
284
285    #[test]
286    fn process_id() {
287        let expected: u8 = 1;
288        let id = Id::<GenericMarker>::new(61_189_081_970_774_016);
289
290        assert_eq!(expected, id.process_id());
291    }
292
293    #[test]
294    fn increment() {
295        let expected: u16 = 40;
296        let id = Id::<GenericMarker>::new(762_022_344_856_174_632);
297
298        assert_eq!(expected, id.increment());
299    }
300}