1use std::sync::{LazyLock, RwLock};
2
3use string_interner::{DefaultBackend, DefaultSymbol, StringInterner};
4
5static STRING_INTERNER: LazyLock<RwLock<StringInterner<DefaultBackend>>> =
6 LazyLock::new(|| RwLock::new(StringInterner::default()));
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
13pub struct Ident(DefaultSymbol);
14
15impl Ident {
16 pub fn new(s: &str) -> Self {
20 Self(STRING_INTERNER.write().unwrap().get_or_intern(s))
21 }
22
23 pub fn as_str(&self) -> String {
27 STRING_INTERNER.read().unwrap().resolve(self.0).unwrap().to_string()
28 }
29
30 pub fn resolve_with<F, R>(&self, f: F) -> R
35 where
36 F: FnOnce(&str) -> R,
37 {
38 let interner = STRING_INTERNER.read().unwrap();
39 let resolved = interner.resolve(self.0).unwrap();
40 f(resolved)
41 }
42}
43
44impl Default for Ident {
45 fn default() -> Self {
46 Ident::new("")
47 }
48}
49
50impl From<&str> for Ident {
51 fn from(s: &str) -> Self {
52 Self::new(s)
53 }
54}
55
56impl From<String> for Ident {
57 fn from(s: String) -> Self {
58 Self::new(&s)
59 }
60}
61
62impl std::fmt::Display for Ident {
63 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64 self.resolve_with(|s| write!(f, "{}", s))
65 }
66}
67
68#[cfg(feature = "ast-json")]
69impl serde::Serialize for Ident {
70 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
71 where
72 S: serde::Serializer,
73 {
74 self.as_str().serialize(serializer)
75 }
76}
77
78#[cfg(feature = "ast-json")]
79impl<'de> serde::Deserialize<'de> for Ident {
80 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
81 where
82 D: serde::Deserializer<'de>,
83 {
84 let s = String::deserialize(deserializer)?;
85 Ok(Ident::new(&s))
86 }
87}
88
89pub fn all_symbols() -> Vec<String> {
91 STRING_INTERNER
92 .read()
93 .unwrap()
94 .iter()
95 .map(|(_, s)| s.to_string())
96 .collect()
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102
103 #[test]
104 fn test_ident_new_and_as_str() {
105 let ident = Ident::new("hello");
106 assert_eq!(ident.as_str(), "hello");
107 }
108
109 #[test]
110 fn test_ident_from_str_and_string() {
111 let ident1: Ident = "world".into();
112 let ident2: Ident = String::from("world").into();
113 assert_eq!(ident1, ident2);
114 assert_eq!(ident1.as_str(), "world");
115 }
116
117 #[test]
118 fn test_ident_display_trait() {
119 let ident = Ident::new("display_test");
120 let s = format!("{}", ident);
121 assert_eq!(s, "display_test");
122 }
123
124 #[test]
125 fn test_ident_resolve_with() {
126 let ident = Ident::new("resolve");
127 let len = ident.resolve_with(|s| s.len());
128 assert_eq!(len, "resolve".len());
129 }
130
131 #[cfg(feature = "ast-json")]
132 #[test]
133 fn test_ident_serde() {
134 let ident = Ident::new("serde_test");
135 let serialized = serde_json::to_string(&ident).unwrap();
136 assert_eq!(serialized, "\"serde_test\"");
137 let deserialized: Ident = serde_json::from_str(&serialized).unwrap();
138 assert_eq!(deserialized, ident);
139 }
140}