1use std::ops::{Deref, DerefMut};
22use std::sync::{Mutex, Arc};
23use std::borrow::Cow;
24use std::fmt;
25use std::str;
26
27use regex::{Regex, RegexBuilder, Error};
28use regex::{Match, Captures, Replacer};
29use crate::syntax;
30use crate::options::Options;
31use crate::lru::LruCache;
32
33#[derive(Clone, Debug)]
35pub struct RegexCache(LruCache<String, Regex>);
36
37impl RegexCache {
38 pub fn new(capacity: usize) -> RegexCache {
40 RegexCache(LruCache::new(capacity))
41 }
42
43 pub fn save(&mut self, re: Regex) -> &Regex {
60 let source = re.as_str().to_owned();
61
62 if !self.0.contains_key(re.as_str()) {
63 self.insert(source.clone(), re);
64 }
65
66 self.0.get_mut(&source).unwrap()
67 }
68
69 pub fn compile(&mut self, source: &str) -> Result<&Regex, Error> {
81 if !self.0.contains_key(source) {
82 self.0.insert(source.into(), Regex::new(source)?);
83 }
84
85 Ok(self.0.get_mut(source).unwrap())
86 }
87
88 pub fn configure<F>(&mut self, source: &str, f: F) -> Result<&Regex, Error>
103 where F: FnOnce(&mut RegexBuilder) -> &mut RegexBuilder
104 {
105 if !self.0.contains_key(source) {
106 self.0.insert(source.into(), f(&mut RegexBuilder::new(source)).build()?);
107 }
108
109 Ok(self.0.get_mut(source).unwrap())
110 }
111}
112
113impl Deref for RegexCache {
114 type Target = LruCache<String, Regex>;
115
116 fn deref(&self) -> &Self::Target {
117 &self.0
118 }
119}
120
121impl DerefMut for RegexCache {
122 fn deref_mut(&mut self) -> &mut Self::Target {
123 &mut self.0
124 }
125}
126
127#[derive(Clone)]
128pub struct CachedRegex {
129 builder: CachedRegexBuilder,
130}
131
132macro_rules! regex {
133 ($self:ident) => (
134 $self.builder.cache.lock().unwrap().configure(&$self.builder.source, |b|
135 $self.builder.options.define(b)).unwrap()
136 )
137}
138
139impl CachedRegex {
140 pub fn new(cache: Arc<Mutex<RegexCache>>, source: &str) -> Result<CachedRegex, Error> {
143 if let Err(err) = syntax::Parser::new().parse(source) {
144 return Err(Error::Syntax(err.to_string()));
145 }
146
147 Ok(CachedRegex::new_unchecked(cache, source))
148 }
149
150 pub fn new_unchecked(cache: Arc<Mutex<RegexCache>>, source: &str) -> CachedRegex {
156 CachedRegex::from(CachedRegexBuilder::new(cache, source))
157 }
158
159 fn from(builder: CachedRegexBuilder) -> Self {
160 CachedRegex {
161 builder: builder,
162 }
163 }
164
165 pub fn is_match(&self, text: &str) -> bool {
167 regex!(self).is_match(text)
168 }
169
170 pub fn find<'t>(&self, text: &'t str) -> Option<Match<'t>> {
172 regex!(self).find(text)
173 }
174
175 pub fn captures<'t>(&self, text: &'t str) -> Option<Captures<'t>> {
177 regex!(self).captures(text)
178 }
179
180 pub fn replace<'t, R: Replacer>(&self, text: &'t str, rep: R) -> Cow<'t, str> {
182 regex!(self).replace(text, rep)
183 }
184
185 pub fn replace_all<'t, R: Replacer>(&self, text: &'t str, rep: R) -> Cow<'t, str> {
187 regex!(self).replace_all(text, rep)
188 }
189
190 pub fn shortest_match(&self, text: &str) -> Option<usize> {
192 regex!(self).shortest_match(text)
193 }
194
195 pub fn captures_len(&self) -> usize {
196 regex!(self).captures_len()
197 }
198
199 pub fn as_str(&self) -> &str {
200 &self.builder.source
201 }
202}
203
204impl fmt::Debug for CachedRegex {
205 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
206 fmt::Debug::fmt(regex!(self), f)
207 }
208}
209
210impl fmt::Display for CachedRegex {
211 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
212 fmt::Display::fmt(regex!(self), f)
213 }
214}
215
216#[derive(Clone, Debug)]
218pub struct CachedRegexBuilder {
219 cache: Arc<Mutex<RegexCache>>,
220 source: String,
221 options: Options,
222}
223
224impl CachedRegexBuilder {
225 pub fn new(cache: Arc<Mutex<RegexCache>>, source: &str) -> CachedRegexBuilder {
230 CachedRegexBuilder {
231 cache: cache,
232 source: source.to_owned(),
233 options: Default::default(),
234 }
235 }
236
237 pub fn build(&self) -> Result<CachedRegex, Error> {
243 if let Err(err) = syntax::Parser::new().parse(&self.source) {
244 return Err(Error::Syntax(err.to_string()));
245 }
246
247 Ok(CachedRegex::from(self.clone()))
248 }
249
250 pub fn build_unchecked(&self) -> CachedRegex {
260 CachedRegex::from(self.clone())
261 }
262
263 pub fn case_insensitive(&mut self, yes: bool) -> &mut CachedRegexBuilder {
265 self.options.case_insensitive = yes;
266 self
267 }
268
269 pub fn multi_line(&mut self, yes: bool) -> &mut CachedRegexBuilder {
271 self.options.multi_line = yes;
272 self
273 }
274
275 pub fn dot_matches_new_line(&mut self, yes: bool) -> &mut CachedRegexBuilder {
283 self.options.dot_matches_new_line = yes;
284 self
285 }
286
287 pub fn swap_greed(&mut self, yes: bool) -> &mut CachedRegexBuilder {
289 self.options.swap_greed = yes;
290 self
291 }
292
293 pub fn ignore_whitespace(&mut self, yes: bool) -> &mut CachedRegexBuilder {
295 self.options.ignore_whitespace = yes;
296 self
297 }
298
299 pub fn unicode(&mut self, yes: bool) -> &mut CachedRegexBuilder {
301 self.options.unicode = yes;
302 self
303 }
304
305 pub fn size_limit(&mut self, limit: usize) -> &mut CachedRegexBuilder {
311 self.options.size_limit = limit;
312 self
313 }
314
315 pub fn dfa_size_limit(&mut self, limit: usize) -> &mut CachedRegexBuilder {
325 self.options.dfa_size_limit = limit;
326 self
327 }
328}
329
330#[cfg(test)]
331mod test {
332 use std::sync::{Arc, Mutex};
333 use crate::cache::{RegexCache, CachedRegex};
334
335 #[test]
336 fn respects_limit() {
337 let mut cache = RegexCache::new(2);
338
339 cache.compile("[01]2").unwrap();
340 cache.compile("[21]0").unwrap();
341
342 assert_eq!(cache.len(), 2);
343 cache.compile("[21]3").unwrap();
344 assert_eq!(cache.len(), 2);
345 }
346
347 #[test]
348 fn cached_regex() {
349 let cache = Arc::new(Mutex::new(RegexCache::new(100)));
350 let re = CachedRegex::new(cache.clone(), r"^\d+$").unwrap();
351
352 assert!(re.is_match("123"));
353 assert!(!re.is_match("abc"));
354 }
355}