use regex::Regex;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PatternDialect {
Literal,
Glob,
Regex,
}
impl PatternDialect {
pub fn as_str(self) -> &'static str {
match self {
PatternDialect::Literal => "literal",
PatternDialect::Glob => "glob",
PatternDialect::Regex => "regex",
}
}
}
const PATTERN_REGEX_METACHARS: &[char] = &[
'.', '+', '?', '(', ')', '|', '[', ']', '{', '}', '^', '$', '\\',
];
pub fn promote_pattern(source: &str) -> (String, PatternDialect) {
let has_regex_metachar = source.chars().any(|c| PATTERN_REGEX_METACHARS.contains(&c));
let has_glob_star = source.contains('*');
if has_regex_metachar {
(format!("^(?:{source})$"), PatternDialect::Regex)
} else if has_glob_star {
let pattern = regex::escape(source).replace("\\*", ".*");
(format!("^{pattern}$"), PatternDialect::Glob)
} else {
(
format!("^{}$", regex::escape(source)),
PatternDialect::Literal,
)
}
}
pub fn compile_pattern(source: &str) -> Result<(Regex, PatternDialect), String> {
let (anchored, dialect) = promote_pattern(source);
let re = Regex::new(&anchored).map_err(|e| {
format!(
"pattern '{source}' did not compile (dialect={}): {e}",
dialect.as_str()
)
})?;
Ok((re, dialect))
}