use std::sync::{Mutex, MutexGuard, OnceLock};
use string_interner::{DefaultSymbol, StringInterner, backend::BucketBackend};
type Inner = StringInterner<BucketBackend>;
static INTERNER: OnceLock<Mutex<Interner>> = OnceLock::new();
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Symbol(DefaultSymbol);
pub struct Interner(Inner);
impl Interner {
pub fn get_or_intern(&mut self, s: &str) -> Symbol {
Symbol(self.0.get_or_intern(s))
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn resolve(&self, symbol: Symbol) -> &'static str {
let s = self
.0
.resolve(symbol.0)
.expect("symbol should exist in interner");
unsafe { &*(s as *const str) }
}
}
pub fn interner() -> MutexGuard<'static, Interner> {
INTERNER
.get_or_init(|| Mutex::new(Interner(Inner::new())))
.lock()
.expect("interner lock poisoned")
}
pub fn get_or_intern(s: &str) -> Symbol {
interner().get_or_intern(s)
}
pub fn resolve(symbol: Symbol) -> &'static str {
interner().resolve(symbol)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn round_trip_returns_original_string() {
let sym = get_or_intern("interner_test_round_trip");
assert_eq!(resolve(sym), "interner_test_round_trip");
}
#[test]
fn deduplication_returns_same_symbol() {
let first = get_or_intern("interner_test_dedup");
let second = get_or_intern("interner_test_dedup");
assert_eq!(first, second);
}
#[test]
fn distinct_strings_yield_distinct_symbols() {
let a = get_or_intern("interner_test_distinct_a");
let b = get_or_intern("interner_test_distinct_b");
assert_ne!(a, b);
}
#[test]
fn static_str_survives_after_lock_dropped() {
let sym = get_or_intern("interner_test_static_lifetime");
let s: &'static str = {
let guard = interner();
guard.resolve(sym)
};
assert_eq!(s, "interner_test_static_lifetime");
}
#[test]
fn len_increases_after_interning_new_string() {
let mut guard = interner();
let before = guard.len();
guard.get_or_intern("interner_test_len_unique_string");
assert_eq!(guard.len(), before + 1);
}
#[test]
fn len_stable_after_interning_duplicate() {
let mut guard = interner();
guard.get_or_intern("interner_test_len_dup");
let after_first = guard.len();
guard.get_or_intern("interner_test_len_dup");
assert_eq!(guard.len(), after_first);
}
}