mirror of
https://github.com/watchexec/watchexec.git
synced 2024-11-14 00:01:12 +01:00
Get filetype filters actually working
This commit is contained in:
parent
ae6af17aea
commit
14b0364135
2 changed files with 138 additions and 76 deletions
|
@ -1,4 +1,8 @@
|
||||||
use std::{collections::HashSet, env::var, path::PathBuf};
|
use std::{
|
||||||
|
collections::HashSet,
|
||||||
|
env::{self, var},
|
||||||
|
path::PathBuf,
|
||||||
|
};
|
||||||
|
|
||||||
use dunce::canonicalize;
|
use dunce::canonicalize;
|
||||||
use miette::{IntoDiagnostic, Result};
|
use miette::{IntoDiagnostic, Result};
|
||||||
|
@ -116,9 +120,14 @@ async fn main() -> Result<()> {
|
||||||
|
|
||||||
let mut filters = Vec::new();
|
let mut filters = Vec::new();
|
||||||
|
|
||||||
// TODO: move into config?
|
// TODO: move into config
|
||||||
|
let workdir = env::current_dir()
|
||||||
|
.and_then(|wd| wd.canonicalize())
|
||||||
|
.into_diagnostic()?;
|
||||||
for filter in args.values_of("filter").unwrap_or_default() {
|
for filter in args.values_of("filter").unwrap_or_default() {
|
||||||
filters.push(filter.parse()?);
|
let mut filter: Filter = filter.parse()?;
|
||||||
|
filter.in_path = Some(workdir.clone());
|
||||||
|
filters.push(filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
for ext in args
|
for ext in args
|
||||||
|
@ -136,6 +145,8 @@ async fn main() -> Result<()> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debug!(?filters, "parsed filters and extensions");
|
||||||
|
|
||||||
let (init, runtime, filterer) = config::new(&args)?;
|
let (init, runtime, filterer) = config::new(&args)?;
|
||||||
filterer.add_filters(&filters).await?;
|
filterer.add_filters(&filters).await?;
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use dunce::canonicalize;
|
use dunce::canonicalize;
|
||||||
|
use globset::Glob;
|
||||||
use ignore::gitignore::{Gitignore, GitignoreBuilder};
|
use ignore::gitignore::{Gitignore, GitignoreBuilder};
|
||||||
use ignore::Match;
|
use ignore::Match;
|
||||||
use tokio::fs::read_to_string;
|
use tokio::fs::read_to_string;
|
||||||
|
@ -16,7 +17,6 @@ use crate::filter::Filterer;
|
||||||
use crate::ignore_files::IgnoreFile;
|
use crate::ignore_files::IgnoreFile;
|
||||||
|
|
||||||
// to make filters
|
// to make filters
|
||||||
pub use globset::Glob;
|
|
||||||
pub use regex::Regex;
|
pub use regex::Regex;
|
||||||
|
|
||||||
pub mod error;
|
pub mod error;
|
||||||
|
@ -62,85 +62,116 @@ impl TaggedFilterer {
|
||||||
|
|
||||||
trace!(tags=%event.tags.len(), "checking all tags on the event");
|
trace!(tags=%event.tags.len(), "checking all tags on the event");
|
||||||
for tag in &event.tags {
|
for tag in &event.tags {
|
||||||
let filters = self.filters.borrow().get(&tag.into()).cloned();
|
trace!(?tag, "checking tag");
|
||||||
if let Some(tag_filters) = filters {
|
for matcher in Matcher::from_tag(tag) {
|
||||||
trace!(?tag, "checking tag");
|
let filters = self.filters.borrow().get(matcher).cloned();
|
||||||
|
if let Some(tag_filters) = filters {
|
||||||
|
if tag_filters.is_empty() {
|
||||||
|
trace!(?tag, ?matcher, "no filters for this tag, skipping (pass)");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if tag_filters.is_empty() {
|
trace!(?tag, ?matcher, filters=%tag_filters.len(), "found some filters for this tag");
|
||||||
trace!(?tag, "no filters for this tag, skipping (pass)");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
trace!(?tag, filters=%tag_filters.len(), "found some filters for this tag");
|
let mut tag_match = true;
|
||||||
|
|
||||||
let mut tag_match = true;
|
if let (Matcher::Path, Tag::Path { path, file_type }) = (matcher, tag) {
|
||||||
|
let is_dir = file_type.map_or(false, |ft| ft.is_dir());
|
||||||
|
|
||||||
if let Tag::Path { path, file_type } = tag {
|
let gc = self.glob_compiled.borrow();
|
||||||
let is_dir = file_type.map_or(false, |ft| ft.is_dir());
|
if let Some(igs) = gc.as_ref() {
|
||||||
|
trace!(?tag, ?matcher, "checking against compiled Glob filters");
|
||||||
let gc = self.glob_compiled.borrow();
|
match igs.matched(path, is_dir) {
|
||||||
if let Some(igs) = gc.as_ref() {
|
Match::None => {
|
||||||
trace!(?tag, "checking against compiled Glob filters");
|
trace!(?tag, ?matcher, "no match (fail)");
|
||||||
match igs.matched(path, is_dir) {
|
tag_match = false;
|
||||||
Match::None => {
|
}
|
||||||
trace!(?tag, "no match (fail)");
|
Match::Ignore(glob) => {
|
||||||
tag_match = false;
|
trace!(?tag, ?matcher, ?glob, "positive match (pass)");
|
||||||
|
tag_match = true;
|
||||||
|
}
|
||||||
|
Match::Whitelist(glob) => {
|
||||||
|
trace!(?tag, ?matcher, ?glob, "negative match (ignore)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Match::Ignore(glob) => {
|
}
|
||||||
trace!(?tag, ?glob, "positive match (pass)");
|
|
||||||
tag_match = true;
|
let ngc = self.not_glob_compiled.borrow();
|
||||||
}
|
if let Some(ngs) = ngc.as_ref() {
|
||||||
Match::Whitelist(glob) => {
|
trace!(?tag, ?matcher, "checking against compiled NotGlob filters");
|
||||||
trace!(?tag, ?glob, "negative match (ignore)");
|
match ngs.matched(path, is_dir) {
|
||||||
|
Match::None => {
|
||||||
|
trace!(?tag, ?matcher, "no match (pass)");
|
||||||
|
tag_match = true;
|
||||||
|
}
|
||||||
|
Match::Ignore(glob) => {
|
||||||
|
trace!(?tag, ?matcher, ?glob, "positive match (fail)");
|
||||||
|
tag_match = false;
|
||||||
|
}
|
||||||
|
Match::Whitelist(glob) => {
|
||||||
|
trace!(?tag, ?matcher, ?glob, "negative match (pass)");
|
||||||
|
tag_match = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let ngc = self.not_glob_compiled.borrow();
|
// those are handled with the compiled ignore filters above
|
||||||
if let Some(ngs) = ngc.as_ref() {
|
let tag_filters = tag_filters
|
||||||
trace!(?tag, "checking against compiled NotGlob filters");
|
.into_iter()
|
||||||
match ngs.matched(path, is_dir) {
|
.filter(|f| {
|
||||||
Match::None => {
|
!matches!(
|
||||||
trace!(?tag, "no match (pass)");
|
(tag, matcher, f),
|
||||||
tag_match = true;
|
(
|
||||||
}
|
Tag::Path { .. },
|
||||||
Match::Ignore(glob) => {
|
Matcher::Path,
|
||||||
trace!(?tag, ?glob, "positive match (fail)");
|
Filter {
|
||||||
tag_match = false;
|
on: Matcher::Path,
|
||||||
}
|
op: Op::Glob | Op::NotGlob,
|
||||||
Match::Whitelist(glob) => {
|
pat: Pattern::Glob(_),
|
||||||
trace!(?tag, ?glob, "negative match (pass)");
|
..
|
||||||
tag_match = true;
|
}
|
||||||
}
|
)
|
||||||
}
|
)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
if tag_filters.is_empty() {
|
||||||
|
trace!(
|
||||||
|
?tag,
|
||||||
|
?matcher,
|
||||||
|
"no more filters for this tag, skipping (pass)"
|
||||||
|
);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
for filter in &tag_filters {
|
trace!(?tag, ?matcher, filters=%tag_filters.len(), "got some filters to check still");
|
||||||
trace!(?filter, ?tag, "checking filter againt tag");
|
|
||||||
if let Some(app) = self.match_tag(filter, tag)? {
|
for filter in &tag_filters {
|
||||||
if filter.negate {
|
trace!(?filter, ?tag, "checking filter againt tag");
|
||||||
if app {
|
if let Some(app) = self.match_tag(filter, tag)? {
|
||||||
trace!(prev=%tag_match, now=%true, "negate filter passes, resetting tag to pass");
|
if filter.negate {
|
||||||
tag_match = true;
|
if app {
|
||||||
|
trace!(prev=%tag_match, now=%true, "negate filter passes, resetting tag to pass");
|
||||||
|
tag_match = true;
|
||||||
|
} else {
|
||||||
|
trace!(prev=%tag_match, now=%tag_match, "negate filter fails, ignoring");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
trace!(prev=%tag_match, now=%tag_match, "negate filter fails, ignoring");
|
trace!(prev=%tag_match, this=%app, now=%(tag_match&app), "filter applies to this tag");
|
||||||
|
tag_match &= app;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
trace!(prev=%tag_match, this=%app, now=%(tag_match&app), "filter applies to this tag");
|
|
||||||
tag_match &= app;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if !tag_match {
|
if !tag_match {
|
||||||
trace!(?tag, "tag fails check, failing entire event");
|
trace!(?tag, ?matcher, "tag fails check, failing entire event");
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
trace!(?tag, "tag passes check, continuing");
|
trace!(?tag, ?matcher, "tag passes check, continuing");
|
||||||
} else {
|
} else {
|
||||||
trace!(?tag, "no filters for this tag, skipping (pass)");
|
trace!(?tag, ?matcher, "no filters for this tag, skipping (pass)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -379,8 +410,25 @@ impl Filter {
|
||||||
(Op::InSet, Pattern::Exact(pat)) => subject == pat,
|
(Op::InSet, Pattern::Exact(pat)) => subject == pat,
|
||||||
(Op::NotInSet, Pattern::Set(set)) => !set.contains(subject),
|
(Op::NotInSet, Pattern::Set(set)) => !set.contains(subject),
|
||||||
(Op::NotInSet, Pattern::Exact(pat)) => subject != pat,
|
(Op::NotInSet, Pattern::Exact(pat)) => subject != pat,
|
||||||
(Op::Glob | Op::NotGlob, Pattern::Glob(_)) => {
|
(op @ Op::Glob | op @ Op::NotGlob, Pattern::Glob(glob)) => {
|
||||||
todo!("glob matching for non paths???")
|
// FIXME: someway that isn't this horrible
|
||||||
|
match Glob::new(glob) {
|
||||||
|
Ok(glob) => {
|
||||||
|
let matches = glob.compile_matcher().is_match(subject);
|
||||||
|
match op {
|
||||||
|
Op::Glob => matches,
|
||||||
|
Op::NotGlob => !matches,
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
warn!(
|
||||||
|
"failed to compile glob for non-path match, skipping (pass): {}",
|
||||||
|
err
|
||||||
|
);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
(op, pat) => {
|
(op, pat) => {
|
||||||
warn!(
|
warn!(
|
||||||
|
@ -427,15 +475,18 @@ pub enum Matcher {
|
||||||
ProcessCompletion,
|
ProcessCompletion,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&Tag> for Matcher {
|
impl Matcher {
|
||||||
fn from(tag: &Tag) -> Self {
|
fn from_tag(tag: &Tag) -> &'static [Self] {
|
||||||
match tag {
|
match tag {
|
||||||
Tag::Path { .. } => Matcher::Path,
|
Tag::Path {
|
||||||
Tag::FileEventKind(_) => Matcher::FileEventKind,
|
file_type: None, ..
|
||||||
Tag::Source(_) => Matcher::Source,
|
} => &[Matcher::Path],
|
||||||
Tag::Process(_) => Matcher::Process,
|
Tag::Path { .. } => &[Matcher::Path, Matcher::FileType],
|
||||||
Tag::Signal(_) => Matcher::Signal,
|
Tag::FileEventKind(_) => &[Matcher::FileEventKind],
|
||||||
Tag::ProcessCompletion(_) => Matcher::ProcessCompletion,
|
Tag::Source(_) => &[Matcher::Source],
|
||||||
|
Tag::Process(_) => &[Matcher::Process],
|
||||||
|
Tag::Signal(_) => &[Matcher::Signal],
|
||||||
|
Tag::ProcessCompletion(_) => &[Matcher::ProcessCompletion],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue