1use std::{
2    borrow::Cow,
3    fmt::{self, Display, Formatter},
4    hash::{Hash, Hasher},
5};
6
7use is_macro::Is;
8use num_bigint::BigInt as BigIntValue;
9use swc_atoms::{Atom, Wtf8Atom};
10use swc_common::{ast_node, util::take::Take, EqIgnoreSpan, Span, DUMMY_SP};
11
12use crate::jsx::JSXText;
13
14#[ast_node]
15#[derive(Eq, Hash, EqIgnoreSpan, Is)]
16#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
17#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
18pub enum Lit {
19    #[tag("StringLiteral")]
20    Str(Str),
21
22    #[tag("BooleanLiteral")]
23    Bool(Bool),
24
25    #[tag("NullLiteral")]
26    Null(Null),
27
28    #[tag("NumericLiteral")]
29    Num(Number),
30
31    #[tag("BigIntLiteral")]
32    BigInt(BigInt),
33
34    #[tag("RegExpLiteral")]
35    Regex(Regex),
36
37    #[tag("JSXText")]
38    JSXText(JSXText),
39}
40
41macro_rules! bridge_lit_from {
42    ($bridge: ty, $src:ty) => {
43        bridge_expr_from!(crate::Lit, $src);
44        bridge_from!(Lit, $bridge, $src);
45    };
46}
47
48bridge_expr_from!(Lit, Str);
49bridge_expr_from!(Lit, Bool);
50bridge_expr_from!(Lit, Number);
51bridge_expr_from!(Lit, BigInt);
52bridge_expr_from!(Lit, Regex);
53bridge_expr_from!(Lit, Null);
54bridge_expr_from!(Lit, JSXText);
55
56bridge_lit_from!(Str, &'_ str);
57bridge_lit_from!(Str, Atom);
58bridge_lit_from!(Str, Wtf8Atom);
59bridge_lit_from!(Str, Cow<'_, str>);
60bridge_lit_from!(Str, String);
61bridge_lit_from!(Bool, bool);
62bridge_lit_from!(Number, f64);
63bridge_lit_from!(Number, usize);
64bridge_lit_from!(BigInt, BigIntValue);
65
66impl Lit {
67    pub fn set_span(&mut self, span: Span) {
68        match self {
69            Lit::Str(s) => s.span = span,
70            Lit::Bool(b) => b.span = span,
71            Lit::Null(n) => n.span = span,
72            Lit::Num(n) => n.span = span,
73            Lit::BigInt(n) => n.span = span,
74            Lit::Regex(n) => n.span = span,
75            Lit::JSXText(n) => n.span = span,
76            #[cfg(all(swc_ast_unknown, feature = "encoding-impl"))]
77            _ => swc_common::unknown!(),
78        }
79    }
80}
81
82#[ast_node("BigIntLiteral")]
83#[derive(Eq, Hash)]
84pub struct BigInt {
85    pub span: Span,
86    #[cfg_attr(any(feature = "rkyv-impl"), rkyv(with = EncodeBigInt))]
87    #[cfg_attr(feature = "encoding-impl", encoding(with = "EncodeBigInt2"))]
88    pub value: Box<BigIntValue>,
89
90    #[cfg_attr(
93        feature = "encoding-impl",
94        encoding(with = "cbor4ii::core::types::Maybe")
95    )]
96    pub raw: Option<Atom>,
97}
98
99#[cfg(feature = "shrink-to-fit")]
100impl shrink_to_fit::ShrinkToFit for BigInt {
101    #[inline(always)]
102    fn shrink_to_fit(&mut self) {}
103}
104
105impl EqIgnoreSpan for BigInt {
106    fn eq_ignore_span(&self, other: &Self) -> bool {
107        self.value == other.value
108    }
109}
110
111#[cfg(feature = "encoding-impl")]
112struct EncodeBigInt2<T>(T);
113
114#[cfg(feature = "encoding-impl")]
115impl cbor4ii::core::enc::Encode for EncodeBigInt2<&'_ Box<BigIntValue>> {
116    #[inline]
117    fn encode<W: cbor4ii::core::enc::Write>(
118        &self,
119        writer: &mut W,
120    ) -> Result<(), cbor4ii::core::enc::Error<W::Error>> {
121        cbor4ii::core::types::Bytes(self.0.to_signed_bytes_le().as_slice()).encode(writer)
122    }
123}
124
125#[cfg(feature = "encoding-impl")]
126impl<'de> cbor4ii::core::dec::Decode<'de> for EncodeBigInt2<Box<BigIntValue>> {
127    #[inline]
128    fn decode<R: cbor4ii::core::dec::Read<'de>>(
129        reader: &mut R,
130    ) -> Result<Self, cbor4ii::core::dec::Error<R::Error>> {
131        let buf = <cbor4ii::core::types::Bytes<&'de [u8]>>::decode(reader)?;
132        Ok(EncodeBigInt2(Box::new(BigIntValue::from_signed_bytes_le(
133            buf.0,
134        ))))
135    }
136}
137
138#[cfg(feature = "rkyv-impl")]
139#[derive(Debug, Clone, Copy)]
140#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
141#[cfg_attr(feature = "rkyv-impl", repr(C))]
142pub struct EncodeBigInt;
143
144#[cfg(feature = "rkyv-impl")]
145impl rkyv::with::ArchiveWith<Box<BigIntValue>> for EncodeBigInt {
146    type Archived = rkyv::Archived<String>;
147    type Resolver = rkyv::Resolver<String>;
148
149    fn resolve_with(
150        field: &Box<BigIntValue>,
151        resolver: Self::Resolver,
152        out: rkyv::Place<Self::Archived>,
153    ) {
154        use rkyv::Archive;
155
156        let s = field.to_string();
157        s.resolve(resolver, out);
158    }
159}
160
161#[cfg(feature = "rkyv-impl")]
162impl<S> rkyv::with::SerializeWith<Box<BigIntValue>, S> for EncodeBigInt
163where
164    S: ?Sized + rancor::Fallible + rkyv::ser::Writer,
165    S::Error: rancor::Source,
166{
167    fn serialize_with(
168        field: &Box<BigIntValue>,
169        serializer: &mut S,
170    ) -> Result<Self::Resolver, S::Error> {
171        let field = field.to_string();
172        rkyv::string::ArchivedString::serialize_from_str(&field, serializer)
173    }
174}
175
176#[cfg(feature = "rkyv-impl")]
177impl<D> rkyv::with::DeserializeWith<rkyv::Archived<String>, Box<BigIntValue>, D> for EncodeBigInt
178where
179    D: ?Sized + rancor::Fallible,
180{
181    fn deserialize_with(
182        field: &rkyv::Archived<String>,
183        deserializer: &mut D,
184    ) -> Result<Box<BigIntValue>, D::Error> {
185        use rkyv::Deserialize;
186
187        let s: String = field.deserialize(deserializer)?;
188
189        Ok(Box::new(s.parse().unwrap()))
190    }
191}
192
193#[cfg(feature = "arbitrary")]
194#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
195impl<'a> arbitrary::Arbitrary<'a> for BigInt {
196    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
197        let span = u.arbitrary()?;
198        let value = Box::new(u.arbitrary::<usize>()?.into());
199        let raw = Some(u.arbitrary::<String>()?.into());
200
201        Ok(Self { span, value, raw })
202    }
203}
204
205impl From<BigIntValue> for BigInt {
206    #[inline]
207    fn from(value: BigIntValue) -> Self {
208        BigInt {
209            span: DUMMY_SP,
210            value: Box::new(value),
211            raw: None,
212        }
213    }
214}
215
216#[ast_node("StringLiteral")]
218#[derive(Eq, Hash)]
219#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
220pub struct Str {
221    pub span: Span,
222
223    pub value: Wtf8Atom,
224
225    #[cfg_attr(
228        feature = "encoding-impl",
229        encoding(with = "cbor4ii::core::types::Maybe")
230    )]
231    pub raw: Option<Atom>,
232}
233
234impl Take for Str {
235    fn dummy() -> Self {
236        Str {
237            span: DUMMY_SP,
238            value: Wtf8Atom::default(),
239            raw: None,
240        }
241    }
242}
243
244#[cfg(feature = "arbitrary")]
245#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
246impl<'a> arbitrary::Arbitrary<'a> for Str {
247    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
248        let span = u.arbitrary()?;
249        let value = u.arbitrary::<Wtf8Atom>()?.into();
250        let raw = Some(u.arbitrary::<String>()?.into());
251
252        Ok(Self { span, value, raw })
253    }
254}
255
256impl Str {
257    #[inline]
258    pub fn is_empty(&self) -> bool {
259        self.value.is_empty()
260    }
261
262    pub fn from_tpl_raw(tpl_raw: &str) -> Atom {
263        let mut buf = String::with_capacity(tpl_raw.len());
264
265        let mut iter = tpl_raw.chars();
266
267        while let Some(c) = iter.next() {
268            match c {
269                '\\' => {
270                    if let Some(next) = iter.next() {
271                        match next {
272                            '`' | '$' | '\\' => {
273                                buf.push(next);
274                            }
275                            'b' => {
276                                buf.push('\u{0008}');
277                            }
278                            'f' => {
279                                buf.push('\u{000C}');
280                            }
281                            'n' => {
282                                buf.push('\n');
283                            }
284                            'r' => {
285                                buf.push('\r');
286                            }
287                            't' => {
288                                buf.push('\t');
289                            }
290                            'v' => {
291                                buf.push('\u{000B}');
292                            }
293                            _ => {
294                                buf.push('\\');
295                                buf.push(next);
296                            }
297                        }
298                    }
299                }
300
301                c => {
302                    buf.push(c);
303                }
304            }
305        }
306
307        buf.into()
308    }
309}
310
311impl EqIgnoreSpan for Str {
312    fn eq_ignore_span(&self, other: &Self) -> bool {
313        self.value == other.value
314    }
315}
316
317impl From<Atom> for Str {
318    #[inline]
319    fn from(value: Atom) -> Self {
320        Str {
321            span: DUMMY_SP,
322            value: value.into(),
323            raw: None,
324        }
325    }
326}
327
328impl From<Wtf8Atom> for Str {
329    #[inline]
330    fn from(value: Wtf8Atom) -> Self {
331        Str {
332            span: DUMMY_SP,
333            value,
334            raw: None,
335        }
336    }
337}
338
339bridge_from!(Str, Atom, &'_ str);
340bridge_from!(Str, Atom, String);
341bridge_from!(Str, Atom, Cow<'_, str>);
342
343#[ast_node("BooleanLiteral")]
353#[derive(Copy, Eq, Hash, EqIgnoreSpan)]
354#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
355#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
356pub struct Bool {
357    pub span: Span,
358    pub value: bool,
359}
360
361impl Take for Bool {
362    fn dummy() -> Self {
363        Bool {
364            span: DUMMY_SP,
365            value: false,
366        }
367    }
368}
369
370impl From<bool> for Bool {
371    #[inline]
372    fn from(value: bool) -> Self {
373        Bool {
374            span: DUMMY_SP,
375            value,
376        }
377    }
378}
379
380#[ast_node("NullLiteral")]
381#[derive(Copy, Eq, Hash, EqIgnoreSpan)]
382#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
383#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
384pub struct Null {
385    pub span: Span,
386}
387
388impl Take for Null {
389    fn dummy() -> Self {
390        Null { span: DUMMY_SP }
391    }
392}
393
394#[ast_node("RegExpLiteral")]
395#[derive(Eq, Hash, EqIgnoreSpan)]
396#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
397pub struct Regex {
398    pub span: Span,
399
400    #[cfg_attr(feature = "serde-impl", serde(rename = "pattern"))]
401    pub exp: Atom,
402
403    #[cfg_attr(feature = "serde-impl", serde(default))]
404    pub flags: Atom,
405}
406
407impl Take for Regex {
408    fn dummy() -> Self {
409        Self {
410            span: DUMMY_SP,
411            exp: Default::default(),
412            flags: Default::default(),
413        }
414    }
415}
416
417#[cfg(feature = "arbitrary")]
418#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
419impl<'a> arbitrary::Arbitrary<'a> for Regex {
420    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
421        use swc_atoms::atom;
422
423        let span = u.arbitrary()?;
424        let exp = u.arbitrary::<String>()?.into();
425        let flags = atom!(""); Ok(Self { span, exp, flags })
428    }
429}
430
431#[ast_node("NumericLiteral")]
443#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
444pub struct Number {
445    pub span: Span,
446    pub value: f64,
450
451    #[cfg_attr(
454        feature = "encoding-impl",
455        encoding(with = "cbor4ii::core::types::Maybe")
456    )]
457    pub raw: Option<Atom>,
458}
459
460impl Eq for Number {}
461
462impl EqIgnoreSpan for Number {
463    fn eq_ignore_span(&self, other: &Self) -> bool {
464        self.value == other.value && self.value.is_sign_positive() == other.value.is_sign_positive()
465    }
466}
467
468#[allow(clippy::derived_hash_with_manual_eq)]
469#[allow(clippy::transmute_float_to_int)]
470impl Hash for Number {
471    fn hash<H: Hasher>(&self, state: &mut H) {
472        fn integer_decode(val: f64) -> (u64, i16, i8) {
473            let bits: u64 = val.to_bits();
474            let sign: i8 = if bits >> 63 == 0 { 1 } else { -1 };
475            let mut exponent: i16 = ((bits >> 52) & 0x7ff) as i16;
476            let mantissa = if exponent == 0 {
477                (bits & 0xfffffffffffff) << 1
478            } else {
479                (bits & 0xfffffffffffff) | 0x10000000000000
480            };
481
482            exponent -= 1023 + 52;
483            (mantissa, exponent, sign)
484        }
485
486        self.span.hash(state);
487        integer_decode(self.value).hash(state);
488        self.raw.hash(state);
489    }
490}
491
492impl Display for Number {
493    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
494        if self.value.is_infinite() {
495            if self.value.is_sign_positive() {
496                Display::fmt("Infinity", f)
497            } else {
498                Display::fmt("-Infinity", f)
499            }
500        } else {
501            Display::fmt(&self.value, f)
502        }
503    }
504}
505
506#[cfg(feature = "arbitrary")]
507#[cfg_attr(docsrs, doc(cfg(feature = "arbitrary")))]
508impl<'a> arbitrary::Arbitrary<'a> for Number {
509    fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
510        let span = u.arbitrary()?;
511        let value = u.arbitrary::<f64>()?;
512        let raw = Some(u.arbitrary::<String>()?.into());
513
514        Ok(Self { span, value, raw })
515    }
516}
517
518impl From<f64> for Number {
519    #[inline]
520    fn from(value: f64) -> Self {
521        Number {
522            span: DUMMY_SP,
523            value,
524            raw: None,
525        }
526    }
527}
528
529impl From<usize> for Number {
530    #[inline]
531    fn from(value: usize) -> Self {
532        Number {
533            span: DUMMY_SP,
534            value: value as _,
535            raw: None,
536        }
537    }
538}