llmcc_core/
interner.rs

1use std::cell::RefCell;
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: RefCell<StringInterner<DefaultBackend>>,
14}
15
16impl Default for InternPool {
17    fn default() -> Self {
18        Self {
19            inner: RefCell::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.borrow_mut().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.borrow().resolve(symbol).map(|s| s.to_owned())
38    }
39
40    /// Resolve an interned symbol and apply a closure while the borrow is active.
41    pub fn with_resolved<R, F>(&self, symbol: InternedStr, f: F) -> Option<R>
42    where
43        F: FnOnce(&str) -> R,
44    {
45        self.inner.borrow().resolve(symbol).map(f)
46    }
47}
48
49#[cfg(test)]
50mod tests {
51    use super::*;
52
53    #[test]
54    fn interning_returns_stable_symbol() {
55        let pool = InternPool::default();
56        let first = pool.intern("foo");
57        let second = pool.intern("foo");
58        assert_eq!(
59            first, second,
60            "Interned symbols should be stable for the same string"
61        );
62    }
63
64    #[test]
65    fn resolve_owned_recovers_string() {
66        let pool = InternPool::default();
67        let sym = pool.intern("bar");
68        let resolved = pool
69            .resolve_owned(sym)
70            .expect("symbol should resolve to a string");
71        assert_eq!(resolved, "bar");
72    }
73
74    #[test]
75    fn with_resolved_provides_borrowed_str() {
76        let pool = InternPool::default();
77        let sym = pool.intern("baz");
78        let length = pool
79            .with_resolved(sym, |s| s.len())
80            .expect("symbol should resolve to a closure result");
81        assert_eq!(length, 3);
82    }
83}