1mod algo;
2mod napi;
3
4use std::fmt::Debug;
5
6use cow_utils::CowUtils;
7use rspack_cacheable::{
8 cacheable,
9 with::{AsString, AsStringConverter},
10};
11use rspack_error::Error;
12use swc_core::ecma::ast::Regex as SwcRegex;
13
14use self::algo::Algo;
15
16#[cacheable(with=AsString)]
18#[derive(Clone)]
19pub struct RspackRegex {
20 algo: Box<Algo>,
21 pub flags: String,
22 pub source: String,
23}
24
25impl PartialEq for RspackRegex {
26 fn eq(&self, other: &Self) -> bool {
27 self.flags == other.flags && self.source == other.source
28 }
29}
30
31impl Eq for RspackRegex {}
32
33impl std::hash::Hash for RspackRegex {
34 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
35 self.flags.hash(state);
36 self.source.hash(state);
37 }
38}
39
40impl Debug for RspackRegex {
41 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42 f.debug_struct("RspackRegex")
43 .field("flags", &self.flags)
44 .field("source", &self.source)
45 .finish()
46 }
47}
48
49impl RspackRegex {
50 #[inline]
51 pub fn test(&self, text: &str) -> bool {
52 self.algo.test(text)
53 }
54
55 #[inline]
56 pub fn global(&self) -> bool {
57 self.algo.global()
58 }
59
60 #[inline]
61 pub fn sticky(&self) -> bool {
62 self.algo.sticky()
63 }
64
65 #[inline]
66 pub fn source(&self) -> &str {
67 &self.source
68 }
69
70 #[inline]
71 pub fn flags(&self) -> &str {
72 &self.flags
73 }
74
75 #[inline]
76 pub fn new(expr: &str) -> Result<Self, Error> {
77 Self::with_flags(expr, "")
78 }
79
80 pub fn with_flags(expr: &str, flags: &str) -> Result<Self, Error> {
81 let mut chars = flags.chars().collect::<Vec<char>>();
82 chars.sort_unstable();
83 Ok(Self {
84 flags: chars.into_iter().collect::<String>(),
85 source: expr.to_string(),
86 algo: Box::new(Algo::new(expr, flags)?),
87 })
88 }
89
90 #[inline]
92 pub fn to_source_string(&self) -> String {
93 format!("/{}/{}", self.source, self.flags)
94 }
95
96 #[inline]
98 pub fn to_pretty_string(&self, strip_slash: bool) -> String {
99 if strip_slash {
100 format!("{}{}", self.source, self.flags)
101 } else {
102 self.to_source_string()
103 }
104 .cow_replace('!', "%21")
105 .cow_replace('|', "%7C")
106 .into_owned()
107 }
108}
109
110impl TryFrom<&SwcRegex> for RspackRegex {
111 type Error = Error;
112
113 fn try_from(value: &SwcRegex) -> Result<Self, Self::Error> {
114 RspackRegex::with_flags(value.exp.as_ref(), value.flags.as_ref())
115 }
116}
117
118impl TryFrom<SwcRegex> for RspackRegex {
119 type Error = Error;
120
121 fn try_from(value: SwcRegex) -> Result<Self, Self::Error> {
122 RspackRegex::with_flags(value.exp.as_ref(), value.flags.as_ref())
123 }
124}
125
126impl AsStringConverter for RspackRegex {
127 fn to_string(&self) -> Result<String, rspack_cacheable::SerializeError> {
128 Ok(format!("{}#{}", self.flags, self.source))
129 }
130 fn from_str(s: &str) -> Result<Self, rspack_cacheable::DeserializeError>
131 where
132 Self: Sized,
133 {
134 let (flags, source) = s.split_once("#").expect("should have flags");
135 Ok(RspackRegex::with_flags(source, flags).expect("should generate regex"))
136 }
137}