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::{Borrow, Cow},
14 cell::UnsafeCell,
15 fmt::{self, Display, Formatter},
16 hash::Hash,
17 mem::transmute,
18 ops::Deref,
19 rc::Rc,
20};
21
22pub use hstr::wtf8;
23use once_cell::sync::Lazy;
24use serde::Serializer;
25use wtf8::Wtf8;
26
27pub use crate::{fast::UnsafeAtom, wtf8_atom::Wtf8Atom};
28
29mod fast;
30mod wtf8_atom;
31
32#[derive(Clone, Default, PartialEq, Eq, Hash)]
37#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
38#[repr(transparent)]
39pub struct Atom(hstr::Atom);
40
41#[cfg(feature = "encoding-impl")]
42impl cbor4ii::core::enc::Encode for Atom {
43 #[inline]
44 fn encode<W: cbor4ii::core::enc::Write>(
45 &self,
46 writer: &mut W,
47 ) -> Result<(), cbor4ii::core::enc::Error<W::Error>> {
48 self.as_str().encode(writer)
49 }
50}
51
52#[cfg(feature = "encoding-impl")]
53impl<'de> cbor4ii::core::dec::Decode<'de> for Atom {
54 #[inline]
55 fn decode<R: cbor4ii::core::dec::Read<'de>>(
56 reader: &mut R,
57 ) -> Result<Self, cbor4ii::core::dec::Error<R::Error>> {
58 let s = <&str>::decode(reader)?;
59 Ok(Atom::new(s))
60 }
61}
62
63#[cfg(feature = "arbitrary")]
64#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
65impl<'a> arbitrary::Arbitrary<'a> for Atom {
66 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
67 let sym = u.arbitrary::<String>()?;
68 if sym.is_empty() {
69 return Err(arbitrary::Error::NotEnoughData);
70 }
71 Ok(Self(hstr::Atom::from(sym)))
72 }
73}
74
75fn _asserts() {
76 fn _assert_send<T: Send>() {}
79 fn _assert_sync<T: Sync>() {}
80
81 _assert_send::<Atom>();
82 _assert_sync::<Atom>();
83}
84
85impl Atom {
86 #[inline(always)]
88 pub fn new<S>(s: S) -> Self
89 where
90 hstr::Atom: From<S>,
91 {
92 Atom(hstr::Atom::from(s))
93 }
94
95 #[inline]
96 pub fn to_ascii_lowercase(&self) -> Self {
97 Self(self.0.to_ascii_lowercase())
98 }
99
100 #[inline]
101 pub fn as_str(&self) -> &str {
102 &self.0
103 }
104
105 pub unsafe fn from_wtf8_unchecked(s: Wtf8Atom) -> Self {
119 Atom(unsafe { hstr::Atom::from_wtf8_unchecked(s.0) })
120 }
121}
122
123impl Deref for Atom {
124 type Target = str;
125
126 #[inline]
127 fn deref(&self) -> &Self::Target {
128 &self.0
129 }
130}
131
132macro_rules! impl_eq {
133 ($T:ty) => {
134 impl PartialEq<$T> for Atom {
135 fn eq(&self, other: &$T) -> bool {
136 &**self == &**other
137 }
138 }
139 };
140}
141
142macro_rules! impl_from {
143 ($T:ty) => {
144 impl From<$T> for Atom {
145 fn from(s: $T) -> Self {
146 Atom::new(s)
147 }
148 }
149 };
150}
151
152impl From<hstr::Atom> for Atom {
153 #[inline(always)]
154 fn from(s: hstr::Atom) -> Self {
155 Atom(s)
156 }
157}
158
159impl From<Atom> for hstr::Wtf8Atom {
160 #[inline(always)]
161 fn from(s: Atom) -> Self {
162 hstr::Wtf8Atom::from(&*s)
163 }
164}
165
166impl PartialEq<str> for Atom {
167 fn eq(&self, other: &str) -> bool {
168 &**self == other
169 }
170}
171
172impl_eq!(&'_ str);
173impl_eq!(Box<str>);
174impl_eq!(std::sync::Arc<str>);
175impl_eq!(Rc<str>);
176impl_eq!(Cow<'_, str>);
177impl_eq!(String);
178
179impl_from!(&'_ str);
180impl_from!(Box<str>);
181impl_from!(String);
182impl_from!(Cow<'_, str>);
183
184impl AsRef<str> for Atom {
185 fn as_ref(&self) -> &str {
186 self
187 }
188}
189
190impl fmt::Debug for Atom {
191 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
192 fmt::Debug::fmt(&**self, f)
193 }
194}
195
196impl Display for Atom {
197 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
198 Display::fmt(&**self, f)
199 }
200}
201
202impl PartialOrd for Atom {
203 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
204 Some(self.cmp(other))
205 }
206}
207
208impl Ord for Atom {
209 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
210 self.as_str().cmp(other.as_str())
211 }
212}
213
214impl Borrow<Wtf8Atom> for Atom {
215 fn borrow(&self) -> &Wtf8Atom {
216 const _: () = assert!(std::mem::size_of::<Atom>() == std::mem::size_of::<Wtf8Atom>());
223 const _: () = assert!(std::mem::align_of::<Atom>() == std::mem::align_of::<Wtf8Atom>());
224 unsafe { transmute::<&Atom, &Wtf8Atom>(self) }
225 }
226}
227
228impl serde::ser::Serialize for Atom {
229 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
230 where
231 S: Serializer,
232 {
233 serializer.serialize_str(self)
234 }
235}
236
237impl<'de> serde::de::Deserialize<'de> for Atom {
238 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
239 where
240 D: serde::Deserializer<'de>,
241 {
242 String::deserialize(deserializer).map(Self::new)
243 }
244}
245
246#[macro_export]
248macro_rules! atom {
249 ($s:tt) => {{
250 $crate::Atom::from($crate::hstr::atom!($s))
251 }};
252}
253
254#[macro_export]
256macro_rules! lazy_atom {
257 ($s:tt) => {{
258 $crate::Atom::from($crate::hstr::atom!($s))
259 }};
260}
261
262impl PartialEq<Atom> for str {
263 fn eq(&self, other: &Atom) -> bool {
264 *self == **other
265 }
266}
267
268#[cfg(feature = "rkyv-impl")]
270impl rkyv::Archive for Atom {
271 type Archived = rkyv::string::ArchivedString;
272 type Resolver = rkyv::string::StringResolver;
273
274 #[allow(clippy::unit_arg)]
275 fn resolve(&self, resolver: Self::Resolver, out: rkyv::Place<Self::Archived>) {
276 rkyv::string::ArchivedString::resolve_from_str(self, resolver, out)
277 }
278}
279
280#[cfg(feature = "rkyv-impl")]
282impl<S: rancor::Fallible + rkyv::ser::Writer + ?Sized> rkyv::Serialize<S> for Atom
283where
284 <S as rancor::Fallible>::Error: rancor::Source,
285{
286 fn serialize(&self, serializer: &mut S) -> Result<Self::Resolver, S::Error> {
287 rkyv::string::ArchivedString::serialize_from_str(self.as_str(), serializer)
288 }
289}
290
291#[cfg(feature = "rkyv-impl")]
293impl<D> rkyv::Deserialize<Atom, D> for rkyv::string::ArchivedString
294where
295 D: ?Sized + rancor::Fallible,
296{
297 fn deserialize(&self, _: &mut D) -> Result<Atom, <D as rancor::Fallible>::Error> {
298 Ok(Atom::new(self.as_str()))
299 }
300}
301
302#[doc(hidden)]
303pub type CahcedAtom = Lazy<Atom>;
304
305pub type StaticString = Atom;
309
310#[derive(Default)]
311pub struct AtomStore(hstr::AtomStore);
312
313impl AtomStore {
314 #[inline]
315 pub fn atom<'a>(&mut self, s: impl Into<Cow<'a, str>>) -> Atom {
316 Atom(self.0.atom(s))
317 }
318
319 #[inline]
320 pub fn wtf8_atom<'a>(&mut self, s: impl Into<Cow<'a, Wtf8>>) -> Wtf8Atom {
321 Wtf8Atom(self.0.wtf8_atom(s))
322 }
323}
324
325#[derive(Default)]
327pub struct AtomStoreCell(UnsafeCell<AtomStore>);
328
329impl AtomStoreCell {
330 #[inline]
331 pub fn atom<'a>(&self, s: impl Into<Cow<'a, str>>) -> Atom {
332 let s: Cow<'a, str> = s.into();
334 unsafe { (*self.0.get()).atom(s) }
339 }
340
341 #[inline]
342 pub fn wtf8_atom<'a>(&self, s: impl Into<Cow<'a, Wtf8>>) -> Wtf8Atom {
343 let s: Cow<'a, Wtf8> = s.into();
345 unsafe { (*self.0.get()).wtf8_atom(s) }
350 }
351}
352
353#[cfg(feature = "shrink-to-fit")]
355impl shrink_to_fit::ShrinkToFit for Atom {
356 #[inline(always)]
357 fn shrink_to_fit(&mut self) {}
358}