use std::path::Path;
#[derive(Debug, Clone)]
pub struct QuillIgnore {
pub(crate) patterns: Vec<String>,
}
impl QuillIgnore {
pub fn new(patterns: Vec<String>) -> Self {
Self { patterns }
}
pub fn from_content(content: &str) -> Self {
let patterns = content
.lines()
.map(|line| line.trim())
.filter(|line| !line.is_empty() && !line.starts_with('#'))
.map(|line| line.to_string())
.collect();
Self::new(patterns)
}
pub fn is_ignored<P: AsRef<Path>>(&self, path: P) -> bool {
let path = path.as_ref();
let path_str = path.to_string_lossy();
for pattern in &self.patterns {
if self.matches_pattern(pattern, &path_str) {
return true;
}
}
false
}
fn matches_pattern(&self, pattern: &str, path: &str) -> bool {
if let Some(pattern_prefix) = pattern.strip_suffix('/') {
return path.starts_with(pattern_prefix)
&& (path.len() == pattern_prefix.len()
|| path.chars().nth(pattern_prefix.len()) == Some('/'));
}
if !pattern.contains('*') {
return path == pattern || path.ends_with(&format!("/{}", pattern));
}
if pattern == "*" {
return true;
}
let pattern_parts: Vec<&str> = pattern.split('*').collect();
if pattern_parts.len() == 2 {
let (prefix, suffix) = (pattern_parts[0], pattern_parts[1]);
if prefix.is_empty() {
return path.ends_with(suffix);
} else if suffix.is_empty() {
return path.starts_with(prefix);
} else {
return path.starts_with(prefix) && path.ends_with(suffix);
}
}
false
}
}