1use std::ops::Deref;
22use std::fmt;
23use std::str;
24
25use std::sync::Arc;
26use oncemutex::OnceMutex;
27
28use regex::{Regex, RegexBuilder, Error};
29use crate::syntax;
30use crate::options::Options;
31
32#[derive(Clone)]
48pub struct LazyRegex {
49 builder: LazyRegexBuilder,
50 regex: Arc<OnceMutex<Option<Regex>>>
51}
52
53impl LazyRegex {
54 pub fn new(source: &str) -> Result<LazyRegex, Error> {
57 if let Err(err) = syntax::Parser::new().parse(source) {
58 return Err(Error::Syntax(err.to_string()));
59 }
60
61 Ok(LazyRegex::from(LazyRegexBuilder::new(source)))
62 }
63
64 fn from(builder: LazyRegexBuilder) -> Self {
65 LazyRegex {
66 builder: builder,
67 regex: Arc::new(OnceMutex::new(None)),
68 }
69 }
70
71 fn create(builder: &LazyRegexBuilder) -> Regex {
72 builder.options.define(&mut RegexBuilder::new(&builder.source))
73 .build().unwrap()
74 }
75}
76
77impl Deref for LazyRegex {
78 type Target = Regex;
79
80 fn deref(&self) -> &Regex {
81 self.as_ref()
82 }
83}
84
85impl AsRef<Regex> for LazyRegex {
86 fn as_ref(&self) -> &Regex {
87 if let Some(mut guard) = self.regex.lock() {
88 *guard = Some(LazyRegex::create(&self.builder));
89 }
90
91 (*self.regex).as_ref().unwrap()
92 }
93}
94
95impl Into<Regex> for LazyRegex {
96 fn into(self) -> Regex {
97 let (regex, builder) = (self.regex, self.builder);
98
99 Arc::try_unwrap(regex).ok().and_then(|m| m.into_inner()).unwrap_or_else(||
100 LazyRegex::create(&builder))
101 }
102}
103
104impl fmt::Debug for LazyRegex {
105 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
106 fmt::Debug::fmt(&**self, f)
107 }
108}
109
110impl fmt::Display for LazyRegex {
111 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
112 fmt::Display::fmt(&**self, f)
113 }
114}
115
116impl str::FromStr for LazyRegex {
117 type Err = Error;
118
119 fn from_str(s: &str) -> Result<LazyRegex, Error> {
120 LazyRegex::new(s)
121 }
122}
123
124#[derive(Clone, Eq, PartialEq, Debug)]
126pub struct LazyRegexBuilder {
127 source: String,
128 options: Options,
129}
130
131impl LazyRegexBuilder {
132 pub fn new(source: &str) -> LazyRegexBuilder {
137 LazyRegexBuilder {
138 source: source.to_owned(),
139 options: Default::default(),
140 }
141 }
142
143 pub fn build(&self) -> Result<LazyRegex, Error> {
149 if let Err(err) = syntax::Parser::new().parse(&self.source) {
150 return Err(Error::Syntax(err.to_string()));
151 }
152
153 Ok(LazyRegex::from(self.clone()))
154 }
155
156 pub fn case_insensitive(&mut self, yes: bool) -> &mut LazyRegexBuilder {
158 self.options.case_insensitive = yes;
159 self
160 }
161
162 pub fn multi_line(&mut self, yes: bool) -> &mut LazyRegexBuilder {
164 self.options.multi_line = yes;
165 self
166 }
167
168 pub fn dot_matches_new_line(&mut self, yes: bool) -> &mut LazyRegexBuilder {
176 self.options.dot_matches_new_line = yes;
177 self
178 }
179
180 pub fn swap_greed(&mut self, yes: bool) -> &mut LazyRegexBuilder {
182 self.options.swap_greed = yes;
183 self
184 }
185
186 pub fn ignore_whitespace(&mut self, yes: bool) -> &mut LazyRegexBuilder {
188 self.options.ignore_whitespace = yes;
189 self
190 }
191
192 pub fn unicode(&mut self, yes: bool) -> &mut LazyRegexBuilder {
194 self.options.unicode = yes;
195 self
196 }
197
198 pub fn size_limit(&mut self, limit: usize) -> &mut LazyRegexBuilder {
204 self.options.size_limit = limit;
205 self
206 }
207
208 pub fn dfa_size_limit(&mut self, limit: usize) -> &mut LazyRegexBuilder {
218 self.options.dfa_size_limit = limit;
219 self
220 }
221}
222
223#[cfg(test)]
224mod test {
225 use crate::{LazyRegex, LazyRegexBuilder};
226
227 #[test]
228 fn new() {
229 assert!(LazyRegex::new(r"^\d+$").unwrap()
230 .is_match("2345"));
231
232 assert!(!LazyRegex::new(r"^[a-z]+$").unwrap()
233 .is_match("2345"));
234 }
235
236 #[test]
237 fn build() {
238 assert!(LazyRegexBuilder::new(r"^abc$")
239 .case_insensitive(true).build().unwrap()
240 .is_match("ABC"));
241
242 assert!(!LazyRegexBuilder::new(r"^abc$")
243 .case_insensitive(false).build().unwrap()
244 .is_match("ABC"));
245 }
246
247 #[test]
248 fn same() {
249 let re = LazyRegex::new(r"^\d+$").unwrap();
250
251 assert!(re.is_match("1234"));
252 assert!(re.is_match("1234"));
253 assert!(re.is_match("1234"));
254 assert!(re.is_match("1234"));
255 }
256}