#[derive(Debug)]
pub struct WildMatch {
pattern: Vec<char>,
match_min_len: usize,
}
impl WildMatch {
pub fn new(pattern: &str) -> WildMatch {
let mut simplified: Vec<char> = Vec::new();
let mut prev_was_star = false;
let mut match_min_len = 0;
for i in pattern.chars() {
match i {
'*' => {
if !prev_was_star {
simplified.push(i);
}
prev_was_star = true;
}
_ => {
simplified.push(i);
prev_was_star = false;
match_min_len += 1;
}
}
}
WildMatch {
pattern: simplified,
match_min_len: match_min_len,
}
}
pub fn is_match(&self, input: &str) -> bool {
if self.pattern.len() == 1 && self.pattern[0] == '*' {
return true;
}
let mut pattern_idx = 0;
let mut pattern_len = 0;
let mut wildcard = false;
for input_char in input.chars() {
match self.pattern.get(pattern_idx) {
None if pattern_len >= self.match_min_len => {
return wildcard;
}
Some(c) if c == &input_char || c == &'?' => {
pattern_idx += 1;
pattern_len += 1;
if wildcard {
wildcard = false;
}
}
Some(_) if wildcard => {
continue;
}
Some(c) if c == &'*' => {
wildcard = true;
pattern_idx += 1;
if self.pattern.get(pattern_idx) == Some(&input_char)
|| self.pattern.get(pattern_idx) == Some(&'?')
{
pattern_idx += 1;
pattern_len += 1;
if wildcard {
wildcard = false;
}
}
}
_ => {
pattern_idx = 0;
pattern_len = 0;
}
}
}
let current = self.pattern.get(pattern_idx);
return (current.is_none() || current == Some(&'*')) && pattern_len >= self.match_min_len;
}
}
#[cfg(test)]
mod tests {
use super::*;
use ntest::test_case;
#[test_case("**", test_name = "star_star")]
#[test_case("*", test_name = "star")]
#[test_case("*?*", test_name = "star_q_star")]
#[test_case("c*", test_name = "c_star")]
#[test_case("c?*", test_name = "c_q_star")]
#[test_case("???", test_name = "qqq")]
#[test_case("c?t", test_name = "c_q_t")]
#[test_case("cat", test_name = "cat")]
#[test_case("*cat", test_name = "star_cat")]
#[test_case("cat*", test_name = "cat_star")]
fn is_match(pattern: &str) {
let m = WildMatch::new(pattern);
assert!(m.is_match("cat"));
}
#[test_case("*d*", test_name = "star_d_star")]
#[test_case("*d", test_name = "star_d")]
#[test_case("d*", test_name = "d_star")]
#[test_case("*c", test_name = "star_c")]
#[test_case("?", test_name = "q")]
#[test_case("??", test_name = "q2")]
#[test_case("????", test_name = "q4")]
#[test_case("?????", test_name = "q5")]
#[test_case("*????", test_name = "wild_q_four")]
#[test_case("cats", test_name = "longer")]
#[test_case("cat?", test_name = "longer_q")]
#[test_case("cacat", test_name = "cacat")]
#[test_case("cat*dog", test_name = "cat_star_dog")]
fn no_match(pattern: &str) {
let m = WildMatch::new(pattern);
assert!(!m.is_match("cat"));
}
#[test]
fn longer_string_match() {
let m = WildMatch::new("*cat*");
assert!(m.is_match("d&(*og_cat_dog"));
}
}