use core::hash::Hash;
use crate::id::Id;
pub trait SnowflakeId: Id {
fn timestamp(&self) -> Self::Ty;
fn max_timestamp() -> Self::Ty;
fn machine_id(&self) -> Self::Ty;
fn max_machine_id() -> Self::Ty;
fn sequence(&self) -> Self::Ty;
fn max_sequence() -> Self::Ty;
#[must_use]
fn from_components(timestamp: Self::Ty, machine_id: Self::Ty, sequence: Self::Ty) -> Self;
fn has_sequence_room(&self) -> bool {
self.sequence() < Self::max_sequence()
}
fn next_sequence(&self) -> Self::Ty {
self.sequence() + Self::ONE
}
#[must_use]
fn increment_sequence(&self) -> Self {
Self::from_components(self.timestamp(), self.machine_id(), self.next_sequence())
}
#[must_use]
fn rollover_to_timestamp(&self, ts: Self::Ty) -> Self {
Self::from_components(ts, self.machine_id(), Self::ZERO)
}
fn is_valid(&self) -> bool;
#[must_use]
fn into_valid(self) -> Self;
}
#[cfg_attr(docsrs, doc(cfg(feature = "snowflake")))]
#[macro_export]
macro_rules! define_snowflake_id {
(
$(#[$meta:meta])*
$name:ident, $int:ty,
reserved: $reserved_bits:expr,
timestamp: $timestamp_bits:expr,
machine_id: $machine_bits:expr,
sequence: $sequence_bits:expr
) => {
$(#[$meta])*
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct $name {
id: $int,
}
const _: () = {
assert!(
$reserved_bits + $timestamp_bits + $machine_bits + $sequence_bits == <$int>::BITS,
"Layout must match underlying type width"
);
};
impl $name {
pub const RESERVED_BITS: $int = $reserved_bits;
pub const TIMESTAMP_BITS: $int = $timestamp_bits;
pub const MACHINE_ID_BITS: $int = $machine_bits;
pub const SEQUENCE_BITS: $int = $sequence_bits;
pub const SEQUENCE_SHIFT: $int = 0;
pub const MACHINE_ID_SHIFT: $int = Self::SEQUENCE_SHIFT + Self::SEQUENCE_BITS;
pub const TIMESTAMP_SHIFT: $int = Self::MACHINE_ID_SHIFT + Self::MACHINE_ID_BITS;
pub const RESERVED_SHIFT: $int = Self::TIMESTAMP_SHIFT + Self::TIMESTAMP_BITS;
pub const RESERVED_MASK: $int = ((1 << Self::RESERVED_BITS) - 1);
pub const TIMESTAMP_MASK: $int = ((1 << Self::TIMESTAMP_BITS) - 1);
pub const MACHINE_ID_MASK: $int = ((1 << Self::MACHINE_ID_BITS) - 1);
pub const SEQUENCE_MASK: $int = ((1 << Self::SEQUENCE_BITS) - 1);
const fn valid_mask() -> $int {
(Self::TIMESTAMP_MASK << Self::TIMESTAMP_SHIFT) |
(Self::MACHINE_ID_MASK << Self::MACHINE_ID_SHIFT) |
(Self::SEQUENCE_MASK << Self::SEQUENCE_SHIFT)
}
#[must_use]
pub const fn from_components(timestamp: $int, machine_id: $int, sequence: $int) -> Self {
debug_assert!(timestamp <= Self::TIMESTAMP_MASK, "timestamp overflow");
debug_assert!(machine_id <= Self::MACHINE_ID_MASK, "machine_id overflow");
debug_assert!(sequence <= Self::SEQUENCE_MASK, "sequence overflow");
let t = (timestamp & Self::TIMESTAMP_MASK) << Self::TIMESTAMP_SHIFT;
let m = (machine_id & Self::MACHINE_ID_MASK) << Self::MACHINE_ID_SHIFT;
let s = (sequence & Self::SEQUENCE_MASK) << Self::SEQUENCE_SHIFT;
Self { id: t | m | s }
}
#[must_use]
pub const fn timestamp(&self) -> $int {
(self.id >> Self::TIMESTAMP_SHIFT) & Self::TIMESTAMP_MASK
}
#[must_use]
pub const fn machine_id(&self) -> $int {
(self.id >> Self::MACHINE_ID_SHIFT) & Self::MACHINE_ID_MASK
}
#[must_use]
pub const fn sequence(&self) -> $int {
(self.id >> Self::SEQUENCE_SHIFT) & Self::SEQUENCE_MASK
}
#[must_use]
pub const fn max_timestamp() -> $int {
Self::TIMESTAMP_MASK
}
#[must_use]
pub const fn max_machine_id() -> $int {
Self::MACHINE_ID_MASK
}
#[must_use]
pub const fn max_sequence() -> $int {
Self::SEQUENCE_MASK
}
#[must_use]
pub const fn to_raw(&self) -> $int {
self.id
}
#[must_use]
pub const fn from_raw(raw: $int) -> Self {
Self { id: raw }
}
}
impl $crate::id::Id for $name {
type Ty = $int;
const ZERO: $int = 0;
const ONE: $int = 1;
fn to_raw(&self) -> Self::Ty {
self.to_raw()
}
fn from_raw(raw: Self::Ty) -> Self {
Self::from_raw(raw)
}
}
impl $crate::id::SnowflakeId for $name {
fn timestamp(&self) -> Self::Ty {
self.timestamp()
}
fn machine_id(&self) -> Self::Ty {
self.machine_id()
}
fn sequence(&self) -> Self::Ty {
self.sequence()
}
fn max_timestamp() -> Self::Ty {
Self::TIMESTAMP_MASK
}
fn max_machine_id() -> Self::Ty {
Self::MACHINE_ID_MASK
}
fn max_sequence() -> Self::Ty {
Self::SEQUENCE_MASK
}
fn from_components(timestamp: $int, machine_id: $int, sequence: $int) -> Self {
Self::from_components(timestamp, machine_id, sequence)
}
fn is_valid(&self) -> bool {
(self.to_raw() & !Self::valid_mask()) == 0
}
fn into_valid(self) -> Self {
let raw = self.to_raw() & Self::valid_mask();
Self::from_raw(raw)
}
}
$crate::cfg_base32! {
impl core::fmt::Display for $name {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
use $crate::base32::Base32SnowExt;
self.encode().fmt(f)
}
}
impl PartialEq<str> for $name {
fn eq(&self, other: &str) -> bool {
use $crate::base32::Base32SnowExt;
Self::decode(other).map(|id| id == *self).unwrap_or(false)
}
}
impl PartialEq<&str> for $name {
fn eq(&self, other: &&str) -> bool {
self == *other
}
}
impl PartialEq<$name> for &str {
fn eq(&self, other: &$name) -> bool {
other == *self
}
}
$crate::cfg_alloc! {
impl PartialEq<$crate::__internal::String> for $name {
fn eq(&self, other: &$crate::__internal::String) -> bool {
self == other.as_str()
}
}
impl PartialEq<$name> for $crate::__internal::String {
fn eq(&self, other: &$name) -> bool {
other == self
}
}
impl From<$name> for $crate::__internal::String {
fn from(val: $name) -> Self {
use $crate::base32::Base32SnowExt;
val.encode().as_string()
}
}
impl From<&$name> for $crate::__internal::String {
fn from(val: &$name) -> Self {
use $crate::base32::Base32SnowExt;
val.encode().as_string()
}
}
}
impl core::convert::TryFrom<&str> for $name {
type Error = $crate::base32::Error<$name>;
fn try_from(s: &str) -> Result<Self, Self::Error> {
use $crate::base32::Base32SnowExt;
Self::decode(s)
}
}
impl core::str::FromStr for $name {
type Err = $crate::base32::Error<$name>;
fn from_str(s: &str) -> Result<Self, Self::Err> {
use $crate::base32::Base32SnowExt;
Self::decode(s)
}
}
}
impl core::fmt::Debug for $name {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let full = core::any::type_name::<Self>();
let name = full.rsplit("::").next().unwrap_or(full);
let mut dbg = f.debug_struct(name);
dbg.field("id", &format_args!("{:} (0x{:x})", self.to_raw(), self.to_raw()));
dbg.field("timestamp", &format_args!("{:} (0x{:x})", self.timestamp(), self.timestamp()));
dbg.field("machine_id", &format_args!("{:} (0x{:x})", self.machine_id(), self.machine_id()));
dbg.field("sequence", &format_args!("{:} (0x{:x})", self.sequence(), self.sequence()));
dbg.finish()
}
}
};
}
define_snowflake_id!(
SnowflakeTwitterId, u64,
reserved: 1,
timestamp: 41,
machine_id: 10,
sequence: 12
);
define_snowflake_id!(
SnowflakeDiscordId, u64,
reserved: 0,
timestamp: 42,
machine_id: 10,
sequence: 12
);
define_snowflake_id!(
SnowflakeMastodonId, u64,
reserved: 0,
timestamp: 48,
machine_id: 0,
sequence: 16
);
define_snowflake_id!(
SnowflakeInstagramId, u64,
reserved: 0,
timestamp: 41,
machine_id: 13,
sequence: 10
);
#[cfg(all(test, feature = "std"))]
mod tests {
use std::println;
use super::*;
#[test]
fn test_snowflake_twitter_id_fields_and_bounds() {
let ts = SnowflakeTwitterId::max_timestamp();
let mid = SnowflakeTwitterId::max_machine_id();
let seq = SnowflakeTwitterId::max_sequence();
let id = SnowflakeTwitterId::from_components(ts, mid, seq);
println!("ID: {id:#?}");
assert_eq!(id.timestamp(), ts);
assert_eq!(id.machine_id(), mid);
assert_eq!(id.sequence(), seq);
assert_eq!(SnowflakeTwitterId::from_components(ts, mid, seq), id);
}
#[test]
fn test_snowflake_discord_id_fields_and_bounds() {
let ts = SnowflakeDiscordId::max_timestamp();
let mid = SnowflakeDiscordId::max_machine_id();
let seq = SnowflakeDiscordId::max_sequence();
let id = SnowflakeDiscordId::from_components(ts, mid, seq);
println!("ID: {id:#?}");
assert_eq!(id.timestamp(), ts);
assert_eq!(id.machine_id(), mid);
assert_eq!(id.sequence(), seq);
assert_eq!(SnowflakeDiscordId::from_components(ts, mid, seq), id);
}
#[test]
fn test_snowflake_mastodon_id_fields_and_bounds() {
let ts = SnowflakeMastodonId::max_timestamp();
let mid = SnowflakeMastodonId::max_machine_id();
let seq = SnowflakeMastodonId::max_sequence();
let id = SnowflakeMastodonId::from_components(ts, mid, seq);
println!("ID: {id:#?}");
assert_eq!(id.timestamp(), ts);
assert_eq!(id.machine_id(), 0); assert_eq!(id.sequence(), seq);
assert_eq!(SnowflakeMastodonId::from_components(ts, 0, seq), id);
}
#[test]
fn test_snowflake_instagram_id_fields_and_bounds() {
let ts = SnowflakeInstagramId::max_timestamp();
let mid = SnowflakeInstagramId::max_machine_id();
let seq = SnowflakeInstagramId::max_sequence();
let id = SnowflakeInstagramId::from_components(ts, mid, seq);
println!("ID: {id:#?}");
assert_eq!(id.timestamp(), ts);
assert_eq!(id.machine_id(), mid);
assert_eq!(id.sequence(), seq);
assert_eq!(SnowflakeInstagramId::from_components(ts, mid, seq), id);
}
#[test]
#[should_panic(expected = "timestamp overflow")]
fn twitter_timestamp_overflow_panics() {
let ts = SnowflakeTwitterId::max_timestamp() + 1;
let _ = SnowflakeTwitterId::from_components(ts, 0, 0);
}
#[test]
#[should_panic(expected = "machine_id overflow")]
fn twitter_machine_id_overflow_panics() {
let mid = SnowflakeTwitterId::max_machine_id() + 1;
let _ = SnowflakeTwitterId::from_components(0, mid, 0);
}
#[test]
#[should_panic(expected = "sequence overflow")]
fn twitter_sequence_overflow_panics() {
let seq = SnowflakeTwitterId::max_sequence() + 1;
let _ = SnowflakeTwitterId::from_components(0, 0, seq);
}
#[test]
#[should_panic(expected = "timestamp overflow")]
fn discord_timestamp_overflow_panics() {
let ts = SnowflakeDiscordId::max_timestamp() + 1;
let _ = SnowflakeDiscordId::from_components(ts, 0, 0);
}
#[test]
#[should_panic(expected = "machine_id overflow")]
fn discord_machine_id_overflow_panics() {
let mid = SnowflakeDiscordId::max_machine_id() + 1;
let _ = SnowflakeDiscordId::from_components(0, mid, 0);
}
#[test]
#[should_panic(expected = "sequence overflow")]
fn discord_sequence_overflow_panics() {
let seq = SnowflakeDiscordId::max_sequence() + 1;
let _ = SnowflakeDiscordId::from_components(0, 0, seq);
}
#[test]
#[should_panic(expected = "timestamp overflow")]
fn mastodon_timestamp_overflow_panics() {
let ts = SnowflakeMastodonId::max_timestamp() + 1;
let _ = SnowflakeMastodonId::from_components(ts, 0, 0);
}
#[test]
#[should_panic(expected = "machine_id overflow")]
fn mastodon_machine_id_overflow_panics() {
let mid = SnowflakeMastodonId::max_machine_id() + 1;
let _ = SnowflakeMastodonId::from_components(0, mid, 0);
}
#[test]
#[should_panic(expected = "sequence overflow")]
fn mastodon_sequence_overflow_panics() {
let seq = SnowflakeMastodonId::max_sequence() + 1;
let _ = SnowflakeMastodonId::from_components(0, 0, seq);
}
#[test]
#[should_panic(expected = "timestamp overflow")]
fn instagram_timestamp_overflow_panics() {
let ts = SnowflakeInstagramId::max_timestamp() + 1;
let _ = SnowflakeInstagramId::from_components(ts, 0, 0);
}
#[test]
#[should_panic(expected = "machine_id overflow")]
fn instagram_machine_id_overflow_panics() {
let mid = SnowflakeInstagramId::max_machine_id() + 1;
let _ = SnowflakeInstagramId::from_components(0, mid, 0);
}
#[test]
#[should_panic(expected = "sequence overflow")]
fn instagram_sequence_overflow_panics() {
let seq = SnowflakeInstagramId::max_sequence() + 1;
let _ = SnowflakeInstagramId::from_components(0, 0, seq);
}
#[test]
fn twitter_validity() {
let id = SnowflakeTwitterId::from_raw(u64::MAX);
assert!(!id.is_valid()); let valid = id.into_valid();
assert!(valid.is_valid());
}
#[test]
fn instagram_validity() {
let id = SnowflakeInstagramId::from_raw(u64::MAX);
assert!(id.is_valid());
let valid = id.into_valid();
assert!(valid.is_valid());
}
#[test]
fn discord_validity() {
let id = SnowflakeDiscordId::from_raw(u64::MAX);
assert!(id.is_valid());
let valid = id.into_valid();
assert!(valid.is_valid());
}
#[test]
fn mastodon_validity() {
let id = SnowflakeMastodonId::from_raw(u64::MAX);
assert!(id.is_valid());
let valid = id.into_valid();
assert!(valid.is_valid());
}
#[test]
fn twitter_low_bit_fields() {
let id = SnowflakeTwitterId::from_components(0, 0, 0);
assert_eq!(id.timestamp(), 0);
assert_eq!(id.machine_id(), 0);
assert_eq!(id.sequence(), 0);
let id = SnowflakeTwitterId::from_components(1, 1, 1);
assert_eq!(id.timestamp(), 1);
assert_eq!(id.machine_id(), 1);
assert_eq!(id.sequence(), 1);
}
#[test]
fn discord_low_bit_fields() {
let id = SnowflakeDiscordId::from_components(0, 0, 0);
assert_eq!(id.timestamp(), 0);
assert_eq!(id.machine_id(), 0);
assert_eq!(id.sequence(), 0);
let id = SnowflakeDiscordId::from_components(1, 1, 1);
assert_eq!(id.timestamp(), 1);
assert_eq!(id.machine_id(), 1);
assert_eq!(id.sequence(), 1);
}
#[test]
fn mastodon_low_bit_fields() {
let id = SnowflakeMastodonId::from_components(0, 0, 0);
assert_eq!(id.timestamp(), 0);
assert_eq!(id.machine_id(), 0);
assert_eq!(id.sequence(), 0);
let id = SnowflakeMastodonId::from_components(1, 0, 1);
assert_eq!(id.timestamp(), 1);
assert_eq!(id.machine_id(), 0); assert_eq!(id.sequence(), 1);
}
#[test]
fn instagram_low_bit_fields() {
let id = SnowflakeInstagramId::from_components(0, 0, 0);
assert_eq!(id.timestamp(), 0);
assert_eq!(id.machine_id(), 0);
assert_eq!(id.sequence(), 0);
let id = SnowflakeInstagramId::from_components(1, 1, 1);
assert_eq!(id.timestamp(), 1);
assert_eq!(id.machine_id(), 1);
assert_eq!(id.sequence(), 1);
}
#[test]
fn twitter_edge_rollover() {
let id = SnowflakeTwitterId::from_components(0, 0, SnowflakeTwitterId::max_sequence());
assert_eq!(id.sequence(), SnowflakeTwitterId::max_sequence());
let id = SnowflakeTwitterId::from_components(0, SnowflakeTwitterId::max_machine_id(), 0);
assert_eq!(id.machine_id(), SnowflakeTwitterId::max_machine_id());
}
#[test]
fn discord_edge_rollover() {
let id = SnowflakeDiscordId::from_components(0, 0, SnowflakeDiscordId::max_sequence());
assert_eq!(id.sequence(), SnowflakeDiscordId::max_sequence());
let id = SnowflakeDiscordId::from_components(0, SnowflakeDiscordId::max_machine_id(), 0);
assert_eq!(id.machine_id(), SnowflakeDiscordId::max_machine_id());
}
#[test]
fn mastodon_edge_rollover() {
let id = SnowflakeMastodonId::from_components(0, 0, SnowflakeMastodonId::max_sequence());
assert_eq!(id.sequence(), SnowflakeMastodonId::max_sequence());
}
#[test]
fn instagram_edge_rollover() {
let id = SnowflakeInstagramId::from_components(0, 0, SnowflakeInstagramId::max_sequence());
assert_eq!(id.sequence(), SnowflakeInstagramId::max_sequence());
let id =
SnowflakeInstagramId::from_components(0, SnowflakeInstagramId::max_machine_id(), 0);
assert_eq!(id.machine_id(), SnowflakeInstagramId::max_machine_id());
}
}