use std::borrow;
use std::num::NonZeroU128;
use std::sync::atomic::{self, AtomicU64};
static NEXT_ID: AtomicU64 = AtomicU64::new(1);
static NEXT_WINDOW_ID: AtomicU64 = AtomicU64::new(1);
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Id(pub Internal);
impl Id {
pub fn new(id: impl Into<borrow::Cow<'static, str>>) -> Self {
Self(Internal::Custom(Self::next(), id.into()))
}
pub fn reset() {
NEXT_ID.store(1, atomic::Ordering::Relaxed);
}
fn next() -> u64 {
NEXT_ID.fetch_add(1, atomic::Ordering::Relaxed)
}
pub fn unique() -> Self {
let id = Self::next();
Self(Internal::Unique(id))
}
}
impl From<u64> for Id {
fn from(value: u64) -> Self {
Self(Internal::Unique(value))
}
}
impl From<Id> for NonZeroU128 {
fn from(id: Id) -> NonZeroU128 {
match &id.0 {
Internal::Unique(id) => NonZeroU128::try_from(*id as u128).unwrap(),
Internal::Custom(id, _) => {
NonZeroU128::try_from(*id as u128).unwrap()
}
Internal::Set(_) => {
panic!("Cannot convert a set id to a NonZeroU128")
}
}
}
}
impl ToString for Id {
fn to_string(&self) -> String {
match &self.0 {
Internal::Unique(_) => "Undefined".to_string(),
Internal::Custom(_, id) => id.to_string(),
Internal::Set(_) => "Set".to_string(),
}
}
}
pub fn window_node_id() -> NonZeroU128 {
std::num::NonZeroU128::try_from(
u64::MAX as u128
+ NEXT_WINDOW_ID.fetch_add(1, atomic::Ordering::Relaxed) as u128,
)
.unwrap()
}
#[derive(Debug, Clone, Eq)]
pub enum Internal {
Unique(u64),
Custom(u64, borrow::Cow<'static, str>),
Set(Vec<Self>),
}
impl PartialEq for Internal {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Unique(l0), Self::Unique(r0)) => l0 == r0,
(Self::Custom(_, l1), Self::Custom(_, r1)) => l1 == r1,
(Self::Set(l0), Self::Set(r0)) => l0 == r0,
_ => false,
}
}
}
pub trait IdEq {
fn eq(&self, other: &Self) -> bool;
}
impl IdEq for Internal {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Unique(l0), Self::Unique(r0)) => l0 == r0,
(Self::Custom(l0, l1), Self::Custom(r0, r1)) => {
l0 == r0 || l1 == r1
}
(Self::Unique(l0), Self::Custom(r0, _))
| (Self::Custom(l0, _), Self::Unique(r0)) => l0 == r0,
(Self::Set(l0), Self::Set(r0)) => l0 == r0,
(Self::Set(l0), r) | (r, Self::Set(l0)) => {
l0.iter().any(|l| l == r)
}
}
}
}
impl std::hash::Hash for Internal {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
match self {
Self::Unique(id) => id.hash(state),
Self::Custom(name, _) => name.hash(state),
Self::Set(ids) => ids.hash(state),
}
}
}
#[cfg(test)]
mod tests {
use super::Id;
#[test]
fn unique_generates_different_ids() {
let a = Id::unique();
let b = Id::unique();
assert_ne!(a, b);
}
}