use std::time::{Duration, SystemTime, UNIX_EPOCH};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
pub const FLUXER_EPOCH_MS: u64 = 1_420_070_400_000;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Snowflake(u64);
impl Snowflake {
pub const fn new(value: u64) -> Self {
Self(value)
}
pub const fn from_parts(
timestamp_ms: u64,
worker_id: u8,
process_id: u8,
increment: u16,
) -> Self {
let value = (timestamp_ms << 22)
| ((worker_id as u64 & 0x1F) << 17)
| ((process_id as u64 & 0x1F) << 12)
| (increment as u64 & 0xFFF);
Self(value)
}
pub const fn value(self) -> u64 {
self.0
}
pub const fn timestamp_ms(self) -> u64 {
self.0 >> 22
}
pub const fn unix_timestamp_ms(self) -> u64 {
self.timestamp_ms() + FLUXER_EPOCH_MS
}
pub fn created_at(self) -> SystemTime {
UNIX_EPOCH + Duration::from_millis(self.unix_timestamp_ms())
}
pub const fn worker_id(self) -> u8 {
((self.0 >> 17) & 0x1F) as u8
}
pub const fn process_id(self) -> u8 {
((self.0 >> 12) & 0x1F) as u8
}
pub const fn increment(self) -> u16 {
(self.0 & 0xFFF) as u16
}
pub fn is_valid(self) -> bool {
if self.0 == 0 {
return false;
}
let Ok(duration_since_epoch) = SystemTime::now().duration_since(UNIX_EPOCH) else {
return false;
};
let now_ms = duration_since_epoch.as_millis() as u64;
let one_year_ms = 365 * 24 * 60 * 60 * 1000;
self.unix_timestamp_ms() <= now_ms + one_year_ms
}
pub fn epoch() -> SystemTime {
UNIX_EPOCH + Duration::from_millis(FLUXER_EPOCH_MS)
}
}
impl std::fmt::Display for Snowflake {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<u64> for Snowflake {
fn from(v: u64) -> Self {
Self(v)
}
}
impl From<Snowflake> for u64 {
fn from(sf: Snowflake) -> Self {
sf.0
}
}
impl Serialize for Snowflake {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(&self.0.to_string())
}
}
impl<'de> Deserialize<'de> for Snowflake {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
use serde::de;
struct Visitor;
impl<'de> de::Visitor<'de> for Visitor {
type Value = Snowflake;
fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("a snowflake ID as a string or number")
}
fn visit_u64<E: de::Error>(self, v: u64) -> Result<Snowflake, E> {
Ok(Snowflake(v))
}
fn visit_i64<E: de::Error>(self, v: i64) -> Result<Snowflake, E> {
u64::try_from(v)
.map(Snowflake)
.map_err(|_| E::custom(format!("negative snowflake: {v}")))
}
fn visit_str<E: de::Error>(self, v: &str) -> Result<Snowflake, E> {
v.parse::<u64>()
.map(Snowflake)
.map_err(|_| E::custom(format!("invalid snowflake string: {v}")))
}
}
deserializer.deserialize_any(Visitor)
}
}
#[macro_export]
macro_rules! snowflake_id {
($(#[$meta:meta])* $vis:vis $name:ident) => {
$(#[$meta])*
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
$vis struct $name(pub $crate::Snowflake);
impl $name {
pub const fn new(value: u64) -> Self {
Self($crate::Snowflake::new(value))
}
}
impl std::ops::Deref for $name {
type Target = $crate::Snowflake;
fn deref(&self) -> &$crate::Snowflake {
&self.0
}
}
impl std::fmt::Display for $name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl From<u64> for $name {
fn from(v: u64) -> Self {
Self($crate::Snowflake::new(v))
}
}
impl From<$crate::Snowflake> for $name {
fn from(sf: $crate::Snowflake) -> Self {
Self(sf)
}
}
impl From<$name> for u64 {
fn from(id: $name) -> Self {
id.0.value()
}
}
impl From<$name> for $crate::Snowflake {
fn from(id: $name) -> Self {
id.0
}
}
impl serde::Serialize for $name {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.0.serialize(serializer)
}
}
impl<'de> serde::Deserialize<'de> for $name {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
$crate::Snowflake::deserialize(deserializer).map(Self)
}
}
};
}
#[cfg(test)]
mod tests {
use super::*;
const EXAMPLE: u64 = 1491150377518758430;
#[test]
fn parts_roundtrip() {
let sf = Snowflake::new(EXAMPLE);
let rebuilt = Snowflake::from_parts(
sf.timestamp_ms(),
sf.worker_id(),
sf.process_id(),
sf.increment(),
);
assert_eq!(sf, rebuilt);
}
#[test]
fn timestamp() {
let sf = Snowflake::new(EXAMPLE);
let ts = sf.unix_timestamp_ms();
assert!(ts > 1_700_000_000_000);
assert!(ts < 2_000_000_000_000);
}
#[test]
fn worker_and_process_in_range() {
let sf = Snowflake::new(EXAMPLE);
assert!(sf.worker_id() <= 31);
assert!(sf.process_id() <= 31);
assert!(sf.increment() <= 4095);
}
#[test]
fn validation() {
assert!(!Snowflake::new(0).is_valid());
assert!(Snowflake::new(EXAMPLE).is_valid());
assert!(!Snowflake::new(u64::MAX).is_valid());
}
#[test]
fn from_parts() {
let sf = Snowflake::from_parts(1000, 5, 10, 42);
assert_eq!(sf.timestamp_ms(), 1000);
assert_eq!(sf.worker_id(), 5);
assert_eq!(sf.process_id(), 10);
assert_eq!(sf.increment(), 42);
}
#[test]
fn serde_from_string() {
let sf: Snowflake = serde_json::from_str("\"1491150377518758430\"").unwrap();
assert_eq!(sf.value(), EXAMPLE);
}
#[test]
fn serde_from_number() {
let sf: Snowflake = serde_json::from_str("1491150377518758430").unwrap();
assert_eq!(sf.value(), EXAMPLE);
}
#[test]
fn serde_serializes_as_string() {
let sf = Snowflake::new(EXAMPLE);
let json = serde_json::to_string(&sf).unwrap();
assert_eq!(json, format!("\"{EXAMPLE}\""));
}
snowflake_id!(pub TestId);
#[test]
fn typed_id_deref() {
let id = TestId::new(EXAMPLE);
assert_eq!(id.timestamp_ms(), Snowflake::new(EXAMPLE).timestamp_ms());
assert_eq!(id.worker_id(), Snowflake::new(EXAMPLE).worker_id());
assert!(id.is_valid());
}
#[test]
fn typed_id_from_u64() {
let id: TestId = EXAMPLE.into();
assert_eq!(id.value(), EXAMPLE);
}
#[test]
fn typed_id_serde_roundtrip() {
let id = TestId::new(EXAMPLE);
let json = serde_json::to_string(&id).unwrap();
let parsed: TestId = serde_json::from_str(&json).unwrap();
assert_eq!(id, parsed);
}
}