1use std::{
2 borrow::{Borrow, Cow},
3 fmt, hash,
4 ops::Deref,
5};
6
7use oxc_allocator::{Allocator, CloneIn, Dummy, FromIn, StringBuilder as ArenaStringBuilder};
8#[cfg(feature = "serialize")]
9use oxc_estree::{ESTree, Serializer as ESTreeSerializer};
10#[cfg(feature = "serialize")]
11use serde::{Serialize, Serializer as SerdeSerializer};
12
13use crate::CompactStr;
14
15#[repr(transparent)]
20#[derive(Clone, Copy, Eq)]
21pub struct Atom<'a>(&'a str);
22
23impl Atom<'static> {
24 #[expect(clippy::inline_always)]
26 #[inline(always)] pub const fn new_const(s: &'static str) -> Self {
28 Atom(s)
29 }
30
31 #[inline]
33 pub const fn empty() -> Self {
34 Self::new_const("")
35 }
36}
37
38impl<'a> Atom<'a> {
39 #[expect(clippy::inline_always)]
41 #[inline(always)] pub fn as_str(&self) -> &'a str {
43 self.0
44 }
45
46 #[inline]
50 pub fn into_string(self) -> String {
51 String::from(self.as_str())
52 }
53
54 #[inline]
58 pub fn into_compact_str(self) -> CompactStr {
59 CompactStr::new(self.as_str())
60 }
61
62 #[inline]
64 pub fn to_compact_str(self) -> CompactStr {
65 CompactStr::new(self.as_str())
66 }
67
68 #[expect(clippy::inline_always)]
87 #[inline(always)]
88 pub fn from_strs_array_in<const N: usize>(
89 strings: [&str; N],
90 allocator: &'a Allocator,
91 ) -> Atom<'a> {
92 Self::from(allocator.alloc_concat_strs_array(strings))
93 }
94
95 #[inline]
102 pub fn from_cow_in(value: &Cow<'a, str>, allocator: &'a Allocator) -> Atom<'a> {
103 match value {
104 Cow::Borrowed(s) => Atom::from(*s),
105 Cow::Owned(s) => Atom::from_in(s, allocator),
106 }
107 }
108}
109
110impl<'new_alloc> CloneIn<'new_alloc> for Atom<'_> {
111 type Cloned = Atom<'new_alloc>;
112
113 #[inline]
114 fn clone_in(&self, allocator: &'new_alloc Allocator) -> Self::Cloned {
115 Atom::from_in(self.as_str(), allocator)
116 }
117}
118
119impl<'a> Dummy<'a> for Atom<'a> {
120 #[expect(clippy::inline_always)]
122 #[inline(always)]
123 fn dummy(_allocator: &'a Allocator) -> Self {
124 Atom::empty()
125 }
126}
127
128impl<'alloc> FromIn<'alloc, &Atom<'alloc>> for Atom<'alloc> {
129 #[expect(clippy::inline_always)]
130 #[inline(always)] fn from_in(s: &Atom<'alloc>, _: &'alloc Allocator) -> Self {
132 *s
133 }
134}
135
136impl<'alloc> FromIn<'alloc, &str> for Atom<'alloc> {
137 #[inline]
138 fn from_in(s: &str, allocator: &'alloc Allocator) -> Self {
139 Self::from(allocator.alloc_str(s))
140 }
141}
142
143impl<'alloc> FromIn<'alloc, String> for Atom<'alloc> {
144 #[inline]
145 fn from_in(s: String, allocator: &'alloc Allocator) -> Self {
146 Self::from_in(s.as_str(), allocator)
147 }
148}
149
150impl<'alloc> FromIn<'alloc, &String> for Atom<'alloc> {
151 #[inline]
152 fn from_in(s: &String, allocator: &'alloc Allocator) -> Self {
153 Self::from_in(s.as_str(), allocator)
154 }
155}
156
157impl<'alloc> FromIn<'alloc, Cow<'_, str>> for Atom<'alloc> {
158 #[inline]
159 fn from_in(s: Cow<'_, str>, allocator: &'alloc Allocator) -> Self {
160 Self::from_in(&*s, allocator)
161 }
162}
163
164impl<'a> From<&'a str> for Atom<'a> {
165 #[expect(clippy::inline_always)]
166 #[inline(always)] fn from(s: &'a str) -> Self {
168 Self(s)
169 }
170}
171
172impl<'alloc> From<ArenaStringBuilder<'alloc>> for Atom<'alloc> {
173 #[inline]
174 fn from(s: ArenaStringBuilder<'alloc>) -> Self {
175 Self::from(s.into_str())
176 }
177}
178
179impl<'a> From<Atom<'a>> for &'a str {
180 #[expect(clippy::inline_always)]
181 #[inline(always)] fn from(s: Atom<'a>) -> Self {
183 s.as_str()
184 }
185}
186
187impl From<Atom<'_>> for CompactStr {
188 #[inline]
189 fn from(val: Atom<'_>) -> Self {
190 val.into_compact_str()
191 }
192}
193
194impl From<Atom<'_>> for String {
195 #[inline]
196 fn from(val: Atom<'_>) -> Self {
197 val.into_string()
198 }
199}
200
201impl<'a> From<Atom<'a>> for Cow<'a, str> {
202 #[inline]
203 fn from(value: Atom<'a>) -> Self {
204 Cow::Borrowed(value.as_str())
205 }
206}
207
208impl Deref for Atom<'_> {
209 type Target = str;
210
211 #[expect(clippy::inline_always)]
212 #[inline(always)] fn deref(&self) -> &Self::Target {
214 self.as_str()
215 }
216}
217
218impl AsRef<str> for Atom<'_> {
219 #[expect(clippy::inline_always)]
220 #[inline(always)] fn as_ref(&self) -> &str {
222 self.as_str()
223 }
224}
225
226impl Borrow<str> for Atom<'_> {
227 #[expect(clippy::inline_always)]
228 #[inline(always)] fn borrow(&self) -> &str {
230 self.as_str()
231 }
232}
233
234impl<T: AsRef<str>> PartialEq<T> for Atom<'_> {
235 #[inline]
236 fn eq(&self, other: &T) -> bool {
237 self.as_str() == other.as_ref()
238 }
239}
240
241impl PartialEq<Atom<'_>> for &str {
242 #[inline]
243 fn eq(&self, other: &Atom<'_>) -> bool {
244 *self == other.as_str()
245 }
246}
247
248impl PartialEq<str> for Atom<'_> {
249 #[inline]
250 fn eq(&self, other: &str) -> bool {
251 self.as_str() == other
252 }
253}
254
255impl PartialEq<Atom<'_>> for Cow<'_, str> {
256 #[inline]
257 fn eq(&self, other: &Atom<'_>) -> bool {
258 self.as_ref() == other.as_str()
259 }
260}
261
262impl hash::Hash for Atom<'_> {
263 #[inline]
264 fn hash<H: hash::Hasher>(&self, hasher: &mut H) {
265 self.as_str().hash(hasher);
266 }
267}
268
269impl fmt::Debug for Atom<'_> {
270 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
271 fmt::Debug::fmt(self.as_str(), f)
272 }
273}
274
275impl fmt::Display for Atom<'_> {
276 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
277 fmt::Display::fmt(self.as_str(), f)
278 }
279}
280
281#[cfg(feature = "serialize")]
282impl Serialize for Atom<'_> {
283 #[inline] fn serialize<S: SerdeSerializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
285 Serialize::serialize(self.as_str(), serializer)
286 }
287}
288
289#[cfg(feature = "serialize")]
290impl ESTree for Atom<'_> {
291 #[inline] fn serialize<S: ESTreeSerializer>(&self, serializer: S) {
293 ESTree::serialize(self.as_str(), serializer);
294 }
295}
296
297#[macro_export]
323macro_rules! format_atom {
324 ($alloc:expr, $($arg:tt)*) => {{
325 use ::std::{write, fmt::Write};
326 use $crate::{Atom, __internal::ArenaStringBuilder};
327
328 let mut s = ArenaStringBuilder::new_in($alloc);
329 write!(s, $($arg)*).unwrap();
330 Atom::from(s)
331 }}
332}