use std::cell::RefCell;
use std::ops::Deref;
use std::rc::Rc;
use cached::Cached;
use cached::SizedCache;
use regex::{Error, Regex};
type OptionalCache = Option<Rc<RefCell<SizedCache<String, Rc<Result<Regex, Error>>>>>>;
#[derive(Clone)]
pub struct RegexCache {
cache: OptionalCache,
}
pub trait RegexCompile {
fn compile_regex(&self, regex: &str) -> Rc<Result<Regex, Error>>;
}
impl RegexCompile for RegexCache {
fn compile_regex(&self, regex: &str) -> Rc<Result<Regex, Error>> {
match &self.cache {
None => Rc::new(Regex::new(regex)),
Some(cache) => cache
.deref()
.borrow_mut()
.cache_get_or_set_with(regex.into(), || Rc::new(Regex::new(regex)))
.clone(),
}
}
}
impl RegexCache {
pub fn new(size: usize) -> Self {
let cache = if size > 0 {
Some(Rc::new(RefCell::new(SizedCache::with_size(size))))
} else {
None
};
RegexCache { cache }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn can_compile_with_no_cache() -> Result<(), Error> {
let cache = RegexCache::new(0);
match &*cache.compile_regex("[a-z]+") {
Ok(regex) => {
assert!(regex.is_match("hello"));
assert!(!regex.is_match("1234"));
}
Err(e) => {
return Err(e.clone());
}
}
Ok(())
}
#[test]
fn can_recompile_with_cache() -> Result<(), Error> {
let cache = RegexCache::new(10);
match &*cache.compile_regex("[a-z]+") {
Ok(regex) => {
assert!(regex.is_match("hello"));
assert!(!regex.is_match("1234"));
}
Err(e) => {
return Err(e.clone());
}
}
match &*cache.compile_regex("[a-z]+") {
Ok(regex) => {
assert!(regex.is_match("hello"));
assert!(!regex.is_match("1234"));
}
Err(e) => {
return Err(e.clone());
}
}
match &*cache.compile_regex("[a-z]+") {
Ok(regex) => {
assert!(regex.is_match("hello"));
assert!(!regex.is_match("1234"));
}
Err(e) => {
return Err(e.clone());
}
}
match &*cache.compile_regex("[a-z]+") {
Ok(regex) => {
assert!(regex.is_match("hello"));
assert!(!regex.is_match("1234"));
}
Err(e) => {
return Err(e.clone());
}
}
Ok(())
}
}