Match path globs

This commit is contained in:
Félix Saparelli 2021-10-13 04:06:39 +13:00
parent 758ac2dc89
commit fb4f136c0d
No known key found for this signature in database
GPG Key ID: B948C4BAE44FC474
3 changed files with 81 additions and 9 deletions

View File

@ -9,6 +9,7 @@
use std::{
collections::HashMap,
fmt,
fs::FileType,
path::{Path, PathBuf},
process::ExitStatus,
};
@ -28,7 +29,10 @@ pub struct Event {
#[derive(Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum Tag {
Path(PathBuf),
Path {
path: PathBuf,
file_type: Option<FileType>,
},
FileEventKind(EventKind),
Source(Source),
Process(u32),
@ -39,7 +43,7 @@ pub enum Tag {
impl Tag {
pub const fn discriminant_name(&self) -> &'static str {
match self {
Tag::Path(_) => "Path",
Tag::Path { .. } => "Path",
Tag::FileEventKind(_) => "FileEventKind",
Tag::Source(_) => "Source",
Tag::Process(_) => "Process",
@ -94,7 +98,7 @@ impl Event {
/// Return all paths in the event's tags.
pub fn paths(&self) -> impl Iterator<Item = &Path> {
self.tags.iter().filter_map(|p| match p {
Tag::Path(p) => Some(p.as_path()),
Tag::Path { path, .. } => Some(path.as_path()),
_ => None,
})
}
@ -121,7 +125,24 @@ impl fmt::Display for Event {
write!(f, "Event")?;
for p in &self.tags {
match p {
Tag::Path(p) => write!(f, " path={}", p.display())?,
Tag::Path { path, file_type } => {
write!(f, " path={}", path.display())?;
if let Some(ft) = file_type {
write!(
f,
" filetype={}",
if ft.is_file() {
"file"
} else if ft.is_dir() {
"dir"
} else if ft.is_symlink() {
"symlink"
} else {
"special"
}
)?;
}
}
Tag::FileEventKind(kind) => write!(f, " kind={:?}", kind)?,
Tag::Source(s) => write!(f, " source={:?}", s)?,
Tag::Process(p) => write!(f, " process={}", p)?,

View File

@ -4,6 +4,7 @@ use std::sync::Arc;
use dunce::canonicalize;
use ignore::gitignore::{Gitignore, GitignoreBuilder};
use ignore::Match;
use tokio::fs::read_to_string;
use tracing::{debug, trace, warn};
use unicase::UniCase;
@ -52,7 +53,8 @@ impl Filterer for TaggedFilterer {
impl TaggedFilterer {
fn check(&self, event: &Event) -> Result<bool, TaggedFiltererError> {
// TODO: trace logging
// TODO: tracing with spans
if self.filters.borrow().is_empty() {
trace!("no filters, skipping entire check (pass)");
return Ok(true);
@ -72,6 +74,48 @@ impl TaggedFilterer {
trace!(?tag, filters=%tag_filters.len(), "found some filters for this tag");
let mut tag_match = true;
if let Tag::Path { path, file_type } = tag {
let is_dir = file_type.map_or(false, |ft| ft.is_dir());
let gc = self.glob_compiled.borrow();
if let Some(igs) = gc.as_ref() {
trace!(?tag, "checking against compiled Glob filters");
match igs.matched(path, is_dir) {
Match::None => {
trace!(?tag, "no match (fail)");
tag_match = false;
}
Match::Ignore(glob) => {
trace!(?tag, ?glob, "positive match (pass)");
tag_match = true;
}
Match::Whitelist(glob) => {
trace!(?tag, ?glob, "negative match (ignore)");
}
}
}
let ngc = self.not_glob_compiled.borrow();
if let Some(ngs) = ngc.as_ref() {
trace!(?tag, "checking against compiled NotGlob filters");
match ngs.matched(path, is_dir) {
Match::None => {
trace!(?tag, "no match (pass)");
tag_match = true;
}
Match::Ignore(glob) => {
trace!(?tag, ?glob, "positive match (fail)");
tag_match = false;
}
Match::Whitelist(glob) => {
trace!(?tag, ?glob, "negative match (pass)");
tag_match = true;
}
}
}
}
for filter in &tag_filters {
trace!(?filter, ?tag, "checking filter againt tag");
if let Some(app) = self.match_tag(filter, tag)? {
@ -134,7 +178,7 @@ impl TaggedFilterer {
trace!(?tag, matcher=?filter.on, "matching filter to tag");
match (tag, filter.on) {
(tag, Matcher::Tag) => filter.matches(tag.discriminant_name()),
(Tag::Path(path), Matcher::Path) => {
(Tag::Path { path, .. }, Matcher::Path) => {
let resolved = if let Some(ctx) = &filter.in_path {
if let Ok(suffix) = path.strip_prefix(ctx) {
suffix.strip_prefix("/").unwrap_or(suffix)
@ -152,7 +196,9 @@ impl TaggedFilterer {
trace!(?resolved, "resolved path to match filter against");
if matches!(filter.op, Op::Glob | Op::NotGlob) {
todo!("glob match using compiled ignores");
unreachable!(
"path glob match with match_tag is too late; should be handled above"
);
} else {
filter.matches(resolved.to_string_lossy())
}
@ -369,7 +415,7 @@ pub enum Matcher {
impl From<&Tag> for Matcher {
fn from(tag: &Tag) -> Self {
match tag {
Tag::Path(_) => Matcher::Path,
Tag::Path { .. } => Matcher::Path,
Tag::FileEventKind(_) => Matcher::FileEventKind,
Tag::Source(_) => Matcher::Source,
Tag::Process(_) => Matcher::Process,

View File

@ -2,6 +2,7 @@
use std::{
collections::{HashMap, HashSet},
fs::metadata,
mem::take,
path::PathBuf,
sync::{Arc, Mutex},
@ -236,7 +237,11 @@ fn process_event(
tags.push(Tag::FileEventKind(nev.kind));
for path in nev.paths {
tags.push(Tag::Path(dunce::canonicalize(path)?));
// possibly pull file_type from whatever notify (or the native driver) returns?
tags.push(Tag::Path {
file_type: metadata(&path).ok().map(|m| m.file_type()),
path: dunce::canonicalize(path)?,
});
}
if let Some(pid) = nev.attrs.process_id() {