use std::collections::HashMap;
use std::sync::{LazyLock, Mutex, PoisonError};
use fancy_regex::Regex;
pub type Captures<'t> = fancy_regex::Captures<'t, str>;
static CACHE: LazyLock<Mutex<HashMap<String, &'static Regex>>> =
LazyLock::new(|| Mutex::new(HashMap::new()));
pub fn rx(pattern: &str) -> &'static Regex {
let mut cache = CACHE.lock().unwrap_or_else(PoisonError::into_inner);
if let Some(found) = cache.get(pattern) {
return found;
}
let compiled = Regex::new(pattern).unwrap_or_else(|e| {
panic!("cpplint: regular expression {pattern:?} does not compile: {e}")
});
let leaked: &'static Regex = Box::leak(Box::new(compiled));
cache.insert(pattern.to_string(), leaked);
leaked
}
fn run<'t>(re: &Regex, text: &'t str) -> Option<Captures<'t>> {
re.captures(text).unwrap_or_else(|e| {
panic!(
"cpplint: regular expression {:?} failed on {text:?}: {e}",
re.as_str()
)
})
}
pub fn search<'t>(pattern: &str, text: &'t str) -> Option<Captures<'t>> {
run(rx(pattern), text)
}
pub fn pmatch<'t>(pattern: &str, text: &'t str) -> Option<Captures<'t>> {
run(rx(&format!("^(?:{pattern})")), text)
}
pub fn is_search(pattern: &str, text: &str) -> bool {
search(pattern, text).is_some()
}
pub fn is_match(pattern: &str, text: &str) -> bool {
pmatch(pattern, text).is_some()
}
pub fn g<'t>(caps: &Captures<'t>, i: usize) -> &'t str {
caps.get(i).map_or("", |m| m.as_str())
}
pub fn group<'t>(caps: &Captures<'t>, i: usize) -> Option<&'t str> {
caps.get(i).map(|m| m.as_str())
}
pub fn start(caps: &Captures<'_>, i: usize) -> usize {
caps.get(i).map_or(0, |m| m.start())
}
pub fn end(caps: &Captures<'_>, i: usize) -> usize {
caps.get(i).map_or(0, |m| m.end())
}
pub fn sub(pattern: &str, replacement: &str, text: &str) -> String {
rx(pattern).replace_all(text, replacement).into_owned()
}
pub fn split<'t>(pattern: &str, text: &'t str) -> Vec<&'t str> {
let re = rx(pattern);
let mut parts = Vec::new();
let mut last = 0;
for found in re.find_iter(text) {
let m = found.unwrap_or_else(|e| {
panic!("cpplint: regular expression {pattern:?} failed on {text:?}: {e}")
});
parts.push(&text[last..m.start()]);
last = m.end();
}
parts.push(&text[last..]);
parts
}
pub fn find_all<'t>(pattern: &str, text: &'t str) -> Vec<Captures<'t>> {
rx(pattern)
.captures_iter(text)
.map(|c| {
c.unwrap_or_else(|e| {
panic!("cpplint: regular expression {pattern:?} failed on {text:?}: {e}")
})
})
.collect()
}
pub fn escape(text: &str) -> String {
regex::escape(text)
}