mirror of
https://github.com/watchexec/watchexec.git
synced 2024-09-28 22:21:33 +02:00
Integrate IgnoreFilterer into TaggedFilterer
This commit is contained in:
parent
8219e577d0
commit
d6dfb87063
@ -8,7 +8,6 @@ use dunce::canonicalize;
|
|||||||
use globset::Glob;
|
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 tracing::{debug, trace, trace_span, warn};
|
use tracing::{debug, trace, trace_span, warn};
|
||||||
use unicase::UniCase;
|
use unicase::UniCase;
|
||||||
|
|
||||||
@ -16,7 +15,7 @@ use crate::error::RuntimeError;
|
|||||||
use crate::event::{Event, FileType, ProcessEnd, Tag};
|
use crate::event::{Event, FileType, ProcessEnd, Tag};
|
||||||
use crate::filter::tagged::error::TaggedFiltererError;
|
use crate::filter::tagged::error::TaggedFiltererError;
|
||||||
use crate::filter::Filterer;
|
use crate::filter::Filterer;
|
||||||
use crate::ignore::IgnoreFile;
|
use crate::ignore::{IgnoreFile, IgnoreFilterer};
|
||||||
use crate::signal::process::SubSignal;
|
use crate::signal::process::SubSignal;
|
||||||
use crate::signal::source::MainSignal;
|
use crate::signal::source::MainSignal;
|
||||||
|
|
||||||
@ -116,6 +115,9 @@ pub struct TaggedFilterer {
|
|||||||
/// All filters that are applied, in order, by matcher.
|
/// All filters that are applied, in order, by matcher.
|
||||||
filters: swaplock::SwapLock<HashMap<Matcher, Vec<Filter>>>,
|
filters: swaplock::SwapLock<HashMap<Matcher, Vec<Filter>>>,
|
||||||
|
|
||||||
|
/// Sub-filterer for ignore files.
|
||||||
|
ignore_filterer: swaplock::SwapLock<IgnoreFilterer>,
|
||||||
|
|
||||||
/// Compiled matcher for Glob filters.
|
/// Compiled matcher for Glob filters.
|
||||||
glob_compiled: swaplock::SwapLock<Option<Gitignore>>,
|
glob_compiled: swaplock::SwapLock<Option<Gitignore>>,
|
||||||
|
|
||||||
@ -134,6 +136,15 @@ impl TaggedFilterer {
|
|||||||
let _span = trace_span!("filterer_check").entered();
|
let _span = trace_span!("filterer_check").entered();
|
||||||
trace!(?event, "checking event");
|
trace!(?event, "checking event");
|
||||||
|
|
||||||
|
{
|
||||||
|
trace!("checking internal ignore filterer");
|
||||||
|
let igf = self.ignore_filterer.borrow();
|
||||||
|
if !igf.check_event(event).expect("IgnoreFilterer never errors") {
|
||||||
|
trace!("internal ignore filterer matched (fail)");
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if self.filters.borrow().is_empty() {
|
if self.filters.borrow().is_empty() {
|
||||||
trace!("no filters, skipping entire check (pass)");
|
trace!("no filters, skipping entire check (pass)");
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
@ -154,8 +165,6 @@ impl TaggedFilterer {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: integrate ignore::Filter
|
|
||||||
|
|
||||||
trace!(filters=%tag_filters.len(), "found some filters for this matcher");
|
trace!(filters=%tag_filters.len(), "found some filters for this matcher");
|
||||||
|
|
||||||
let mut tag_match = true;
|
let mut tag_match = true;
|
||||||
@ -309,7 +318,7 @@ impl TaggedFilterer {
|
|||||||
///
|
///
|
||||||
/// The origin is the directory the main project that is being watched is in. This is used to
|
/// The origin is the directory the main project that is being watched is in. This is used to
|
||||||
/// resolve absolute paths given in filters without an `in_path` field (e.g. all filters parsed
|
/// resolve absolute paths given in filters without an `in_path` field (e.g. all filters parsed
|
||||||
/// from text).
|
/// from text), and for ignore file based filtering.
|
||||||
///
|
///
|
||||||
/// The workdir is used to resolve relative paths given in filters without an `in_path` field.
|
/// The workdir is used to resolve relative paths given in filters without an `in_path` field.
|
||||||
///
|
///
|
||||||
@ -320,12 +329,14 @@ impl TaggedFilterer {
|
|||||||
origin: impl Into<PathBuf>,
|
origin: impl Into<PathBuf>,
|
||||||
workdir: impl Into<PathBuf>,
|
workdir: impl Into<PathBuf>,
|
||||||
) -> Result<Arc<Self>, TaggedFiltererError> {
|
) -> Result<Arc<Self>, TaggedFiltererError> {
|
||||||
|
let origin = canonicalize(origin.into())?;
|
||||||
Ok(Arc::new(Self {
|
Ok(Arc::new(Self {
|
||||||
origin: canonicalize(origin.into())?,
|
|
||||||
workdir: canonicalize(workdir.into())?,
|
|
||||||
filters: swaplock::SwapLock::new(HashMap::new()),
|
filters: swaplock::SwapLock::new(HashMap::new()),
|
||||||
|
ignore_filterer: swaplock::SwapLock::new(IgnoreFilterer::empty(&origin)),
|
||||||
glob_compiled: swaplock::SwapLock::new(None),
|
glob_compiled: swaplock::SwapLock::new(None),
|
||||||
not_glob_compiled: swaplock::SwapLock::new(None),
|
not_glob_compiled: swaplock::SwapLock::new(None),
|
||||||
|
workdir: canonicalize(workdir.into())?,
|
||||||
|
origin,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -431,7 +442,8 @@ impl TaggedFilterer {
|
|||||||
/// with a single write, without needing to acquire the lock repeatedly.
|
/// with a single write, without needing to acquire the lock repeatedly.
|
||||||
///
|
///
|
||||||
/// If filters with glob operations are added, the filterer's glob matchers are recompiled after
|
/// If filters with glob operations are added, the filterer's glob matchers are recompiled after
|
||||||
/// the new filters are added, in this method.
|
/// the new filters are added, in this method. This should not be used for inserting an
|
||||||
|
/// [`IgnoreFile`]: use [`add_ignore_file()`](Self::add_ignore_file) instead.
|
||||||
pub async fn add_filters(&self, filters: &[Filter]) -> Result<(), TaggedFiltererError> {
|
pub async fn add_filters(&self, filters: &[Filter]) -> Result<(), TaggedFiltererError> {
|
||||||
debug!(?filters, "adding filters to filterer");
|
debug!(?filters, "adding filters to filterer");
|
||||||
|
|
||||||
@ -526,26 +538,18 @@ impl TaggedFilterer {
|
|||||||
.map_err(TaggedFiltererError::GlobsetChange)
|
.map_err(TaggedFiltererError::GlobsetChange)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reads a gitignore-style [`IgnoreFile`] and adds all of its contents to the filterer.
|
/// Reads a gitignore-style [`IgnoreFile`] and adds it to the filterer.
|
||||||
///
|
|
||||||
/// Empty lines and lines starting with `#` are ignored. The `applies_in` field of the
|
|
||||||
/// [`IgnoreFile`] is used for the `in_path` field of each [`Filter`].
|
|
||||||
///
|
|
||||||
/// This method reads the entire file into memory.
|
|
||||||
pub async fn add_ignore_file(&self, file: &IgnoreFile) -> Result<(), TaggedFiltererError> {
|
pub async fn add_ignore_file(&self, file: &IgnoreFile) -> Result<(), TaggedFiltererError> {
|
||||||
let content = read_to_string(&file.path).await?;
|
let mut new = { self.ignore_filterer.borrow().clone() };
|
||||||
let lines = content.lines();
|
|
||||||
let mut ignores = Vec::with_capacity(lines.size_hint().0);
|
|
||||||
|
|
||||||
for line in lines {
|
new.add_file(file)
|
||||||
if line.is_empty() || line.starts_with('#') {
|
.await
|
||||||
continue;
|
.map_err(TaggedFiltererError::Ignore)?;
|
||||||
}
|
self.ignore_filterer
|
||||||
|
.replace(new)
|
||||||
ignores.push(Filter::from_glob_ignore(file.applies_in.clone(), line));
|
.await
|
||||||
}
|
.map_err(TaggedFiltererError::IgnoreSwap)?;
|
||||||
|
Ok(())
|
||||||
self.add_filters(&ignores).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clears all filters from the filterer.
|
/// Clears all filters from the filterer.
|
||||||
|
@ -10,6 +10,7 @@ use tokio::sync::watch::error::SendError;
|
|||||||
use crate::{
|
use crate::{
|
||||||
error::RuntimeError,
|
error::RuntimeError,
|
||||||
filter::tagged::{Filter, Matcher},
|
filter::tagged::{Filter, Matcher},
|
||||||
|
ignore::IgnoreFilterer,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Errors emitted by the TaggedFilterer.
|
/// Errors emitted by the TaggedFilterer.
|
||||||
@ -55,6 +56,16 @@ pub enum TaggedFiltererError {
|
|||||||
#[error("cannot change compiled globset: {0:?}")]
|
#[error("cannot change compiled globset: {0:?}")]
|
||||||
#[diagnostic(code(watchexec::filter::tagged::globset_change))]
|
#[diagnostic(code(watchexec::filter::tagged::globset_change))]
|
||||||
GlobsetChange(#[source] SendError<Option<Gitignore>>),
|
GlobsetChange(#[source] SendError<Option<Gitignore>>),
|
||||||
|
|
||||||
|
/// Error received about the internal ignore filterer.
|
||||||
|
#[error("ignore filterer: {0}")]
|
||||||
|
#[diagnostic(code(watchexec::filter::tagged::ignore))]
|
||||||
|
Ignore(#[source] RuntimeError),
|
||||||
|
|
||||||
|
/// Error received when a new ignore filterer cannot be swapped in.
|
||||||
|
#[error("cannot swap in new ignore filterer: {0:?}")]
|
||||||
|
#[diagnostic(code(watchexec::filter::tagged::ignore_swap))]
|
||||||
|
IgnoreSwap(#[source] SendError<IgnoreFilterer>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<TaggedFiltererError> for RuntimeError {
|
impl From<TaggedFiltererError> for RuntimeError {
|
||||||
|
Loading…
Reference in New Issue
Block a user