symbol_table/
global.rs

1use crate::*;
2
3use std::str::FromStr;
4
5#[cfg(feature = "global")]
6/// Macro for creating symbols from &'static str. Useful for commonly used symbols known at compile time.
7/// This is faster then GlobalSymbol::from(s) by avoiding mutex contention.
8///
9/// # Examples
10///
11/// ```
12/// use symbol_table::static_symbol;
13/// use symbol_table::GlobalSymbol;
14///
15/// let hello = static_symbol!("hello");
16/// assert_eq!(hello, GlobalSymbol::from("hello"));
17///
18/// // The same symbol is returned on subsequent calls
19/// let hello2 = static_symbol!("hello");
20/// assert_eq!(hello, hello2);
21/// ```
22#[macro_export]
23macro_rules! static_symbol {
24    ($s:literal) => {{
25        use std::sync::OnceLock;
26        static SYMBOL: OnceLock<$crate::GlobalSymbol> = OnceLock::new();
27
28        *SYMBOL.get_or_init(|| $crate::GlobalSymbol::from($s))
29    }};
30}
31
32/// A interned string in the global symbol table.
33///
34/// This requires the `global` feature on the crate.
35///
36/// [`GlobalSymbol`] is a wrapper around [`Symbol`] that knows to refer to a
37/// built-in, global [`SymbolTable`]. Strings into the global table are never freed.
38///
39/// This enables a lot of convenience methods and trait implementations over
40/// [`GlobalSymbol`] (see below). In particular,
41///   you can convert it to `&'static str`,
42///   convert [`From`] and [`Into`] a `&str`,
43///   and de/serialize using [`serde`](https://serde.rs) if the `serde` feature is enabled.
44#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
45#[cfg_attr(feature = "serde", derive(serde::Serialize))]
46#[cfg_attr(feature = "serde", serde(into = "&'static str"))]
47pub struct GlobalSymbol(Symbol);
48
49impl From<NonZeroU32> for GlobalSymbol {
50    fn from(n: NonZeroU32) -> Self {
51        Self(Symbol::from(n))
52    }
53}
54
55impl From<GlobalSymbol> for NonZeroU32 {
56    fn from(n: GlobalSymbol) -> Self {
57        n.0.into()
58    }
59}
60
61static SINGLETON: SymbolTable = SymbolTable::new();
62
63impl GlobalSymbol {
64    /// Intern a string into the global symbol table.
65    pub fn new(s: impl AsRef<str>) -> Self {
66        s.as_ref().into()
67    }
68
69    /// Convert this symbol into the string in the static, global symbol table.
70    pub fn as_str(&self) -> &'static str {
71        (*self).into()
72    }
73}
74
75impl From<&str> for GlobalSymbol {
76    fn from(s: &str) -> Self {
77        GlobalSymbol(SINGLETON.intern(s))
78    }
79}
80
81impl From<String> for GlobalSymbol {
82    fn from(s: String) -> Self {
83        s.as_str().into()
84    }
85}
86
87impl From<&String> for GlobalSymbol {
88    fn from(s: &String) -> Self {
89        s.as_str().into()
90    }
91}
92
93impl FromStr for GlobalSymbol {
94    type Err = std::convert::Infallible;
95
96    fn from_str(s: &str) -> Result<Self, Self::Err> {
97        Ok(s.into())
98    }
99}
100
101impl From<GlobalSymbol> for &'static str {
102    fn from(sym: GlobalSymbol) -> Self {
103        SINGLETON.resolve(sym.0)
104    }
105}
106
107impl std::fmt::Debug for GlobalSymbol {
108    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109        std::fmt::Debug::fmt(self.as_str(), f)
110    }
111}
112
113impl std::fmt::Display for GlobalSymbol {
114    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
115        std::fmt::Display::fmt(self.as_str(), f)
116    }
117}
118
119#[cfg(feature = "serde")]
120struct StrVisitor;
121
122#[cfg(feature = "serde")]
123impl serde::de::Visitor<'_> for StrVisitor {
124    type Value = GlobalSymbol;
125
126    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
127        formatter.write_str("a &str")
128    }
129
130    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
131    where
132        E: serde::de::Error,
133    {
134        Ok(v.into())
135    }
136}
137
138#[cfg(feature = "serde")]
139impl<'de> serde::Deserialize<'de> for GlobalSymbol {
140    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
141    where
142        D: serde::Deserializer<'de>,
143    {
144        deserializer.deserialize_str(StrVisitor)
145    }
146}