1use std::{
52 borrow::Cow,
53 fmt::{self, Display},
54 sync::{LazyLock, Mutex},
55};
56
57use ahash::AHashMap;
58use append_only_vec::AppendOnlyVec;
59use paste::paste;
60
61#[macro_export]
62macro_rules! impl_symbol {
63 ( $( $t:tt ),* ) => {
64 $(
65 paste! {
66 #[derive(Debug, Default)]
67 struct [<SymbolRegister $t>] {
68 names: AppendOnlyVec<&'static str>,
69 indices: Mutex<AHashMap<&'static str, $t>>,
70 }
71
72 impl [<SymbolRegister $t>] {
73 fn new() -> Self {
74 Self {
75 names: AppendOnlyVec::new(),
76 indices: Mutex::new(AHashMap::new()),
77 }
78 }
79
80 fn name(&self, idx: usize) -> &'static str {
81 &self.names[idx]
82 }
83
84 fn idx<S>(&self, name: S) -> $t
85 where
86 S: AsRef<str> + Leak<&'static str>,
87 {
88 let mut indices = self.indices.lock().unwrap();
89 if let Some(idx) = indices.get(name.as_ref()) {
90 return *idx;
91 }
92 let name = name.leak();
93 let new_idx = self.names.len() as _;
94 indices.insert(name, new_idx);
95 self.names.push(name);
99 new_idx
100 }
101 }
102
103 static [<SYMBOL_REGISTER_ $t:upper>]: LazyLock<[<SymbolRegister $t>]> =
104 LazyLock::new(|| [<SymbolRegister $t>]::new());
105
106 #[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
108 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
109 #[cfg_attr(feature = "serde", serde(transparent))]
110 pub struct [<Symbol $t>] {
111 #[cfg_attr(
112 feature = "serde",
113 serde(
114 serialize_with = "serialize_sym_" $t,
115 deserialize_with = "deserialize_sym_" $t
116 )
117 )]
118 idx: $t,
119 }
120
121 impl [<Symbol $t>] {
122 pub fn new<S>(name: S) -> Self
124 where S: AsRef<str> + Leak<&'static str>,
125 {
126 let idx = [<SYMBOL_REGISTER_ $t:upper>].idx(name);
127 Self { idx }
128 }
129
130 pub fn new_from_box(name: Box<str>) -> Self {
132 let inner: &'static str = Box::leak(name);
133 let idx = [<SYMBOL_REGISTER_ $t:upper>].idx(inner);
134 Self { idx }
135 }
136
137 pub fn new_from_str<'a>(name: &'a str) -> Self {
144 Self::new(StrWrapper(name))
145 }
146
147 pub fn name(&self) -> &'static str {
149 [<SYMBOL_REGISTER_ $t:upper>].name(self.idx as _)
150 }
151 }
152
153 impl Display for [<Symbol $t>] {
154 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
155 write!(f, "{}", [<SYMBOL_REGISTER_ $t:upper>].name(self.idx as _))
156 }
157 }
158
159 #[cfg(feature = "serde")]
160 fn [<serialize_sym_ $t>]<S: serde::Serializer>(
161 sym: &$t,
162 s: S,
163 ) -> Result<S::Ok, S::Error> {
164 use serde::Serialize;
165 let sym = [<Symbol $t>] { idx: *sym };
166 let name = sym.name();
167 str::serialize(name, s)
168 }
169
170 #[cfg(feature = "serde")]
171 fn [<deserialize_sym_ $t>]<'de, D: serde::Deserializer<'de>>(
172 d: D,
173 ) -> Result<$t, D::Error> {
174 use serde::Deserialize;
175 let name = String::deserialize(d)?;
176 let s = [<Symbol $t>]::new(name);
177 Ok(s.idx)
178 }
179 }
180 )*
181 };
182}
183
184#[cfg(feature = "u8")]
185impl_symbol!(u8);
186
187#[cfg(feature = "u16")]
188impl_symbol!(u16);
189
190#[cfg(feature = "u32")]
191impl_symbol!(u32);
192
193#[cfg(feature = "u64")]
194impl_symbol!(u64);
195
196#[cfg(feature = "u128")]
197impl_symbol!(u128);
198
199#[cfg(feature = "usize")]
200impl_symbol!(usize);
201
202#[cfg(feature = "u32")]
203pub type Symbol = Symbolu32;
204
205pub trait Leak<T> {
207 fn leak(self) -> T;
208}
209
210impl Leak<&'static str> for &'static str {
211 fn leak(self) -> &'static str {
212 self
213 }
214}
215
216impl Leak<&'static str> for String {
217 fn leak(self) -> &'static str {
218 String::leak(self)
219 }
220}
221
222impl Leak<&'static str> for Cow<'static, str> {
223 fn leak(self) -> &'static str {
224 match self {
225 Cow::Borrowed(s) => s,
226 Cow::Owned(s) => s.leak(),
227 }
228 }
229}
230
231#[cfg(feature = "u32")]
233#[macro_export]
234macro_rules! symbols {
235 ( $( $x:ident ),* ) => {
236 $(
237 let $x = $crate::Symbol::new(stringify!($x));
238 )*
239 };
240}
241
242#[cfg(feature = "u8")]
246#[macro_export]
247macro_rules! symbols_u8 {
248 ( $( $x:ident ),* ) => {
249 $(
250 let $x = $crate::Symbolu8::new(stringify!($x));
251 )*
252 };
253}
254
255#[cfg(feature = "u16")]
257#[macro_export]
258macro_rules! symbols_u16 {
259 ( $( $x:ident ),* ) => {
260 $(
261 let $x = $crate::Symbolu16::new(stringify!($x));
262 )*
263 };
264}
265
266#[cfg(feature = "u32")]
268#[macro_export]
269macro_rules! symbols_u32 {
270 ( $( $x:ident ),* ) => {
271 $(
272 let $x = $crate::Symbolu32::new(stringify!($x));
273 )*
274 };
275}
276
277#[cfg(feature = "u64")]
279#[macro_export]
280macro_rules! symbols_u64 {
281 ( $( $x:ident ),* ) => {
282 $(
283 let $x = $crate::Symbolu64::new(stringify!($x));
284 )*
285 };
286}
287
288#[cfg(feature = "u128")]
289#[macro_export]
291macro_rules! symbols_u128 {
292 ( $( $x:ident ),* ) => {
293 $(
294 let $x = $crate::Symbolu128::new(stringify!($x));
295 )*
296 };
297}
298
299#[cfg(feature = "usize")]
301#[macro_export]
302macro_rules! symbols_usize {
303 ( $( $x:ident ),* ) => {
304 $(
305 let $x = $crate::Symbolusize::new(stringify!($x));
306 )*
307 };
308}
309
310struct StrWrapper<'a>(&'a str);
311
312impl<'a> Leak<&'static str> for StrWrapper<'a> {
313 fn leak(self) -> &'static str {
314 self.0.to_owned().leak()
315 }
316}
317
318impl<'a> AsRef<str> for StrWrapper<'a> {
319 fn as_ref(&self) -> &str {
320 &self.0
321 }
322}
323
324#[cfg(test)]
325mod tests {
326 use super::*;
327
328 #[test]
329 fn symbol() {
330 symbols!(x, y, z);
331 assert_eq!(x.name(), "x");
332 assert_eq!(y.name(), "y");
333 assert_eq!(z.name(), "z");
334 let xx = Symbol::new("x");
335 assert_eq!(xx.name(), "x");
336 }
337
338 #[test]
339 fn owned() {
340 symbols!(x);
341 let xx = Symbol::new("x".to_owned());
342 assert_eq!(xx, x);
343 }
344
345 #[test]
346 fn owned_box() {
347 symbols!(x);
348 let xx = Symbol::new_from_box("x".to_owned().into_boxed_str());
349 assert_eq!(xx, x);
350 }
351
352 #[test]
353 fn cow() {
354 let x = Symbol::new(Cow::Owned("x".to_owned()));
355 assert_eq!(x.name(), "x");
356 let xx = Symbol::new(Cow::Borrowed("x"));
357 assert_eq!(xx, x);
358 }
359
360 #[cfg(feature = "u8")]
361 #[test]
362 fn symbol_u8() {
363 symbols_u8!(x, y, z);
364 assert_eq!(x.name(), "x");
365 assert_eq!(y.name(), "y");
366 assert_eq!(z.name(), "z");
367 let xx = Symbolu8::new("x");
368 assert_eq!(xx.name(), "x");
369 }
370
371 #[test]
372 fn symbol_str() {
373 symbols!(x);
374 let xx = {
375 let x = "x".to_owned();
376 Symbol::new_from_str(&x)
377 };
378 assert_eq!(x, xx);
379 }
380}