use std::ops::Deref;
use std::fmt;
use std::str;
use std::sync::Arc;
use oncemutex::OnceMutex;
use regex::{Regex, RegexBuilder, Error};
use syntax;
use options::Options;
#[derive(Clone)]
pub struct LazyRegex {
builder: LazyRegexBuilder,
regex: Arc<OnceMutex<Option<Regex>>>
}
impl LazyRegex {
pub fn new(source: &str) -> Result<LazyRegex, Error> {
if let Err(err) = syntax::Parser::new().parse(source) {
return Err(Error::Syntax(err.to_string()));
}
Ok(LazyRegex::from(LazyRegexBuilder::new(source)))
}
fn from(builder: LazyRegexBuilder) -> Self {
LazyRegex {
builder: builder,
regex: Arc::new(OnceMutex::new(None)),
}
}
fn create(builder: &LazyRegexBuilder) -> Regex {
builder.options.define(&mut RegexBuilder::new(&builder.source))
.build().unwrap()
}
}
impl Deref for LazyRegex {
type Target = Regex;
fn deref(&self) -> &Regex {
self.as_ref()
}
}
impl AsRef<Regex> for LazyRegex {
fn as_ref(&self) -> &Regex {
if let Some(mut guard) = self.regex.lock() {
*guard = Some(LazyRegex::create(&self.builder));
}
(*self.regex).as_ref().unwrap()
}
}
impl Into<Regex> for LazyRegex {
fn into(self) -> Regex {
let (regex, builder) = (self.regex, self.builder);
Arc::try_unwrap(regex).ok().and_then(|m| m.into_inner()).unwrap_or_else(||
LazyRegex::create(&builder))
}
}
impl fmt::Debug for LazyRegex {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
impl fmt::Display for LazyRegex {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&**self, f)
}
}
impl str::FromStr for LazyRegex {
type Err = Error;
fn from_str(s: &str) -> Result<LazyRegex, Error> {
LazyRegex::new(s)
}
}
#[derive(Clone, Eq, PartialEq, Debug)]
pub struct LazyRegexBuilder {
source: String,
options: Options,
}
impl LazyRegexBuilder {
pub fn new(source: &str) -> LazyRegexBuilder {
LazyRegexBuilder {
source: source.to_owned(),
options: Default::default(),
}
}
pub fn build(&self) -> Result<LazyRegex, Error> {
if let Err(err) = syntax::Parser::new().parse(&self.source) {
return Err(Error::Syntax(err.to_string()));
}
Ok(LazyRegex::from(self.clone()))
}
pub fn case_insensitive(&mut self, yes: bool) -> &mut LazyRegexBuilder {
self.options.case_insensitive = yes;
self
}
pub fn multi_line(&mut self, yes: bool) -> &mut LazyRegexBuilder {
self.options.multi_line = yes;
self
}
pub fn dot_matches_new_line(&mut self, yes: bool) -> &mut LazyRegexBuilder {
self.options.dot_matches_new_line = yes;
self
}
pub fn swap_greed(&mut self, yes: bool) -> &mut LazyRegexBuilder {
self.options.swap_greed = yes;
self
}
pub fn ignore_whitespace(&mut self, yes: bool) -> &mut LazyRegexBuilder {
self.options.ignore_whitespace = yes;
self
}
pub fn unicode(&mut self, yes: bool) -> &mut LazyRegexBuilder {
self.options.unicode = yes;
self
}
pub fn size_limit(&mut self, limit: usize) -> &mut LazyRegexBuilder {
self.options.size_limit = limit;
self
}
pub fn dfa_size_limit(&mut self, limit: usize) -> &mut LazyRegexBuilder {
self.options.dfa_size_limit = limit;
self
}
}
#[cfg(test)]
mod test {
use ::{LazyRegex, LazyRegexBuilder};
#[test]
fn new() {
assert!(LazyRegex::new(r"^\d+$").unwrap()
.is_match("2345"));
assert!(!LazyRegex::new(r"^[a-z]+$").unwrap()
.is_match("2345"));
}
#[test]
fn build() {
assert!(LazyRegexBuilder::new(r"^abc$")
.case_insensitive(true).build().unwrap()
.is_match("ABC"));
assert!(!LazyRegexBuilder::new(r"^abc$")
.case_insensitive(false).build().unwrap()
.is_match("ABC"));
}
#[test]
fn same() {
let re = LazyRegex::new(r"^\d+$").unwrap();
assert!(re.is_match("1234"));
assert!(re.is_match("1234"));
assert!(re.is_match("1234"));
assert!(re.is_match("1234"));
}
}