llmcc_core/
interner.rs

1use std::sync::RwLock;
2
3use string_interner::backend::DefaultBackend;
4use string_interner::symbol::DefaultSymbol;
5use string_interner::StringInterner;
6
7/// Interned string symbol backed by a `StringInterner`.
8pub type InternedStr = DefaultSymbol;
9
10/// Shared string interner used across the llmcc core.
11#[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    /// Intern the provided string slice and return its symbol.
26    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    /// Resolve an interned symbol back into an owned string.
34    ///
35    /// Clones the underlying string from the interner to avoid lifetime issues.
36    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    /// Resolve an interned symbol and apply a closure while the borrow is active.
45    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}