#[derive(Debug, Clone)]
pub struct ModulePattern {
pub original: String,
pub needle: String,
}
impl ModulePattern {
pub fn parse(raw: &str) -> Option<Self> {
let trimmed = raw.trim();
if trimmed.is_empty() {
return None;
}
Some(Self {
original: trimmed.to_string(),
needle: trimmed.to_lowercase(),
})
}
pub fn matches_name(&self, module_name_lc: &str) -> bool {
module_name_lc == self.needle || module_name_lc.contains(&self.needle)
}
pub fn matches_path(&self, module_path_lc: &str) -> bool {
module_path_lc.ends_with(&self.needle) || module_path_lc.contains(&self.needle)
}
}
#[derive(Debug, Clone, Default)]
pub struct ModuleMatcher {
patterns: Vec<ModulePattern>,
}
impl ModuleMatcher {
pub fn from_raw<I, S>(raw: I) -> Self
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
let patterns = raw
.into_iter()
.filter_map(|s| ModulePattern::parse(s.as_ref()))
.collect();
Self { patterns }
}
pub fn is_empty(&self) -> bool {
self.patterns.is_empty()
}
pub fn first_match<'a>(
&'a self,
module_name_lc: &str,
module_path_lc: &str,
) -> Option<&'a ModulePattern> {
self.patterns
.iter()
.find(|p| p.matches_name(module_name_lc) || p.matches_path(module_path_lc))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_input_yields_empty_matcher() {
let m = ModuleMatcher::from_raw(["", " "]);
assert!(m.is_empty());
}
#[test]
fn matches_exact_name_case_insensitive() {
let m = ModuleMatcher::from_raw(["NTDLL.DLL"]);
assert!(m
.first_match("ntdll.dll", "c:/windows/system32/ntdll.dll")
.is_some());
}
#[test]
fn matches_path_suffix() {
let m = ModuleMatcher::from_raw(["system32/ntdll.dll"]);
assert!(m
.first_match("ntdll.dll", "c:/windows/system32/ntdll.dll")
.is_some());
}
#[test]
fn returns_first_matching_pattern() {
let m = ModuleMatcher::from_raw(["ntdll", "kernelbase"]);
let hit = m
.first_match("ntdll.dll", "c:/windows/system32/ntdll.dll")
.unwrap();
assert_eq!(hit.original, "ntdll");
}
#[test]
fn no_match_returns_none() {
let m = ModuleMatcher::from_raw(["foobar"]);
assert!(m.first_match("ntdll.dll", "c:/x/ntdll.dll").is_none());
}
}