swc_atoms/
lib.rs

1//! See [JsWord] and [Atom]
2
3#![allow(clippy::unreadable_literal)]
4
5#[doc(hidden)]
6/// Not a public API.
7pub extern crate hstr;
8#[doc(hidden)]
9/// Not a public API.
10pub extern crate once_cell;
11
12use std::{
13    borrow::Cow,
14    cell::UnsafeCell,
15    fmt::{self, Display, Formatter},
16    hash::Hash,
17    ops::Deref,
18    rc::Rc,
19};
20
21use once_cell::sync::Lazy;
22use serde::Serializer;
23
24pub use self::{atom as js_word, Atom as JsWord};
25
26pub mod fast;
27
28/// Clone-on-write string.
29///
30///
31/// See [tendril] for more details.
32#[derive(Clone, Default, PartialEq, Eq, Hash)]
33#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
34#[cfg_attr(feature = "rkyv-impl", repr(C))]
35pub struct Atom(hstr::Atom);
36
37#[cfg(feature = "arbitrary")]
38#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
39impl<'a> arbitrary::Arbitrary<'a> for Atom {
40    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
41        let sym = u.arbitrary::<String>()?;
42        if sym.is_empty() {
43            return Err(arbitrary::Error::NotEnoughData);
44        }
45        Ok(Self(hstr::Atom::from(sym)))
46    }
47}
48
49fn _asserts() {
50    // let _static_assert_size_eq = std::mem::transmute::<Atom, [usize; 1]>;
51
52    fn _assert_send<T: Send>() {}
53    fn _assert_sync<T: Sync>() {}
54
55    _assert_send::<Atom>();
56    _assert_sync::<Atom>();
57}
58
59impl Atom {
60    /// Creates a new [Atom] from a string.
61    #[inline(always)]
62    pub fn new<S>(s: S) -> Self
63    where
64        hstr::Atom: From<S>,
65    {
66        Atom(hstr::Atom::from(s))
67    }
68
69    #[inline]
70    pub fn to_ascii_lowercase(&self) -> Self {
71        Self(self.0.to_ascii_lowercase())
72    }
73
74    #[inline]
75    pub fn as_str(&self) -> &str {
76        &self.0
77    }
78}
79
80impl Deref for Atom {
81    type Target = str;
82
83    #[inline]
84    fn deref(&self) -> &Self::Target {
85        &self.0
86    }
87}
88
89macro_rules! impl_eq {
90    ($T:ty) => {
91        impl PartialEq<$T> for Atom {
92            fn eq(&self, other: &$T) -> bool {
93                &**self == &**other
94            }
95        }
96    };
97}
98
99macro_rules! impl_from {
100    ($T:ty) => {
101        impl From<$T> for Atom {
102            fn from(s: $T) -> Self {
103                Atom::new(s)
104            }
105        }
106    };
107}
108
109impl From<hstr::Atom> for Atom {
110    #[inline(always)]
111    fn from(s: hstr::Atom) -> Self {
112        Atom(s)
113    }
114}
115
116impl PartialEq<str> for Atom {
117    fn eq(&self, other: &str) -> bool {
118        &**self == other
119    }
120}
121
122impl_eq!(&'_ str);
123impl_eq!(Box<str>);
124impl_eq!(std::sync::Arc<str>);
125impl_eq!(Rc<str>);
126impl_eq!(Cow<'_, str>);
127impl_eq!(String);
128
129impl_from!(&'_ str);
130impl_from!(Box<str>);
131impl_from!(String);
132impl_from!(Cow<'_, str>);
133
134impl AsRef<str> for Atom {
135    fn as_ref(&self) -> &str {
136        self
137    }
138}
139
140impl fmt::Debug for Atom {
141    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
142        fmt::Debug::fmt(&**self, f)
143    }
144}
145
146impl Display for Atom {
147    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
148        Display::fmt(&**self, f)
149    }
150}
151
152impl PartialOrd for Atom {
153    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
154        Some(self.cmp(other))
155    }
156}
157
158impl Ord for Atom {
159    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
160        self.as_str().cmp(other.as_str())
161    }
162}
163
164impl serde::ser::Serialize for Atom {
165    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
166    where
167        S: Serializer,
168    {
169        serializer.serialize_str(self)
170    }
171}
172
173impl<'de> serde::de::Deserialize<'de> for Atom {
174    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
175    where
176        D: serde::Deserializer<'de>,
177    {
178        String::deserialize(deserializer).map(Self::new)
179    }
180}
181
182/// Creates an [Atom] from a constant.
183#[macro_export]
184macro_rules! atom {
185    ($s:tt) => {{
186        $crate::Atom::from($crate::hstr::atom!($s))
187    }};
188}
189
190/// Creates an [Atom] from a constant.
191#[macro_export]
192macro_rules! lazy_atom {
193    ($s:tt) => {{
194        $crate::Atom::from($crate::hstr::atom!($s))
195    }};
196}
197
198impl PartialEq<Atom> for str {
199    fn eq(&self, other: &Atom) -> bool {
200        *self == **other
201    }
202}
203
204/// NOT A PUBLIC API
205#[cfg(feature = "rkyv-impl")]
206impl rkyv::Archive for Atom {
207    type Archived = rkyv::string::ArchivedString;
208    type Resolver = rkyv::string::StringResolver;
209
210    #[allow(clippy::unit_arg)]
211    fn resolve(&self, resolver: Self::Resolver, out: rkyv::Place<Self::Archived>) {
212        rkyv::string::ArchivedString::resolve_from_str(self, resolver, out)
213    }
214}
215
216/// NOT A PUBLIC API
217#[cfg(feature = "rkyv-impl")]
218impl<S: rancor::Fallible + rkyv::ser::Writer + ?Sized> rkyv::Serialize<S> for Atom
219where
220    <S as rancor::Fallible>::Error: rancor::Source,
221{
222    fn serialize(&self, serializer: &mut S) -> Result<Self::Resolver, S::Error> {
223        String::serialize(&self.to_string(), serializer)
224    }
225}
226
227/// NOT A PUBLIC API
228#[cfg(feature = "rkyv-impl")]
229impl<D> rkyv::Deserialize<Atom, D> for rkyv::string::ArchivedString
230where
231    D: ?Sized + rancor::Fallible,
232{
233    fn deserialize(&self, deserializer: &mut D) -> Result<Atom, <D as rancor::Fallible>::Error> {
234        let s: String = self.deserialize(deserializer)?;
235
236        Ok(Atom::new(s))
237    }
238}
239
240#[doc(hidden)]
241pub type CahcedAtom = Lazy<Atom>;
242
243/// This should be used as a key for hash maps and hash sets.
244///
245/// This will be replaced with [Atom] in the future.
246pub type StaticString = Atom;
247
248#[derive(Default)]
249pub struct AtomStore(hstr::AtomStore);
250
251impl AtomStore {
252    #[inline]
253    pub fn atom<'a>(&mut self, s: impl Into<Cow<'a, str>>) -> Atom {
254        Atom(self.0.atom(s))
255    }
256}
257
258/// A fast internally mutable cell for [AtomStore].
259#[derive(Default)]
260pub struct AtomStoreCell(UnsafeCell<AtomStore>);
261
262impl AtomStoreCell {
263    #[inline]
264    pub fn atom<'a>(&self, s: impl Into<Cow<'a, str>>) -> Atom {
265        // evaluate the into before borrowing (see #8362)
266        let s: Cow<'a, str> = s.into();
267        // SAFETY: We can skip the borrow check of RefCell because
268        // this API enforces a safe contract. It is slightly faster
269        // to use an UnsafeCell. Note the borrow here is short lived
270        // only to this block.
271        unsafe { (*self.0.get()).atom(s) }
272    }
273}
274
275/// noop
276#[cfg(feature = "shrink-to-fit")]
277impl shrink_to_fit::ShrinkToFit for Atom {
278    #[inline(always)]
279    fn shrink_to_fit(&mut self) {}
280}