use std::{
fmt,
str::FromStr,
time::{Duration, SystemTime},
};
use crate::{Error, RANDOM_BITS, RANDOM_MASK, Ulid, base32, generator, util};
#[allow(clippy::module_name_repetitions)]
#[derive(Default, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct ZeroableUlid(u128);
impl ZeroableUlid {
pub const MIN: Self = Self(0);
pub const MAX: Self = Self(u128::MAX);
#[must_use]
pub const fn zeroed() -> Self {
Self(0)
}
#[must_use]
pub fn new() -> Self {
Self(generator::generate().unwrap())
}
#[must_use]
pub const fn is_zero(self) -> bool {
self.0 == 0
}
#[must_use]
pub const fn timestamp(self) -> u64 {
(self.0 >> RANDOM_BITS) as u64
}
#[must_use]
pub const fn randomness(self) -> u128 {
self.0 & RANDOM_MASK
}
#[must_use]
pub fn datetime(self) -> SystemTime {
SystemTime::UNIX_EPOCH + Duration::from_millis(self.timestamp())
}
#[must_use]
pub const fn to_ulid(self) -> Option<Ulid> {
Ulid::from_u128(self.0)
}
#[must_use]
pub const fn from_ulid(ulid: Ulid) -> Self {
Self::from_u128(ulid.to_u128())
}
#[must_use]
pub const fn to_parts(self) -> (u64, u128) {
(self.timestamp(), self.randomness())
}
pub const fn from_parts(timestamp: u64, randomness: u128) -> Result<Self, Error> {
match util::from_parts(timestamp, randomness) {
Ok(n) => Ok(Self::from_u128(n)),
Err(err) => Err(err),
}
}
#[must_use]
pub const fn to_bytes(self) -> [u8; 16] {
self.0.to_be_bytes()
}
#[must_use]
pub const fn from_bytes(bytes: [u8; 16]) -> Self {
Self::from_u128(u128::from_be_bytes(bytes))
}
#[must_use]
pub const fn to_u128(self) -> u128 {
self.0
}
#[must_use]
pub const fn from_u128(n: u128) -> Self {
Self(n)
}
#[must_use]
pub fn try_new() -> Option<Self> {
Some(Self(generator::generate()?))
}
#[must_use]
pub fn try_datetime(self) -> Option<SystemTime> {
SystemTime::UNIX_EPOCH.checked_add(Duration::from_millis(self.timestamp()))
}
#[must_use]
pub fn try_to_string(self) -> Option<String> {
util::try_to_string(self.0)
}
#[must_use]
pub const unsafe fn from_parts_unchecked(timestamp: u64, randomness: u128) -> Self {
Self(((timestamp as u128) << RANDOM_BITS) | randomness)
}
}
impl fmt::Debug for ZeroableUlid {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
util::debug_ulid("ZeroableUlid", self.0, f)
}
}
impl fmt::Display for ZeroableUlid {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut buffer = [0; 26];
f.write_str(base32::encode(self.0, &mut buffer))
}
}
impl FromStr for ZeroableUlid {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let buffer = util::as_array(s.as_bytes())?;
Ok(Self::from_u128(base32::decode(buffer)?))
}
}
impl From<Ulid> for ZeroableUlid {
fn from(non_zero: Ulid) -> Self {
Self::from_u128(non_zero.to_u128())
}
}
impl From<ZeroableUlid> for u128 {
fn from(ulid: ZeroableUlid) -> Self {
ulid.to_u128()
}
}
impl From<u128> for ZeroableUlid {
fn from(n: u128) -> Self {
Self::from_u128(n)
}
}
impl From<ZeroableUlid> for [u8; 16] {
fn from(ulid: ZeroableUlid) -> Self {
ulid.to_bytes()
}
}
impl From<[u8; 16]> for ZeroableUlid {
fn from(bytes: [u8; 16]) -> Self {
Self::from_bytes(bytes)
}
}
impl From<&[u8; 16]> for ZeroableUlid {
fn from(bytes: &[u8; 16]) -> Self {
Self::from_bytes(*bytes)
}
}
impl TryFrom<&[u8]> for ZeroableUlid {
type Error = Error;
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
Ok(Self::from_bytes(*util::as_array(bytes)?))
}
}