1#![allow(clippy::unreadable_literal)]
4
5#[doc(hidden)]
6pub extern crate hstr;
8#[doc(hidden)]
9pub 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#[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 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 #[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#[macro_export]
184macro_rules! atom {
185 ($s:tt) => {{
186 $crate::Atom::from($crate::hstr::atom!($s))
187 }};
188}
189
190#[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#[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#[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#[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
243pub 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#[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 let s: Cow<'a, str> = s.into();
267 unsafe { (*self.0.get()).atom(s) }
272 }
273}
274
275#[cfg(feature = "shrink-to-fit")]
277impl shrink_to_fit::ShrinkToFit for Atom {
278 #[inline(always)]
279 fn shrink_to_fit(&mut self) {}
280}