intuicio_framework_text/
lib.rs

1use intuicio_derive::IntuicioStruct;
2use lazy_static::lazy_static;
3use std::{
4    ops::Deref,
5    str::FromStr,
6    sync::{Arc, RwLock},
7};
8use string_interner::{
9    StringInterner,
10    backend::{Backend, BufferBackend},
11};
12
13lazy_static! {
14    static ref INTERNER: RwLock<StringInterner<BufferBackend>> =
15        RwLock::new(StringInterner::<BufferBackend>::new());
16}
17
18#[derive(IntuicioStruct, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
19pub struct Name {
20    #[intuicio(ignore)]
21    symbol: <BufferBackend as Backend>::Symbol,
22}
23
24impl Default for Name {
25    fn default() -> Self {
26        Self::new_static("")
27    }
28}
29
30impl Name {
31    pub fn new(value: impl AsRef<str>) -> Self {
32        Self {
33            symbol: INTERNER.write().unwrap().get_or_intern(value),
34        }
35    }
36
37    pub fn new_static(value: &'static str) -> Self {
38        Self {
39            symbol: INTERNER.write().unwrap().get_or_intern_static(value),
40        }
41    }
42
43    pub fn symbol(this: &Self) -> <BufferBackend as Backend>::Symbol {
44        this.symbol
45    }
46
47    pub fn read(this: &Self) -> &str {
48        INTERNER
49            .read()
50            .unwrap()
51            .resolve(this.symbol)
52            .map(|content| unsafe { std::mem::transmute(content) })
53            .unwrap_or_else(|| panic!("Could not resolve Name with symbol: {:?}", this.symbol))
54    }
55}
56
57impl Deref for Name {
58    type Target = str;
59
60    fn deref(&self) -> &Self::Target {
61        Self::read(self)
62    }
63}
64
65impl AsRef<str> for Name {
66    fn as_ref(&self) -> &str {
67        Self::read(self)
68    }
69}
70
71impl FromStr for Name {
72    type Err = ();
73
74    fn from_str(s: &str) -> Result<Self, Self::Err> {
75        Ok(Self::new(s))
76    }
77}
78
79impl From<&str> for Name {
80    fn from(value: &str) -> Self {
81        Self::new(value)
82    }
83}
84
85impl std::fmt::Debug for Name {
86    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87        write!(f, "{:?}", Self::read(self))
88    }
89}
90
91impl std::fmt::Display for Name {
92    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93        write!(f, "{}", Self::read(self))
94    }
95}
96
97#[derive(IntuicioStruct, Clone, PartialEq, PartialOrd, Hash)]
98pub struct Text {
99    #[intuicio(ignore)]
100    content: Arc<str>,
101}
102
103impl Default for Text {
104    fn default() -> Self {
105        Self {
106            content: Arc::from(""),
107        }
108    }
109}
110
111impl Text {
112    pub fn new(value: impl AsRef<str>) -> Self {
113        Self {
114            content: Arc::from(value.as_ref()),
115        }
116    }
117
118    pub fn into_inner(self) -> Arc<str> {
119        self.content
120    }
121
122    pub fn read(this: &Self) -> &str {
123        &this.content
124    }
125
126    pub fn ptr_eq(a: &Self, b: &Self) -> bool {
127        Arc::ptr_eq(&a.content, &b.content)
128    }
129}
130
131impl Deref for Text {
132    type Target = str;
133
134    fn deref(&self) -> &Self::Target {
135        Self::read(self)
136    }
137}
138
139impl AsRef<str> for Text {
140    fn as_ref(&self) -> &str {
141        Self::read(self)
142    }
143}
144
145impl FromStr for Text {
146    type Err = ();
147
148    fn from_str(s: &str) -> Result<Self, Self::Err> {
149        Ok(Self::new(s))
150    }
151}
152
153impl From<&str> for Text {
154    fn from(value: &str) -> Self {
155        Self::new(value)
156    }
157}
158
159impl std::fmt::Debug for Text {
160    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
161        write!(f, "{:?}", Self::read(self))
162    }
163}
164
165impl std::fmt::Display for Text {
166    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
167        write!(f, "{}", Self::read(self))
168    }
169}
170
171#[macro_export]
172macro_rules! name {
173    ($content:literal) => {
174        $crate::Name::new_static($content)
175    };
176}
177
178#[macro_export]
179macro_rules! text {
180    ($content:literal) => {
181        $crate::Text::new($content)
182    };
183}
184
185#[cfg(test)]
186mod tests {
187    use super::*;
188    use std::{collections::HashMap, thread::spawn};
189
190    #[test]
191    fn test_name() {
192        let a = name!("foo");
193        let b = name!("foo");
194        let c = a;
195        let d = name!("bar");
196
197        assert_eq!(a.as_ref(), "foo");
198        assert_eq!(b.as_ref(), "foo");
199        assert_eq!(c.as_ref(), "foo");
200        assert_eq!(d.as_ref(), "bar");
201        assert_eq!(a, b);
202        assert_eq!(a, c);
203        assert_eq!(b, c);
204        assert_ne!(a, d);
205        assert_ne!(b, d);
206        assert_ne!(c, d);
207        assert_eq!(Name::symbol(&a), Name::symbol(&b));
208        assert_eq!(Name::symbol(&a), Name::symbol(&c));
209        assert_eq!(Name::symbol(&b), Name::symbol(&c));
210        assert_ne!(Name::symbol(&a), Name::symbol(&d));
211        assert_ne!(Name::symbol(&b), Name::symbol(&d));
212        assert_ne!(Name::symbol(&c), Name::symbol(&d));
213    }
214
215    #[test]
216    fn test_text() {
217        let a = text!("foo");
218        let b = text!("foo");
219        let c = a.clone();
220        let d = text!("bar");
221
222        assert_eq!(a.as_ref(), "foo");
223        assert_eq!(b.as_ref(), "foo");
224        assert_eq!(c.as_ref(), "foo");
225        assert_eq!(d.as_ref(), "bar");
226        assert_eq!(a, b);
227        assert_eq!(a, c);
228        assert_eq!(b, c);
229        assert_ne!(a, d);
230        assert_ne!(b, d);
231        assert_ne!(c, d);
232        assert!(!Text::ptr_eq(&a, &b));
233        assert!(Text::ptr_eq(&a, &c));
234        assert!(!Text::ptr_eq(&b, &c));
235        assert!(!Text::ptr_eq(&a, &d));
236        assert!(!Text::ptr_eq(&b, &d));
237        assert!(!Text::ptr_eq(&c, &d));
238    }
239
240    #[test]
241    fn test_name_text_map() {
242        let mut map = HashMap::new();
243        map.insert(name!("foo"), text!("Foo"));
244        let bar = text!("Bar");
245        map.insert(name!("bar"), bar.clone());
246        map.insert(name!("bar2"), bar);
247
248        assert_eq!(map.len(), 3);
249        assert_eq!(map.get(&name!("foo")).unwrap().as_ref(), "Foo");
250        assert_eq!(map.get(&name!("bar")).unwrap().as_ref(), "Bar");
251        assert_eq!(map.get(&name!("bar2")).unwrap().as_ref(), "Bar");
252    }
253
254    #[test]
255    fn test_multi_threading() {
256        let name = name!("foo");
257        let text = text!("Foo");
258
259        assert_eq!(name.as_ref(), "foo");
260        assert_eq!(text.as_ref(), "Foo");
261
262        let (name, text) = spawn(move || {
263            assert_eq!(name.as_ref(), "foo");
264            assert_eq!(text.as_ref(), "Foo");
265            (name, text)
266        })
267        .join()
268        .unwrap();
269
270        assert_eq!(name.as_ref(), "foo");
271        assert_eq!(text.as_ref(), "Foo");
272    }
273}