1use std::fmt;
4use std::num::{NonZeroI64, NonZeroU64};
5
6use serde::de::Error;
7
8use super::prelude::*;
9
10macro_rules! newtype_display_impl {
11 ($name:ident, |$this:ident| $inner:expr) => {
12 impl fmt::Display for $name {
13 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
14 fmt::Display::fmt(&(|$this: $name| $inner)(*self), f)
15 }
16 }
17 };
18}
19
20macro_rules! forward_fromstr_impl {
21 ($name:ident, $wrapper:path) => {
22 impl std::str::FromStr for $name {
23 type Err = <u64 as std::str::FromStr>::Err;
24
25 fn from_str(s: &str) -> Result<Self, Self::Err> {
26 Ok(Self($wrapper(s.parse()?)))
27 }
28 }
29 };
30}
31
32macro_rules! id_u64 {
33 ($($name:ident: $doc:literal;)*) => {
34 $(
35 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Deserialize, Serialize)]
36 #[doc = $doc]
37 pub struct $name(InnerId);
38
39 impl $name {
40 #[doc = concat!("Creates a new ", stringify!($name), " from a u64.")]
41 #[inline]
44 #[must_use]
45 #[track_caller]
46 pub const fn new(id: u64) -> Self {
47 match NonZeroU64::new(id) {
48 Some(inner) => Self(InnerId(inner)),
49 None => panic!(concat!("Attempted to call ", stringify!($name), "::new with invalid (0) value"))
50 }
51 }
52
53 #[inline]
55 #[must_use]
56 pub const fn get(self) -> u64 {
57 self.0.0.get()
58 }
59
60 #[doc = concat!("Retrieves the time that the ", stringify!($name), " was created.")]
61 #[must_use]
62 pub fn created_at(&self) -> Timestamp {
63 Timestamp::from_discord_id(self.get())
64 }
65 }
66
67 impl Default for $name {
68 fn default() -> Self {
69 Self(InnerId(NonZeroU64::MIN))
70 }
71 }
72
73 impl AsRef<$name> for $name {
77 fn as_ref(&self) -> &Self {
78 self
79 }
80 }
81
82 impl<'a> From<&'a $name> for $name {
83 fn from(id: &'a $name) -> $name {
84 id.clone()
85 }
86 }
87
88 impl From<u64> for $name {
89 fn from(id: u64) -> $name {
90 $name::new(id)
91 }
92 }
93
94 impl From<NonZeroU64> for $name {
95 fn from(id: NonZeroU64) -> $name {
96 $name(InnerId(id))
97 }
98 }
99
100 impl PartialEq<u64> for $name {
101 fn eq(&self, u: &u64) -> bool {
102 self.get() == *u
103 }
104 }
105
106 impl From<$name> for NonZeroU64 {
107 fn from(id: $name) -> NonZeroU64 {
108 id.0.0
109 }
110 }
111
112 impl From<$name> for NonZeroI64 {
113 fn from(id: $name) -> NonZeroI64 {
114 unsafe {NonZeroI64::new_unchecked(id.get() as i64)}
115 }
116 }
117
118 impl From<$name> for u64 {
119 fn from(id: $name) -> u64 {
120 id.get()
121 }
122 }
123
124 impl From<$name> for i64 {
125 fn from(id: $name) -> i64 {
126 id.get() as i64
127 }
128 }
129
130 newtype_display_impl!($name, |this| this.0.0);
131 forward_fromstr_impl!($name, InnerId);
132
133 #[cfg(feature = "typesize")]
134 impl typesize::TypeSize for $name {}
135 )*
136 }
137}
138
139#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
141#[repr(Rust, packed)]
142pub(crate) struct InnerId(NonZeroU64);
143
144impl fmt::Debug for InnerId {
145 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
146 let inner = self.0;
147 inner.fmt(f)
148 }
149}
150
151struct SnowflakeVisitor;
152
153impl serde::de::Visitor<'_> for SnowflakeVisitor {
154 type Value = InnerId;
155
156 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
157 formatter.write_str("a string or integer snowflake that is not u64::MAX")
158 }
159
160 fn visit_i64<E: Error>(self, value: i64) -> Result<Self::Value, E> {
162 self.visit_u64(u64::try_from(value).map_err(Error::custom)?)
163 }
164
165 fn visit_u64<E: Error>(self, value: u64) -> Result<Self::Value, E> {
166 NonZeroU64::new(value)
167 .map(InnerId)
168 .ok_or_else(|| Error::custom("invalid value, expected non-max"))
169 }
170
171 fn visit_str<E: Error>(self, value: &str) -> Result<Self::Value, E> {
172 value.parse().map(InnerId).map_err(Error::custom)
173 }
174}
175
176impl<'de> serde::Deserialize<'de> for InnerId {
177 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<InnerId, D::Error> {
178 deserializer.deserialize_any(SnowflakeVisitor)
179 }
180}
181
182impl serde::Serialize for InnerId {
183 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
184 serializer.collect_str(&{ self.0 })
185 }
186}
187
188id_u64! {
189 AttachmentId: "An identifier for an attachment.";
190 ApplicationId: "An identifier for an Application.";
191 ChannelId: "An identifier for a Channel";
192 EmojiId: "An identifier for an Emoji";
193 GenericId: "An identifier for an unspecific entity.";
194 GuildId: "An identifier for a Guild";
195 IntegrationId: "An identifier for an Integration";
196 MessageId: "An identifier for a Message";
197 RoleId: "An identifier for a Role";
198 ScheduledEventId: "An identifier for a Scheduled Event";
199 SoundId: "An identifier for a Soundboard sound";
200 StickerId: "An identifier for a sticker.";
201 StickerPackId: "An identifier for a sticker pack.";
202 StickerPackBannerId: "An identifier for a sticker pack banner.";
203 SkuId: "An identifier for a SKU.";
204 UserId: "An identifier for a User";
205 WebhookId: "An identifier for a [`Webhook`]";
206 AuditLogEntryId: "An identifier for an audit log entry.";
207 InteractionId: "An identifier for an interaction.";
208 CommandId: "An identifier for a slash command.";
209 CommandPermissionId: "An identifier for a slash command permission Id.";
210 CommandVersionId: "An identifier for a slash command version Id.";
211 TargetId: "An identifier for a slash command target Id.";
212 StageInstanceId: "An identifier for a stage channel instance.";
213 RuleId: "An identifier for an auto moderation rule";
214 ForumTagId: "An identifier for a forum tag.";
215 EntitlementId: "An identifier for an entitlement.";
216}
217
218#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
223#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
224pub struct ShardId(pub u32);
225
226impl ShardId {
227 #[must_use]
232 pub fn get(self) -> u32 {
233 self.0
234 }
235}
236
237newtype_display_impl!(ShardId, |this| this.0);
238
239#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
245#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Deserialize, Serialize)]
246#[repr(Rust, packed)]
247pub struct AnswerId(u8);
248
249impl AnswerId {
250 #[must_use]
254 pub fn get(self) -> u64 {
255 self.0.into()
256 }
257}
258
259newtype_display_impl!(AnswerId, |this| this.0);
260forward_fromstr_impl!(AnswerId, std::convert::identity);
261
262#[cfg(test)]
263mod tests {
264 use std::num::NonZeroU64;
265
266 use super::{GuildId, InnerId};
267
268 #[test]
269 fn test_created_at() {
270 let id = GuildId::new(175928847299117063);
272 assert_eq!(id.created_at().unix_timestamp(), 1462015105);
273 assert_eq!(id.created_at().to_string(), "2016-04-30T11:18:25.796Z");
274 }
275
276 #[test]
277 fn test_id_serde() {
278 use serde::{Deserialize, Serialize};
279
280 use crate::json::{assert_json, json};
281
282 #[derive(Debug, PartialEq, Deserialize, Serialize)]
283 struct S {
284 id: InnerId,
285 }
286
287 #[derive(Debug, PartialEq, Deserialize, Serialize)]
288 struct Opt {
289 id: Option<GuildId>,
290 }
291
292 let id = GuildId::new(17_5928_8472_9911_7063);
293 assert_json(&id, json!("175928847299117063"));
294
295 let s = S {
296 id: InnerId(NonZeroU64::new(17_5928_8472_9911_7063).unwrap()),
297 };
298 assert_json(&s, json!({"id": "175928847299117063"}));
299
300 let s = Opt {
301 id: Some(GuildId::new(17_5928_8472_9911_7063)),
302 };
303 assert_json(&s, json!({"id": "175928847299117063"}));
304 }
305}