use std::fmt;
use std::num::NonZeroU32;
#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Handle(u64);
const _: () = {
assert!(
core::mem::size_of::<Handle>() == core::mem::size_of::<u64>(),
"Handle must stay layout-identical to u64 (bench transmute relies on repr(transparent))"
);
assert!(
core::mem::align_of::<Handle>() == core::mem::align_of::<u64>(),
"Handle must stay layout-identical to u64 (bench transmute relies on repr(transparent))"
);
};
impl Handle {
#[must_use]
pub const fn get(self) -> u64 {
self.0
}
}
impl From<u64> for Handle {
fn from(v: u64) -> Self {
Handle(v)
}
}
impl From<Handle> for u64 {
fn from(h: Handle) -> Self {
h.0
}
}
impl PartialEq<u64> for Handle {
fn eq(&self, other: &u64) -> bool {
self.0 == *other
}
}
impl PartialEq<Handle> for u64 {
fn eq(&self, other: &Handle) -> bool {
*self == other.0
}
}
impl fmt::Display for Handle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Tag(NonZeroU32);
impl Tag {
#[must_use]
pub const fn new(v: u32) -> Option<Tag> {
match NonZeroU32::new(v) {
Some(nz) => Some(Tag(nz)),
None => None,
}
}
#[must_use]
pub const fn get(self) -> u32 {
self.0.get()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ZeroTagError;
impl fmt::Display for ZeroTagError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "tag must be non-zero (0 is the untagged sentinel)")
}
}
impl std::error::Error for ZeroTagError {}
impl TryFrom<u32> for Tag {
type Error = ZeroTagError;
fn try_from(v: u32) -> Result<Tag, ZeroTagError> {
Tag::new(v).ok_or(ZeroTagError)
}
}
impl From<Tag> for u32 {
fn from(t: Tag) -> Self {
t.0.get()
}
}
impl PartialEq<u32> for Tag {
fn eq(&self, other: &u32) -> bool {
self.get() == *other
}
}
impl PartialEq<Tag> for u32 {
fn eq(&self, other: &Tag) -> bool {
*self == other.get()
}
}
impl fmt::Display for Tag {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.get())
}
}
#[must_use = "TagDropProgress.complete tells you whether the tag drop finished; loop on it or bind with `let _`"]
#[non_exhaustive]
#[derive(Debug, Clone)]
pub struct TagDropProgress {
pub deleted: Vec<Handle>,
pub complete: bool,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn handle_round_trips_u64() {
let h = Handle::from(42);
assert_eq!(h.get(), 42);
assert_eq!(u64::from(h), 42);
assert_eq!(h, 42u64); assert_eq!(42u64, h); }
#[test]
fn handle_displays_as_raw() {
assert_eq!(format!("{}", Handle::from(7)), "7");
}
#[test]
fn tag_new_rejects_zero() {
assert_eq!(Tag::new(0), None);
assert_eq!(Tag::new(5).unwrap().get(), 5);
}
#[test]
fn tag_try_from_zero_errors() {
assert_eq!(Tag::try_from(0), Err(ZeroTagError));
assert_eq!(Tag::try_from(9).unwrap().get(), 9);
}
#[test]
fn tag_eq_and_display() {
let t = Tag::new(3).unwrap();
assert_eq!(t, 3u32);
assert_eq!(3u32, t);
assert_eq!(format!("{t}"), "3");
assert_eq!(u32::from(t), 3);
}
}