use std::{
fmt,
sync::{Mutex, OnceLock},
};
use string_interner::{DefaultStringInterner, DefaultSymbol};
static INTERNER: OnceLock<Mutex<DefaultStringInterner>> = OnceLock::new();
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Id(DefaultSymbol);
impl Id {
pub fn new(name: &str) -> Self {
let mut interner = INTERNER
.get_or_init(|| Mutex::new(DefaultStringInterner::new()))
.lock()
.expect("Failed to acquire interner lock");
let symbol = interner.get_or_intern(name);
Self(symbol)
}
pub fn from_anonymous(idx: usize) -> Self {
let name = format!("__{idx}");
Self::new(&name)
}
pub fn create_nested(&self, child_id: Id) -> Self {
let mut interner = INTERNER
.get_or_init(|| Mutex::new(DefaultStringInterner::new()))
.lock()
.expect("Failed to acquire interner lock");
let parent_str = interner
.resolve(self.0)
.expect("Parent ID should exist in interner");
let child_str = interner
.resolve(child_id.0)
.expect("Child ID should exist in interner");
let nested_name = format!("{}::{}", parent_str, child_str);
let symbol = interner.get_or_intern(&nested_name);
Self(symbol)
}
}
impl fmt::Display for Id {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let interner = INTERNER
.get_or_init(|| Mutex::new(DefaultStringInterner::new()))
.lock()
.expect("Failed to acquire interner lock");
let str_value = interner
.resolve(self.0)
.expect("Symbol should exist in interner");
write!(f, "{}", str_value)
}
}
impl std::str::FromStr for Id {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut interner = INTERNER
.get_or_init(|| Mutex::new(DefaultStringInterner::new()))
.lock()
.expect("Failed to acquire interner lock");
let symbol = interner.get_or_intern(s);
Ok(Self(symbol))
}
}
impl From<&str> for Id {
fn from(name: &str) -> Self {
Self::new(name)
}
}
impl PartialEq<str> for Id {
fn eq(&self, other: &str) -> bool {
let interner = INTERNER
.get_or_init(|| Mutex::new(DefaultStringInterner::new()))
.lock()
.expect("Failed to acquire interner lock");
let self_str = interner
.resolve(self.0)
.expect("Symbol should exist in interner");
self_str == other
}
}
impl PartialEq<&str> for Id {
fn eq(&self, other: &&str) -> bool {
self == *other
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new() {
let id1 = Id::new("Rectangle");
let id2 = Id::new("Rectangle");
let id3 = Id::new("Oval");
assert_eq!(id1, id2);
assert_ne!(id1, id3);
assert_eq!(id1, "Rectangle");
}
#[test]
fn test_from_anonymous() {
let id1 = Id::from_anonymous(0);
let id2 = Id::from_anonymous(1);
let id3 = Id::from_anonymous(0);
assert_ne!(id1, id2);
assert_eq!(id1, id3);
}
#[test]
fn test_create_nested() {
let parent = Id::new("system");
let child1 = Id::new("backend");
let child2 = Id::new("frontend");
let nested1 = parent.create_nested(child1);
let nested2 = parent.create_nested(child2);
assert_ne!(nested1, nested2);
assert_eq!(nested1, "system::backend");
assert_eq!(nested2, "system::frontend");
}
#[test]
fn test_deep_nesting() {
let root = Id::new("system");
let frontend = Id::new("frontend");
let app = Id::new("app");
let component = Id::new("component");
let level1 = root.create_nested(frontend);
let level2 = level1.create_nested(app);
let level3 = level2.create_nested(component);
assert_eq!(level3, "system::frontend::app::component");
}
#[test]
fn test_to_string() {
let id = Id::new("test_component");
assert_eq!(id, "test_component");
}
#[test]
fn test_display_trait() {
let id = Id::new("display_test");
assert_eq!(format!("{}", id), "display_test");
}
#[test]
fn test_from_trait() {
let id1: Id = "test_string".into();
let id2 = Id::new("test_string");
assert_eq!(id1, id2);
assert_eq!(id1, "test_string");
}
#[test]
fn test_hash_and_eq() {
use std::collections::HashMap;
let id1 = Id::new("key1");
let id2 = Id::new("key1");
let id3 = Id::new("key2");
let mut map = HashMap::new();
map.insert(id1, "value1");
map.insert(id3, "value2");
assert_eq!(map.get(&id2), Some(&"value1"));
assert_eq!(map.len(), 2);
}
#[test]
fn test_copy_trait() {
let id1 = Id::new("copy_test");
let id2 = id1; let id3 = id1;
assert_eq!(id1, id2);
assert_eq!(id1, id3);
assert_eq!(id2, id3);
assert_eq!(id1, "copy_test");
assert_eq!(id2, "copy_test");
assert_eq!(id3, "copy_test");
}
#[test]
fn test_partial_eq_str() {
let id = Id::new("Rectangle");
assert!(id == "Rectangle");
assert!(id != "Oval");
let nested = Id::new("parent::child");
assert!(nested == "parent::child");
assert!(nested != "parent");
assert!(nested != "child");
let empty = Id::new("");
assert!(empty == "");
assert!(empty != "non-empty");
}
#[test]
fn test_partial_eq_str_ref() {
let id = Id::new("Component");
let name1 = String::from("Component");
let name2 = String::from("Element");
assert!(id == name1.as_str());
assert!(id != name2.as_str());
let slice: &str = "Component";
assert!(id == slice);
let nested = Id::new("frontend::app");
let nested_str = String::from("frontend::app");
assert!(nested == nested_str.as_str());
}
}