use core::fmt;
use core::num::ParseIntError;
use core::str::FromStr;
use std::sync::OnceLock;
use crate::format::{Format, Layout};
use crate::generator::{self, GenerateError, Generator, GeneratorError};
use crate::node::{Node, NodeError};
mod sealed {
pub trait Sealed {}
impl Sealed for i64 {}
impl Sealed for u64 {}
}
pub trait Repr:
sealed::Sealed
+ Copy
+ fmt::Debug
+ fmt::Display
+ FromStr<Err = ParseIntError>
+ Send
+ Sync
+ 'static
{
#[doc(hidden)]
const USABLE_BITS: u8;
#[doc(hidden)]
fn __bits(self) -> u64;
#[doc(hidden)]
fn __checked_bits(self) -> Result<u64, InvalidId>;
#[doc(hidden)]
fn __from_bits(bits: u64) -> Self;
}
impl Repr for i64 {
const USABLE_BITS: u8 = 63;
fn __bits(self) -> u64 {
self as u64
}
fn __checked_bits(self) -> Result<u64, InvalidId> {
if self < 0 {
return Err(InvalidId::Negative { value: self });
}
Ok(self as u64)
}
fn __from_bits(bits: u64) -> Self {
bits as i64
}
}
impl Repr for u64 {
const USABLE_BITS: u8 = 64;
fn __bits(self) -> u64 {
self
}
fn __checked_bits(self) -> Result<u64, InvalidId> {
Ok(self)
}
fn __from_bits(bits: u64) -> Self {
bits
}
}
pub trait Id: Copy + Send + Sync + 'static {
type Repr: Repr;
type Node: Node;
const FORMAT: Format;
#[doc(hidden)]
const __LAYOUT: Layout = Layout::resolve::<Self::Repr, Self::Node>(Self::FORMAT);
#[doc(hidden)]
fn __from_repr(repr: Self::Repr) -> Self;
#[doc(hidden)]
fn __default_generator() -> &'static OnceLock<Generator<Self>>;
fn get(self) -> Self::Repr;
fn generate() -> Result<Self, GenerateError> {
generator::default_generator::<Self>()?.generate()
}
fn generate_blocking() -> Result<Self, GenerateError> {
generator::default_generator::<Self>()?.generate_blocking()
}
#[cfg(feature = "tokio")]
fn generate_async() -> impl Future<Output = Result<Self, GenerateError>> + Send {
async {
generator::default_generator::<Self>()?
.generate_async()
.await
}
}
fn generator(node: Self::Node) -> Result<Generator<Self>, GeneratorError> {
Generator::new(node)
}
fn parts(self) -> Parts<Self::Node> {
let layout = Self::__LAYOUT;
let raw = self.get().__bits();
Parts {
elapsed_millis: raw >> layout.timestamp_shift,
node: Self::Node::unpack(
(raw >> layout.node_shift) & layout.node_max,
layout.node_bits,
),
sequence: raw & layout.sequence_max,
}
}
fn from_parts(parts: Parts<Self::Node>) -> Result<Self, InvalidId> {
let layout = Self::__LAYOUT;
if parts.elapsed_millis > layout.timestamp_max {
return Err(InvalidId::Timestamp {
value: parts.elapsed_millis,
max: layout.timestamp_max,
});
}
if parts.sequence > layout.sequence_max {
return Err(InvalidId::Sequence {
value: parts.sequence,
max: layout.sequence_max,
});
}
let node = parts.node.pack(layout.node_bits)?;
Ok(from_valid_bits(
(parts.elapsed_millis << layout.timestamp_shift)
| (node << layout.node_shift)
| parts.sequence,
))
}
fn unix_millis(self) -> Result<u64, TimestampError> {
let epoch_unix_millis = Self::__LAYOUT.epoch;
let elapsed_millis = self.parts().elapsed_millis;
epoch_unix_millis
.checked_add(elapsed_millis)
.ok_or(TimestampError {
epoch_unix_millis,
elapsed_millis,
})
}
}
pub(crate) fn from_valid_bits<I: Id>(bits: u64) -> I {
debug_assert!(bits <= I::__LAYOUT.raw_max);
I::__from_repr(I::Repr::__from_bits(bits))
}
#[doc(hidden)]
pub fn from_u64<I: Id>(raw: u64) -> Result<I, InvalidId> {
let max = I::__LAYOUT.raw_max;
if raw > max {
return Err(InvalidId::ReservedBits { value: raw, max });
}
Ok(from_valid_bits(raw))
}
#[doc(hidden)]
pub fn from_i64<I: Id>(raw: i64) -> Result<I, InvalidId> {
from_u64(raw.__checked_bits()?)
}
#[doc(hidden)]
pub fn parse<I: Id>(text: &str) -> Result<I, ParseIdError> {
let repr: I::Repr = text.parse().map_err(ParseIdError::Int)?;
Ok(from_u64(repr.__checked_bits()?)?)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Parts<N> {
pub elapsed_millis: u64,
pub node: N,
pub sequence: u64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum InvalidId {
Negative {
value: i64,
},
ReservedBits {
value: u64,
max: u64,
},
Timestamp {
value: u64,
max: u64,
},
Sequence {
value: u64,
max: u64,
},
Node(NodeError),
}
impl fmt::Display for InvalidId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Self::Negative { value } => write!(f, "ID {value} is negative"),
Self::ReservedBits { value, max } => write!(
f,
"ID {value} sets reserved bits; the format's maximum is {max}"
),
Self::Timestamp { value, max } => {
write!(f, "timestamp {value} exceeds the format's maximum of {max}")
}
Self::Sequence { value, max } => {
write!(f, "sequence {value} exceeds the format's maximum of {max}")
}
Self::Node(_) => f.write_str("node does not fit the format"),
}
}
}
impl std::error::Error for InvalidId {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Node(error) => Some(error),
_ => None,
}
}
}
impl From<NodeError> for InvalidId {
fn from(error: NodeError) -> Self {
Self::Node(error)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum ParseIdError {
Int(ParseIntError),
Invalid(InvalidId),
}
impl fmt::Display for ParseIdError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Int(_) => f.write_str("ID is not a valid integer"),
Self::Invalid(_) => f.write_str("integer is not a valid ID"),
}
}
}
impl std::error::Error for ParseIdError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Int(error) => Some(error),
Self::Invalid(error) => Some(error),
}
}
}
impl From<InvalidId> for ParseIdError {
fn from(error: InvalidId) -> Self {
Self::Invalid(error)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub struct TimestampError {
pub epoch_unix_millis: u64,
pub elapsed_millis: u64,
}
impl fmt::Display for TimestampError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"epoch {} plus {} elapsed milliseconds overflows",
self.epoch_unix_millis, self.elapsed_millis
)
}
}
impl std::error::Error for TimestampError {}