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, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
141#[repr(packed)]
142pub(crate) struct InnerId(NonZeroU64);
143
144struct SnowflakeVisitor;
145
146impl serde::de::Visitor<'_> for SnowflakeVisitor {
147 type Value = InnerId;
148
149 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
150 formatter.write_str("a string or integer snowflake that is not u64::MAX")
151 }
152
153 fn visit_i64<E: Error>(self, value: i64) -> Result<Self::Value, E> {
155 self.visit_u64(u64::try_from(value).map_err(Error::custom)?)
156 }
157
158 fn visit_u64<E: Error>(self, value: u64) -> Result<Self::Value, E> {
159 NonZeroU64::new(value)
160 .map(InnerId)
161 .ok_or_else(|| Error::custom("invalid value, expected non-max"))
162 }
163
164 fn visit_str<E: Error>(self, value: &str) -> Result<Self::Value, E> {
165 value.parse().map(InnerId).map_err(Error::custom)
166 }
167}
168
169impl<'de> serde::Deserialize<'de> for InnerId {
170 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<InnerId, D::Error> {
171 deserializer.deserialize_any(SnowflakeVisitor)
172 }
173}
174
175impl serde::Serialize for InnerId {
176 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
177 serializer.collect_str(&{ self.0 })
178 }
179}
180
181id_u64! {
182 AttachmentId: "An identifier for an attachment.";
183 ApplicationId: "An identifier for an Application.";
184 ChannelId: "An identifier for a Channel";
185 EmojiId: "An identifier for an Emoji";
186 GenericId: "An identifier for an unspecific entity.";
187 GuildId: "An identifier for a Guild";
188 IntegrationId: "An identifier for an Integration";
189 MessageId: "An identifier for a Message";
190 RoleId: "An identifier for a Role";
191 ScheduledEventId: "An identifier for a Scheduled Event";
192 StickerId: "An identifier for a sticker.";
193 StickerPackId: "An identifier for a sticker pack.";
194 StickerPackBannerId: "An identifier for a sticker pack banner.";
195 SkuId: "An identifier for a SKU.";
196 UserId: "An identifier for a User";
197 WebhookId: "An identifier for a [`Webhook`]";
198 AuditLogEntryId: "An identifier for an audit log entry.";
199 InteractionId: "An identifier for an interaction.";
200 CommandId: "An identifier for a slash command.";
201 CommandPermissionId: "An identifier for a slash command permission Id.";
202 CommandVersionId: "An identifier for a slash command version Id.";
203 TargetId: "An identifier for a slash command target Id.";
204 StageInstanceId: "An identifier for a stage channel instance.";
205 RuleId: "An identifier for an auto moderation rule";
206 ForumTagId: "An identifier for a forum tag.";
207 EntitlementId: "An identifier for an entitlement.";
208}
209
210#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
215#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
216pub struct ShardId(pub u32);
217
218impl ShardId {
219 #[must_use]
224 pub fn get(self) -> u32 {
225 self.0
226 }
227}
228
229newtype_display_impl!(ShardId, |this| this.0);
230
231#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
237#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Deserialize, Serialize)]
238#[repr(packed)]
239pub struct AnswerId(u8);
240
241impl AnswerId {
242 #[must_use]
246 pub fn get(self) -> u64 {
247 self.0.into()
248 }
249}
250
251newtype_display_impl!(AnswerId, |this| this.0);
252forward_fromstr_impl!(AnswerId, std::convert::identity);
253
254#[cfg(test)]
255mod tests {
256 use std::num::NonZeroU64;
257
258 use super::{GuildId, InnerId};
259
260 #[test]
261 fn test_created_at() {
262 let id = GuildId::new(175928847299117063);
264 assert_eq!(id.created_at().unix_timestamp(), 1462015105);
265 assert_eq!(id.created_at().to_string(), "2016-04-30T11:18:25.796Z");
266 }
267
268 #[test]
269 fn test_id_serde() {
270 use serde::{Deserialize, Serialize};
271
272 use crate::json::{assert_json, json};
273
274 #[derive(Debug, PartialEq, Deserialize, Serialize)]
275 struct S {
276 id: InnerId,
277 }
278
279 #[derive(Debug, PartialEq, Deserialize, Serialize)]
280 struct Opt {
281 id: Option<GuildId>,
282 }
283
284 let id = GuildId::new(17_5928_8472_9911_7063);
285 assert_json(&id, json!("175928847299117063"));
286
287 let s = S {
288 id: InnerId(NonZeroU64::new(17_5928_8472_9911_7063).unwrap()),
289 };
290 assert_json(&s, json!({"id": "175928847299117063"}));
291
292 let s = Opt {
293 id: Some(GuildId::new(17_5928_8472_9911_7063)),
294 };
295 assert_json(&s, json!({"id": "175928847299117063"}));
296 }
297}