use std::ops::{Deref, DerefMut};
use std::sync::{Mutex, Arc};
use std::borrow::Cow;
use std::fmt;
use std::str;
use regex::{Regex, RegexBuilder, Error};
use regex::{Match, Captures, Replacer};
use syntax;
use options::Options;
use lru::LruCache;
#[derive(Clone, Debug)]
pub struct RegexCache(LruCache<String, Regex>);
impl RegexCache {
pub fn new(capacity: usize) -> RegexCache {
RegexCache(LruCache::new(capacity))
}
pub fn save(&mut self, re: Regex) -> &Regex {
let source = re.as_str().to_owned();
if !self.0.contains_key(re.as_str()) {
self.insert(source.clone(), re);
}
self.0.get_mut(&source).unwrap()
}
pub fn compile(&mut self, source: &str) -> Result<&Regex, Error> {
if !self.0.contains_key(source) {
self.0.insert(source.into(), Regex::new(source)?);
}
Ok(self.0.get_mut(source).unwrap())
}
pub fn configure<F>(&mut self, source: &str, f: F) -> Result<&Regex, Error>
where F: FnOnce(&mut RegexBuilder) -> &mut RegexBuilder
{
if !self.0.contains_key(source) {
self.0.insert(source.into(), f(&mut RegexBuilder::new(source)).build()?);
}
Ok(self.0.get_mut(source).unwrap())
}
}
impl Deref for RegexCache {
type Target = LruCache<String, Regex>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for RegexCache {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
#[derive(Clone)]
pub struct CachedRegex {
builder: CachedRegexBuilder,
}
macro_rules! regex {
($self:ident) => (
$self.builder.cache.lock().unwrap().configure(&$self.builder.source, |b|
$self.builder.options.define(b)).unwrap()
)
}
impl CachedRegex {
pub fn new(cache: Arc<Mutex<RegexCache>>, source: &str) -> Result<CachedRegex, Error> {
if let Err(err) = syntax::Parser::new().parse(source) {
return Err(Error::Syntax(err.to_string()));
}
Ok(CachedRegex::from(CachedRegexBuilder::new(cache, source)))
}
fn from(builder: CachedRegexBuilder) -> Self {
CachedRegex {
builder: builder,
}
}
pub fn is_match(&self, text: &str) -> bool {
regex!(self).is_match(text)
}
pub fn find<'t>(&self, text: &'t str) -> Option<Match<'t>> {
regex!(self).find(text)
}
pub fn captures<'t>(&self, text: &'t str) -> Option<Captures<'t>> {
regex!(self).captures(text)
}
pub fn replace<'t, R: Replacer>(&self, text: &'t str, rep: R) -> Cow<'t, str> {
regex!(self).replace(text, rep)
}
pub fn replace_all<'t, R: Replacer>(&self, text: &'t str, rep: R) -> Cow<'t, str> {
regex!(self).replace_all(text, rep)
}
pub fn shortest_match(&self, text: &str) -> Option<usize> {
regex!(self).shortest_match(text)
}
pub fn captures_len(&self) -> usize {
regex!(self).captures_len()
}
pub fn as_str(&self) -> &str {
&self.builder.source
}
}
impl fmt::Debug for CachedRegex {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(regex!(self), f)
}
}
impl fmt::Display for CachedRegex {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(regex!(self), f)
}
}
#[derive(Clone, Debug)]
pub struct CachedRegexBuilder {
cache: Arc<Mutex<RegexCache>>,
source: String,
options: Options,
}
impl CachedRegexBuilder {
pub fn new(cache: Arc<Mutex<RegexCache>>, source: &str) -> CachedRegexBuilder {
CachedRegexBuilder {
cache: cache,
source: source.to_owned(),
options: Default::default(),
}
}
pub fn build(&self) -> Result<CachedRegex, Error> {
if let Err(err) = syntax::Parser::new().parse(&self.source) {
return Err(Error::Syntax(err.to_string()));
}
Ok(CachedRegex::from(self.clone()))
}
pub fn case_insensitive(&mut self, yes: bool) -> &mut CachedRegexBuilder {
self.options.case_insensitive = yes;
self
}
pub fn multi_line(&mut self, yes: bool) -> &mut CachedRegexBuilder {
self.options.multi_line = yes;
self
}
pub fn dot_matches_new_line(&mut self, yes: bool) -> &mut CachedRegexBuilder {
self.options.dot_matches_new_line = yes;
self
}
pub fn swap_greed(&mut self, yes: bool) -> &mut CachedRegexBuilder {
self.options.swap_greed = yes;
self
}
pub fn ignore_whitespace(&mut self, yes: bool) -> &mut CachedRegexBuilder {
self.options.ignore_whitespace = yes;
self
}
pub fn unicode(&mut self, yes: bool) -> &mut CachedRegexBuilder {
self.options.unicode = yes;
self
}
pub fn size_limit(&mut self, limit: usize) -> &mut CachedRegexBuilder {
self.options.size_limit = limit;
self
}
pub fn dfa_size_limit(&mut self, limit: usize) -> &mut CachedRegexBuilder {
self.options.dfa_size_limit = limit;
self
}
}
#[cfg(test)]
mod test {
use std::sync::{Arc, Mutex};
use cache::{RegexCache, CachedRegex};
#[test]
fn respects_limit() {
let mut cache = RegexCache::new(2);
cache.compile("[01]2").unwrap();
cache.compile("[21]0").unwrap();
assert_eq!(cache.len(), 2);
cache.compile("[21]3").unwrap();
assert_eq!(cache.len(), 2);
}
#[test]
fn cached_regex() {
let cache = Arc::new(Mutex::new(RegexCache::new(100)));
let re = CachedRegex::new(cache.clone(), r"^\d+$").unwrap();
assert!(re.is_match("123"));
assert!(!re.is_match("abc"));
}
}