use std::{
fmt,
num::NonZero,
str::FromStr,
time::{Duration, SystemTime},
};
use crate::{Error, RANDOM_BITS, RANDOM_MASK, ZeroableUlid, base32, generator, util};
#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct Ulid(NonZero<u128>);
impl Ulid {
pub const MIN: Self = unsafe { Self::from_u128_unchecked(1) };
pub const MAX: Self = unsafe { Self::from_u128_unchecked(u128::MAX) };
#[must_use]
pub fn new() -> Self {
Self(NonZero::new(generator::generate().unwrap()).unwrap())
}
#[must_use]
pub const fn timestamp(self) -> u64 {
(self.0.get() >> RANDOM_BITS) as u64
}
#[must_use]
pub const fn randomness(self) -> u128 {
self.0.get() & RANDOM_MASK
}
#[must_use]
pub fn datetime(self) -> SystemTime {
SystemTime::UNIX_EPOCH + Duration::from_millis(self.timestamp())
}
#[must_use]
pub const fn to_zeroable_ulid(self) -> ZeroableUlid {
ZeroableUlid::from_u128(self.0.get())
}
#[must_use]
pub const fn from_zeroable_ulid(zeroable: ZeroableUlid) -> Option<Self> {
Self::from_u128(zeroable.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) => match Self::from_u128(n) {
Some(ulid) => Ok(ulid),
None => Err(Error::InvalidZero),
},
Err(error) => Err(error),
}
}
#[must_use]
pub const fn to_bytes(self) -> [u8; 16] {
self.0.get().to_be_bytes()
}
#[must_use]
pub const fn from_bytes(bytes: [u8; 16]) -> Option<Self> {
Self::from_u128(u128::from_be_bytes(bytes))
}
#[must_use]
pub const fn to_u128(self) -> u128 {
self.0.get()
}
#[must_use]
pub const fn from_u128(n: u128) -> Option<Self> {
match NonZero::new(n) {
Some(non_zero) => Some(Self(non_zero)),
None => None,
}
}
#[must_use]
pub const fn to_non_zero_u128(self) -> NonZero<u128> {
self.0
}
#[must_use]
pub const fn from_non_zero_u128(non_zero: NonZero<u128>) -> Self {
Self(non_zero)
}
#[must_use]
pub fn try_new() -> Option<Self> {
Some(Self(NonZero::new(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.get())
}
#[must_use]
pub const unsafe fn from_parts_unchecked(timestamp: u64, randomness: u128) -> Self {
let n = ((timestamp as u128) << RANDOM_BITS) | randomness;
Self(unsafe { NonZero::new_unchecked(n) })
}
#[must_use]
pub const unsafe fn from_u128_unchecked(n: u128) -> Self {
Self(unsafe { NonZero::new_unchecked(n) })
}
#[must_use]
pub const unsafe fn from_bytes_unchecked(bytes: [u8; 16]) -> Self {
let n = u128::from_be_bytes(bytes);
Self(unsafe { NonZero::new_unchecked(n) })
}
}
impl Default for Ulid {
fn default() -> Self {
Self::new()
}
}
impl fmt::Debug for Ulid {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
util::debug_ulid("Ulid", self.0.get(), f)
}
}
impl fmt::Display for Ulid {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut buffer = [0; 26];
f.write_str(base32::encode(self.0.get(), &mut buffer))
}
}
impl FromStr for Ulid {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let buffer = util::as_array(s.as_bytes())?;
Self::from_u128(base32::decode(buffer)?).ok_or(Error::InvalidZero)
}
}
impl TryFrom<ZeroableUlid> for Ulid {
type Error = Error;
fn try_from(zeroable: ZeroableUlid) -> Result<Self, Self::Error> {
Self::from_u128(zeroable.to_u128()).ok_or(Error::InvalidZero)
}
}
impl From<Ulid> for u128 {
fn from(ulid: Ulid) -> Self {
ulid.to_u128()
}
}
impl TryFrom<u128> for Ulid {
type Error = Error;
fn try_from(n: u128) -> Result<Self, Self::Error> {
Self::from_u128(n).ok_or(Error::InvalidZero)
}
}
impl From<Ulid> for NonZero<u128> {
fn from(ulid: Ulid) -> Self {
ulid.to_non_zero_u128()
}
}
impl From<NonZero<u128>> for Ulid {
fn from(non_zero: NonZero<u128>) -> Self {
Self::from_non_zero_u128(non_zero)
}
}
impl From<Ulid> for [u8; 16] {
fn from(ulid: Ulid) -> Self {
ulid.to_bytes()
}
}
impl TryFrom<[u8; 16]> for Ulid {
type Error = Error;
fn try_from(bytes: [u8; 16]) -> Result<Self, Self::Error> {
Self::from_bytes(bytes).ok_or(Error::InvalidZero)
}
}
impl TryFrom<&[u8; 16]> for Ulid {
type Error = Error;
fn try_from(bytes: &[u8; 16]) -> Result<Self, Self::Error> {
Self::from_bytes(*bytes).ok_or(Error::InvalidZero)
}
}
impl TryFrom<&[u8]> for Ulid {
type Error = Error;
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
Self::from_bytes(*util::as_array(bytes)?).ok_or(Error::InvalidZero)
}
}