From 26254f7022557168d9ffa7a3df37f43913035790 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fe=CC=81lix=20Saparelli?= Date: Fri, 24 Dec 2021 02:20:45 +1300 Subject: [PATCH] Add support for "tagged filter files" --- cli/src/main.rs | 2 + lib/src/filter/tagged.rs | 1 + lib/src/filter/tagged/files.rs | 90 ++++++++++++++++++++++++++++++++++ lib/src/ignore_files.rs | 2 +- 4 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 lib/src/filter/tagged/files.rs diff --git a/cli/src/main.rs b/cli/src/main.rs index a871b68..2e26ab1 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -130,6 +130,7 @@ async fn main() -> Result<()> { .and_then(|wd| wd.canonicalize()) .into_diagnostic()?; for filter in args.values_of("filter").unwrap_or_default() { + // TODO: use globset let mut filter: Filter = filter.parse()?; filter.in_path = Some(workdir.clone()); filters.push(filter); @@ -141,6 +142,7 @@ async fn main() -> Result<()> { .map(|s| s.split(',').map(|s| s.trim())) .flatten() { + // TODO: use globset filters.push(Filter { in_path: None, on: Matcher::Path, diff --git a/lib/src/filter/tagged.rs b/lib/src/filter/tagged.rs index 27ae730..e5e9a54 100644 --- a/lib/src/filter/tagged.rs +++ b/lib/src/filter/tagged.rs @@ -24,6 +24,7 @@ use crate::signal::source::MainSignal; pub use regex::Regex; pub mod error; +pub mod files; mod parse; pub mod swaplock; diff --git a/lib/src/filter/tagged/files.rs b/lib/src/filter/tagged/files.rs new file mode 100644 index 0000000..d990599 --- /dev/null +++ b/lib/src/filter/tagged/files.rs @@ -0,0 +1,90 @@ +//! Load "tagged filter files". + +use std::{ + env, + io::Error, + path::{Path, PathBuf}, + str::FromStr, +}; + +use tokio::fs::read_to_string; + +use crate::ignore_files::{discover_file, IgnoreFile}; + +use super::{error::TaggedFiltererError, Filter}; + +/// A filter file. +/// +/// This is merely a type wrapper around an [`IgnoreFile`], as the only difference is how the file +/// is parsed. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct FilterFile(pub IgnoreFile); + +/// Finds all filter files that apply to the current runtime. +/// +/// This considers: +/// - `$XDG_CONFIG_HOME/watchexec/filter`, as well as other locations (APPDATA on Windows…) +/// - Files from the `WATCHEXEC_FILTER_FILES` environment variable (comma-separated) +/// +/// All errors (permissions, etc) are collected and returned alongside the ignore files: you may +/// want to show them to the user while still using whatever ignores were successfully found. Errors +/// from files not being found are silently ignored (the files are just not returned). +pub async fn from_environment() -> (Vec, Vec) { + let mut files = Vec::new(); + let mut errors = Vec::new(); + + for path in env::var("WATCHEXEC_FILTER_FILES") + .unwrap_or_default() + .split(',') + { + discover_file(&mut files, &mut errors, None, None, PathBuf::from(path)).await; + } + + let mut wgis = Vec::with_capacity(5); + if let Ok(home) = env::var("XDG_CONFIG_HOME") { + wgis.push(Path::new(&home).join("watchexec/filter")); + } + if let Ok(home) = env::var("APPDATA") { + wgis.push(Path::new(&home).join("watchexec/filter")); + } + if let Ok(home) = env::var("USERPROFILE") { + wgis.push(Path::new(&home).join(".watchexec/filter")); + } + if let Ok(home) = env::var("HOME") { + wgis.push(Path::new(&home).join(".watchexec/filter")); + } + + for path in wgis { + if discover_file(&mut files, &mut errors, None, None, path).await { + break; + } + } + + (files.into_iter().map(FilterFile).collect(), errors) +} + +impl FilterFile { + /// Read and parse into [`Filter`]s. + /// + /// 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 load(&self) -> Result, TaggedFiltererError> { + let content = read_to_string(&self.0.path).await?; + let lines = content.lines(); + let mut filters = Vec::with_capacity(lines.size_hint().0); + + for line in lines { + if line.is_empty() || line.starts_with('#') { + continue; + } + + let mut f = Filter::from_str(line)?; + f.in_path = self.0.applies_in.clone(); + filters.push(f); + } + + Ok(filters) + } +} diff --git a/lib/src/ignore_files.rs b/lib/src/ignore_files.rs index ecb51a8..824e26b 100644 --- a/lib/src/ignore_files.rs +++ b/lib/src/ignore_files.rs @@ -264,7 +264,7 @@ pub async fn from_environment() -> (Vec, Vec) { } #[inline] -async fn discover_file( +pub(crate) async fn discover_file( files: &mut Vec, errors: &mut Vec, applies_in: Option,