use std::{
fmt::{self, Display, Formatter},
num::ParseIntError,
str::FromStr,
};
#[cfg(feature = "serde")]
use serde::{de::Unexpected, Deserialize, Deserializer, Serialize, Serializer};
use crate::date_time::UtcDateTime;
pub mod generator;
#[cfg(feature = "postgres")]
mod postgres;
const TIMESTAMP_BITS: i64 = 64;
const WORKER_BITS: i64 = 16;
const SEQUENCE_BITS: i64 = 16;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(
all(
target_family = "wasm",
not(any(target_os = "emscripten", target_os = "wasi")),
feature = "wasm"
),
derive(bomboni_wasm::Wasm),
wasm(
bomboni_crate = crate::bomboni,
wasm_abi,
js_value { convert_string },
)
)]
pub struct Id(u128);
impl Id {
#[must_use]
pub const fn new(id: u128) -> Self {
Self(id)
}
#[must_use]
pub fn from_parts(time: UtcDateTime, worker: u16, sequence: u16) -> Self {
let timestamp = time.unix_timestamp() as u128;
let worker = u128::from(worker);
let sequence = u128::from(sequence);
assert!(timestamp < (1 << TIMESTAMP_BITS));
assert!(worker < (1 << WORKER_BITS));
assert!(sequence < (1 << SEQUENCE_BITS));
Self(
(timestamp & ((1 << TIMESTAMP_BITS) - 1)) << (WORKER_BITS + SEQUENCE_BITS)
| ((worker & ((1 << WORKER_BITS) - 1)) << SEQUENCE_BITS)
| (sequence & ((1 << SEQUENCE_BITS) - 1)),
)
}
#[must_use]
pub fn decode(self) -> (UtcDateTime, u16, u16) {
let timestamp =
UtcDateTime::from_timestamp((self.0 >> (WORKER_BITS + SEQUENCE_BITS)) as i64, 0)
.unwrap();
let worker = ((self.0 >> SEQUENCE_BITS) & ((1 << WORKER_BITS) - 1)) as u16;
let sequence = (self.0 & ((1 << SEQUENCE_BITS) - 1)) as u16;
(timestamp, worker, sequence)
}
}
impl Display for Id {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{:x}", self.0)
}
}
impl FromStr for Id {
type Err = ParseIntError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let value = u128::from_str_radix(s, 16)?;
Ok(Self::new(value))
}
}
macro_rules! impl_from {
( $( $source:ty ),* $(,)? ) => {
$(impl From<$source> for Id {
fn from(x: $source) -> Self {
Id::new(x as u128)
}
})*
};
}
impl_from!(i8, i16, i32, i64, i128, u8, u16, u32, u64);
impl From<Id> for u128 {
fn from(id: Id) -> Self {
id.0
}
}
#[cfg(feature = "serde")]
impl Serialize for Id {
fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for Id {
fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
where
D: Deserializer<'de>,
{
use serde::de::Error;
let value = String::deserialize(deserializer)?;
value.parse::<Self>().map_err(|_| {
<D as Deserializer<'de>>::Error::invalid_value(Unexpected::Str(value.as_str()), &"Id")
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let ts = UtcDateTime::from_timestamp(10, 0).unwrap();
let id = Id::from_parts(ts, 1, 1);
assert_eq!(id, Id(0b1010_0000_0000_0000_0001_0000_0000_0000_0001));
let (timestamp, worker, sequence) = id.decode();
assert_eq!(timestamp, ts);
assert_eq!(worker, 1);
assert_eq!(sequence, 1);
}
#[cfg(feature = "serde")]
#[test]
fn serialize() {
let id = Id::from_parts(UtcDateTime::from_timestamp(3, 0).unwrap(), 5, 7);
assert_eq!(serde_json::to_string(&id).unwrap(), r#""300050007""#);
}
}