1use std::sync::RwLock;
2
3use string_interner::backend::DefaultBackend;
4use string_interner::symbol::DefaultSymbol;
5use string_interner::StringInterner;
6
7pub type InternedStr = DefaultSymbol;
9
10#[derive(Debug)]
12pub struct InternPool {
13 inner: RwLock<StringInterner<DefaultBackend>>,
14}
15
16impl Default for InternPool {
17 fn default() -> Self {
18 Self {
19 inner: RwLock::new(StringInterner::new()),
20 }
21 }
22}
23
24impl InternPool {
25 pub fn intern<S>(&self, value: S) -> InternedStr
27 where
28 S: AsRef<str>,
29 {
30 self.inner.write().unwrap().get_or_intern(value.as_ref())
31 }
32
33 pub fn resolve_owned(&self, symbol: InternedStr) -> Option<String> {
37 self.inner
38 .read()
39 .unwrap()
40 .resolve(symbol)
41 .map(|s| s.to_owned())
42 }
43
44 pub fn with_resolved<R, F>(&self, symbol: InternedStr, f: F) -> Option<R>
46 where
47 F: FnOnce(&str) -> R,
48 {
49 self.inner.read().unwrap().resolve(symbol).map(f)
50 }
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56
57 #[test]
58 fn interning_returns_stable_symbol() {
59 let pool = InternPool::default();
60 let first = pool.intern("foo");
61 let second = pool.intern("foo");
62 assert_eq!(
63 first, second,
64 "Interned symbols should be stable for the same string"
65 );
66 }
67
68 #[test]
69 fn resolve_owned_recovers_string() {
70 let pool = InternPool::default();
71 let sym = pool.intern("bar");
72 let resolved = pool
73 .resolve_owned(sym)
74 .expect("symbol should resolve to a string");
75 assert_eq!(resolved, "bar");
76 }
77
78 #[test]
79 fn with_resolved_provides_borrowed_str() {
80 let pool = InternPool::default();
81 let sym = pool.intern("baz");
82 let length = pool
83 .with_resolved(sym, |s| s.len())
84 .expect("symbol should resolve to a closure result");
85 assert_eq!(length, 3);
86 }
87}