mirror of
https://github.com/watchexec/watchexec.git
synced 2024-11-16 09:08:37 +01:00
160 lines
3.5 KiB
Rust
160 lines
3.5 KiB
Rust
|
use std::path::{Path, PathBuf};
|
||
|
|
||
|
use ignore::{gitignore::Glob, Match};
|
||
|
use ignore_files::{IgnoreFile, IgnoreFilter};
|
||
|
|
||
|
pub mod ignore_tests {
|
||
|
pub use super::ig_file as file;
|
||
|
pub use super::ignore_filt as filt;
|
||
|
pub use super::Applies;
|
||
|
pub use super::PathHarness;
|
||
|
}
|
||
|
|
||
|
/// Get the drive letter of the current working directory.
|
||
|
#[cfg(windows)]
|
||
|
fn drive_root() -> String {
|
||
|
let path = std::fs::canonicalize(".").unwrap();
|
||
|
|
||
|
let Some(prefix) = path.components().next() else {
|
||
|
return r"C:\".into();
|
||
|
};
|
||
|
|
||
|
match prefix {
|
||
|
std::path::Component::Prefix(prefix_component) => prefix_component
|
||
|
.as_os_str()
|
||
|
.to_str()
|
||
|
.map(|p| p.to_owned() + r"\")
|
||
|
.unwrap_or(r"C:\".into()),
|
||
|
_ => r"C:\".into(),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn normalize_path(path: &str) -> PathBuf {
|
||
|
#[cfg(windows)]
|
||
|
let path: &str = &String::from(path)
|
||
|
.strip_prefix("/")
|
||
|
.map_or(path.into(), |p| drive_root() + p);
|
||
|
|
||
|
let path: PathBuf = if Path::new(path).has_root() {
|
||
|
path.into()
|
||
|
} else {
|
||
|
std::fs::canonicalize(".").unwrap().join("tests").join(path)
|
||
|
};
|
||
|
|
||
|
dunce::simplified(&path).into()
|
||
|
}
|
||
|
|
||
|
pub trait PathHarness {
|
||
|
fn check_path(&self, path: &Path, is_dir: bool) -> Match<&Glob>;
|
||
|
|
||
|
fn path_pass(&self, path: &str, is_dir: bool, pass: bool) {
|
||
|
let full_path = &normalize_path(path);
|
||
|
|
||
|
tracing::info!(?path, ?is_dir, ?pass, "check");
|
||
|
|
||
|
let result = self.check_path(full_path, is_dir);
|
||
|
|
||
|
assert_eq!(
|
||
|
match result {
|
||
|
Match::None => true,
|
||
|
Match::Ignore(glob) => !glob.from().map_or(true, |f| full_path.starts_with(f)),
|
||
|
Match::Whitelist(_glob) => true,
|
||
|
},
|
||
|
pass,
|
||
|
"{} {:?} (expected {}) [result: {}]",
|
||
|
if is_dir { "dir" } else { "file" },
|
||
|
full_path,
|
||
|
if pass { "pass" } else { "fail" },
|
||
|
match result {
|
||
|
Match::None => String::from("None"),
|
||
|
Match::Ignore(glob) => format!(
|
||
|
"Ignore({})",
|
||
|
glob.from()
|
||
|
.map_or(String::from(""), |f| f.display().to_string())
|
||
|
),
|
||
|
Match::Whitelist(glob) => format!(
|
||
|
"Whitelist({})",
|
||
|
glob.from()
|
||
|
.map_or(String::from(""), |f| f.display().to_string())
|
||
|
),
|
||
|
},
|
||
|
);
|
||
|
}
|
||
|
|
||
|
fn file_does_pass(&self, path: &str) {
|
||
|
self.path_pass(path, false, true);
|
||
|
}
|
||
|
|
||
|
fn file_doesnt_pass(&self, path: &str) {
|
||
|
self.path_pass(path, false, false);
|
||
|
}
|
||
|
|
||
|
fn dir_does_pass(&self, path: &str) {
|
||
|
self.path_pass(path, true, true);
|
||
|
}
|
||
|
|
||
|
fn dir_doesnt_pass(&self, path: &str) {
|
||
|
self.path_pass(path, true, false);
|
||
|
}
|
||
|
|
||
|
fn agnostic_pass(&self, path: &str) {
|
||
|
self.file_does_pass(path);
|
||
|
self.dir_does_pass(path);
|
||
|
}
|
||
|
|
||
|
fn agnostic_fail(&self, path: &str) {
|
||
|
self.file_doesnt_pass(path);
|
||
|
self.dir_doesnt_pass(path);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
impl PathHarness for IgnoreFilter {
|
||
|
fn check_path(&self, path: &Path, is_dir: bool) -> Match<&Glob> {
|
||
|
self.match_path(&path, is_dir)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fn tracing_init() {
|
||
|
use tracing_subscriber::{
|
||
|
fmt::{format::FmtSpan, Subscriber},
|
||
|
util::SubscriberInitExt,
|
||
|
EnvFilter,
|
||
|
};
|
||
|
Subscriber::builder()
|
||
|
.pretty()
|
||
|
.with_span_events(FmtSpan::NEW | FmtSpan::CLOSE)
|
||
|
.with_env_filter(EnvFilter::from_default_env())
|
||
|
.finish()
|
||
|
.try_init()
|
||
|
.ok();
|
||
|
}
|
||
|
|
||
|
pub async fn ignore_filt(origin: &str, ignore_files: &[IgnoreFile]) -> IgnoreFilter {
|
||
|
tracing_init();
|
||
|
let origin = normalize_path(origin);
|
||
|
IgnoreFilter::new(origin, ignore_files)
|
||
|
.await
|
||
|
.expect("making filterer")
|
||
|
}
|
||
|
|
||
|
pub fn ig_file(name: &str) -> IgnoreFile {
|
||
|
let path = normalize_path(name);
|
||
|
let parent: PathBuf = path.parent().unwrap_or(&path).into();
|
||
|
IgnoreFile {
|
||
|
path,
|
||
|
applies_in: Some(parent),
|
||
|
applies_to: None,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub trait Applies {
|
||
|
fn applies_globally(self) -> Self;
|
||
|
}
|
||
|
|
||
|
impl Applies for IgnoreFile {
|
||
|
fn applies_globally(mut self) -> Self {
|
||
|
self.applies_in = None;
|
||
|
self
|
||
|
}
|
||
|
}
|