use ouroboros::self_referencing;
use std::cmp::Ordering;
use std::ffi::{OsStr, OsString};
use std::fmt::{Debug, Display, Formatter};
use std::hash::{Hash};
use std::path::{Path, PathBuf};
use std::sync::LazyLock;
#[self_referencing]
struct IdMap {
data: papaya::HashMap<String, u32, ahash::RandomState>,
#[borrows(data)]
#[covariant]
pin: papaya::HashMapRef<'this, String, u32, ahash::RandomState, papaya::OwnedGuard<'this>>,
}
static IDS: boxcar::Vec<String> = boxcar::Vec::new();
static ID_MAP: LazyLock<IdMap> = LazyLock::new(|| {
let data = papaya::HashMap::builder()
.hasher(ahash::RandomState::new())
.build();
IdMapBuilder {
data,
pin_builder: |data| data.pin_owned(),
}.build()
});
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct ResourceId(u32);
impl ResourceId {
#[inline]
pub fn new(id: impl AsRef<str>) -> Self {
let id = id.as_ref();
let idx = ID_MAP.borrow_pin()
.get_or_insert_with(id.to_owned(), || {
IDS.push(id.to_owned())
.try_into()
.expect("Exceeded u32::MAX ResourceIds")
});
Self(*idx)
}
#[inline]
pub fn from_os_str(id: impl AsRef<OsStr>) -> Self {
Self::new(id.as_ref().to_string_lossy())
}
#[inline]
pub fn as_str(&self) -> &str {
&IDS[self.0 as usize]
}
}
impl Ord for ResourceId {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
self.as_str().cmp(other.as_str())
}
}
impl PartialOrd for ResourceId {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.as_str().partial_cmp(other.as_str())
}
}
impl AsRef<str> for ResourceId {
#[inline]
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl Debug for ResourceId {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ResourceId")
.field("idx", &self.0)
.field("str", &self.as_str())
.finish()
}
}
impl Display for ResourceId {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Display::fmt(self.as_str(), f)
}
}
impl From<&ResourceId> for ResourceId {
#[inline]
fn from(value: &ResourceId) -> Self { value.clone() }
}
macro_rules! impl_from_str {
($str_type:ty) => {
impl From<$str_type> for ResourceId {
#[inline]
fn from(value: $str_type) -> Self {
Self::new(value)
}
}
};
}
impl_from_str!(String);
impl_from_str!(&String);
impl_from_str!(&str);
macro_rules! impl_from_os_str {
($str_type:ty) => {
impl From<$str_type> for ResourceId {
#[inline]
fn from(value: $str_type) -> Self {
Self::from_os_str(value)
}
}
};
}
impl_from_os_str!(PathBuf);
impl_from_os_str!(&PathBuf);
impl_from_os_str!(&Path);
impl_from_os_str!(OsString);
impl_from_os_str!(&OsString);
impl_from_os_str!(&OsStr);