use std::sync::{Arc, OnceLock};
use lasso::{Spur, ThreadedRodeo};
static GLOBAL_RODEO: OnceLock<Arc<ThreadedRodeo>> = OnceLock::new();
pub(crate) fn global_rodeo() -> &'static Arc<ThreadedRodeo> {
GLOBAL_RODEO
.get()
.expect("Global interner not initialized - add AttributesPlugin first")
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct AttributeId(pub(crate) Spur);
#[derive(Clone)]
pub struct Interner {
rodeo: Arc<ThreadedRodeo>,
}
impl Interner {
pub fn new() -> Self {
Self {
rodeo: Arc::new(ThreadedRodeo::default()),
}
}
pub fn global() -> Self {
Self {
rodeo: Arc::clone(global_rodeo()),
}
}
pub fn set_global(&self) {
let _ = GLOBAL_RODEO.set(Arc::clone(&self.rodeo));
}
pub fn get_or_intern(&self, s: &str) -> AttributeId {
AttributeId(self.rodeo.get_or_intern(s))
}
pub fn get(&self, s: &str) -> Option<AttributeId> {
self.rodeo.get(s).map(AttributeId)
}
pub fn resolve(&self, id: AttributeId) -> &str {
self.rodeo.resolve(&id.0)
}
}
impl Default for Interner {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn intern_and_resolve() {
let interner = Interner::new();
let id = interner.get_or_intern("Strength");
assert_eq!(interner.resolve(id), "Strength");
}
#[test]
fn same_string_same_id() {
let interner = Interner::new();
let a = interner.get_or_intern("Damage.current");
let b = interner.get_or_intern("Damage.current");
assert_eq!(a, b);
}
#[test]
fn different_strings_different_ids() {
let interner = Interner::new();
let a = interner.get_or_intern("Strength");
let b = interner.get_or_intern("Dexterity");
assert_ne!(a, b);
}
#[test]
fn get_returns_none_for_unknown() {
let interner = Interner::new();
assert!(interner.get("NotInterned").is_none());
}
#[test]
fn get_returns_some_for_known() {
let interner = Interner::new();
let id = interner.get_or_intern("Health");
assert_eq!(interner.get("Health"), Some(id));
}
}