use std::fmt;
use std::num::{NonZeroI64, NonZeroU64};
use super::Timestamp;
macro_rules! id_u64 {
($($name:ident;)*) => {
$(
impl $name {
#[doc = concat!("Creates a new ", stringify!($name), " from a u64.")]
#[inline]
#[must_use]
#[track_caller]
pub const fn new(id: u64) -> Self {
match NonZeroU64::new(id) {
Some(inner) => Self(inner),
None => panic!(concat!("Attempted to call ", stringify!($name), "::new with invalid (0) value"))
}
}
#[inline]
#[must_use]
pub const fn get(self) -> u64 {
self.0.get()
}
#[doc = concat!("Retrieves the time that the ", stringify!($name), " was created.")]
#[must_use]
pub fn created_at(&self) -> Timestamp {
Timestamp::from_discord_id(self.get())
}
}
impl Default for $name {
fn default() -> Self {
Self(NonZeroU64::MIN)
}
}
impl AsRef<$name> for $name {
fn as_ref(&self) -> &Self {
self
}
}
impl<'a> From<&'a $name> for $name {
fn from(id: &'a $name) -> $name {
id.clone()
}
}
impl From<u64> for $name {
fn from(id: u64) -> $name {
$name::new(id)
}
}
impl From<NonZeroU64> for $name {
fn from(id: NonZeroU64) -> $name {
$name(id)
}
}
impl PartialEq<u64> for $name {
fn eq(&self, u: &u64) -> bool {
self.get() == *u
}
}
impl fmt::Display for $name {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let inner = self.0;
fmt::Display::fmt(&inner, f)
}
}
impl From<$name> for NonZeroU64 {
fn from(id: $name) -> NonZeroU64 {
id.0
}
}
impl From<$name> for NonZeroI64 {
fn from(id: $name) -> NonZeroI64 {
unsafe {NonZeroI64::new_unchecked(id.get() as i64)}
}
}
impl From<$name> for u64 {
fn from(id: $name) -> u64 {
id.get()
}
}
impl From<$name> for i64 {
fn from(id: $name) -> i64 {
id.get() as i64
}
}
impl std::str::FromStr for $name {
type Err = <u64 as std::str::FromStr>::Err;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self(s.parse()?))
}
}
#[cfg(feature = "typesize")]
impl typesize::TypeSize for $name {}
)*
}
}
#[repr(packed)]
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
pub struct ApplicationId(#[serde(with = "snowflake")] NonZeroU64);
#[repr(packed)]
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
pub struct ChannelId(#[serde(with = "snowflake")] NonZeroU64);
#[repr(packed)]
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
pub struct EmojiId(#[serde(with = "snowflake")] NonZeroU64);
#[repr(packed)]
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
pub struct GenericId(#[serde(with = "snowflake")] NonZeroU64);
#[repr(packed)]
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
pub struct GuildId(#[serde(with = "snowflake")] NonZeroU64);
#[repr(packed)]
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
pub struct IntegrationId(#[serde(with = "snowflake")] NonZeroU64);
#[repr(packed)]
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
pub struct MessageId(#[serde(with = "snowflake")] NonZeroU64);
#[repr(packed)]
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
pub struct RoleId(#[serde(with = "snowflake")] NonZeroU64);
#[repr(packed)]
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Deserialize, Serialize)]
pub struct RuleId(#[serde(with = "snowflake")] NonZeroU64);
#[repr(packed)]
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
pub struct ScheduledEventId(#[serde(with = "snowflake")] NonZeroU64);
#[repr(packed)]
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
pub struct UserId(#[serde(with = "snowflake")] NonZeroU64);
#[repr(packed)]
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
pub struct WebhookId(#[serde(with = "snowflake")] NonZeroU64);
#[repr(packed)]
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
pub struct AuditLogEntryId(#[serde(with = "snowflake")] NonZeroU64);
#[repr(packed)]
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
pub struct AttachmentId(#[serde(with = "snowflake")] NonZeroU64);
#[repr(packed)]
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
pub struct StickerId(#[serde(with = "snowflake")] NonZeroU64);
#[repr(packed)]
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
pub struct StickerPackId(#[serde(with = "snowflake")] NonZeroU64);
#[repr(packed)]
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
pub struct StickerPackBannerId(#[serde(with = "snowflake")] NonZeroU64);
#[repr(packed)]
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
pub struct SkuId(#[serde(with = "snowflake")] NonZeroU64);
#[repr(packed)]
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
pub struct InteractionId(#[serde(with = "snowflake")] NonZeroU64);
#[repr(packed)]
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
pub struct CommandId(#[serde(with = "snowflake")] NonZeroU64);
#[repr(packed)]
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
pub struct CommandPermissionId(#[serde(with = "snowflake")] NonZeroU64);
#[repr(packed)]
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
pub struct CommandVersionId(#[serde(with = "snowflake")] NonZeroU64);
#[repr(packed)]
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
pub struct TargetId(#[serde(with = "snowflake")] NonZeroU64);
#[repr(packed)]
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
pub struct StageInstanceId(#[serde(with = "snowflake")] NonZeroU64);
#[repr(packed)]
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Deserialize, Serialize)]
pub struct ForumTagId(#[serde(with = "snowflake")] NonZeroU64);
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Deserialize, Serialize)]
pub struct EntitlementId(#[serde(with = "snowflake")] pub NonZeroU64);
id_u64! {
AttachmentId;
ApplicationId;
ChannelId;
EmojiId;
GenericId;
GuildId;
IntegrationId;
MessageId;
RoleId;
ScheduledEventId;
StickerId;
StickerPackId;
StickerPackBannerId;
SkuId;
UserId;
WebhookId;
AuditLogEntryId;
InteractionId;
CommandId;
CommandPermissionId;
CommandVersionId;
TargetId;
StageInstanceId;
RuleId;
ForumTagId;
EntitlementId;
}
#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct ShardId(pub u32);
impl fmt::Display for ShardId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
mod snowflake {
use std::fmt;
use std::num::NonZeroU64;
use serde::de::{Error, Visitor};
use serde::{Deserializer, Serializer};
pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<NonZeroU64, D::Error> {
deserializer.deserialize_any(SnowflakeVisitor)
}
#[allow(clippy::trivially_copy_pass_by_ref)]
pub fn serialize<S: Serializer>(id: &NonZeroU64, serializer: S) -> Result<S::Ok, S::Error> {
serializer.collect_str(&id.get())
}
struct SnowflakeVisitor;
impl<'de> Visitor<'de> for SnowflakeVisitor {
type Value = NonZeroU64;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a non-zero string or integer snowflake")
}
fn visit_i64<E: Error>(self, value: i64) -> Result<Self::Value, E> {
self.visit_u64(u64::try_from(value).map_err(Error::custom)?)
}
fn visit_u64<E: Error>(self, value: u64) -> Result<Self::Value, E> {
NonZeroU64::new(value).ok_or_else(|| Error::custom("invalid value, expected non-zero"))
}
fn visit_str<E: Error>(self, value: &str) -> Result<Self::Value, E> {
value.parse().map_err(Error::custom)
}
}
}
#[cfg(test)]
mod tests {
use std::num::NonZeroU64;
use super::GuildId;
#[test]
fn test_created_at() {
let id = GuildId::new(175928847299117063);
assert_eq!(id.created_at().unix_timestamp(), 1462015105);
assert_eq!(id.created_at().to_string(), "2016-04-30T11:18:25.796Z");
}
#[test]
fn test_id_serde() {
use serde::{Deserialize, Serialize};
use super::snowflake;
use crate::json::{assert_json, json};
#[derive(Debug, PartialEq, Deserialize, Serialize)]
struct S {
#[serde(with = "snowflake")]
id: NonZeroU64,
}
#[derive(Debug, PartialEq, Deserialize, Serialize)]
struct Opt {
id: Option<GuildId>,
}
let id = GuildId::new(17_5928_8472_9911_7063);
assert_json(&id, json!("175928847299117063"));
let s = S {
id: NonZeroU64::new(17_5928_8472_9911_7063).unwrap(),
};
assert_json(&s, json!({"id": "175928847299117063"}));
let s = Opt {
id: Some(GuildId::new(17_5928_8472_9911_7063)),
};
assert_json(&s, json!({"id": "175928847299117063"}));
}
}