rspack_regex/
lib.rs

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/// Using wrapper type required by [TryFrom] trait
17#[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  // https://github.com/webpack/webpack/blob/4baf1c075d59babd028f8201526cb8c4acfd24a0/lib/dependencies/ContextDependency.js#L30
91  #[inline]
92  pub fn to_source_string(&self) -> String {
93    format!("/{}/{}", self.source, self.flags)
94  }
95
96  // https://github.com/webpack/webpack/blob/4baf1c075d59babd028f8201526cb8c4acfd24a0/lib/ContextModule.js#L192
97  #[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}