use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use once_cell::sync::Lazy;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct InternedString {
inner: Arc<str>,
}
static INTERNER: Lazy<Mutex<HashMap<Arc<str>, ()>>> = Lazy::new(|| Mutex::new(HashMap::new()));
impl InternedString {
pub fn new() -> Self {
Self::intern("")
}
pub fn from<S: AsRef<str>>(s: S) -> Self {
Self::intern(s.as_ref())
}
pub fn as_str(&self) -> &str {
&self.inner
}
pub fn len(&self) -> usize {
self.inner.len()
}
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
fn intern(s: &str) -> Self {
let mut interner = INTERNER.lock().unwrap();
if let Some(existing) = interner.keys().find(|k| k.as_ref() == s) {
return InternedString {
inner: Arc::clone(existing),
};
}
let arc_str = Arc::from(s);
interner.insert(Arc::clone(&arc_str), ());
InternedString { inner: arc_str }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty_string() {
let empty1 = InternedString::new();
let empty2 = InternedString::new();
assert_eq!(empty1, empty2);
assert!(empty1.is_empty());
}
#[test]
fn test_interning() {
let s1 = InternedString::from("hello");
let s2 = InternedString::from("hello");
let s3 = InternedString::from("world");
assert_eq!(s1, s2);
assert_ne!(s1, s3);
}
#[test]
fn test_string_length() {
let s = InternedString::from("test");
assert_eq!(s.len(), 4);
}
#[test]
fn test_string_content() {
let s = InternedString::from("RustLang");
assert_eq!(s.as_str(), "RustLang");
}
}