Discover ignore files for path and for user/env
This commit is contained in:
parent
8bc58ba6b5
commit
65b042ec8f
|
@ -248,6 +248,9 @@ name = "cc"
|
|||
version = "1.0.70"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d26a6ce4b6a484fa3edb70f7efa6fc430fd2b87285fe8b84304fd0936faa0dc0"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
|
@ -593,6 +596,16 @@ version = "1.0.7"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191"
|
||||
dependencies = [
|
||||
"matches",
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fsevent-sys"
|
||||
version = "4.0.0"
|
||||
|
@ -745,6 +758,21 @@ version = "0.25.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0a01e0497841a3b2db4f8afa483cce65f7e96a3498bd6c541734792aeac8fe7"
|
||||
|
||||
[[package]]
|
||||
name = "git2"
|
||||
version = "0.13.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a8057932925d3a9d9e4434ea016570d37420ddb1ceed45a174d577f24ed6700"
|
||||
dependencies = [
|
||||
"bitflags 1.2.1",
|
||||
"libc",
|
||||
"libgit2-sys",
|
||||
"log",
|
||||
"openssl-probe",
|
||||
"openssl-sys",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "globset"
|
||||
version = "0.4.8"
|
||||
|
@ -890,6 +918,17 @@ dependencies = [
|
|||
"tokio-io-timeout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
|
||||
dependencies = [
|
||||
"matches",
|
||||
"unicode-bidi",
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indenter"
|
||||
version = "0.3.3"
|
||||
|
@ -971,6 +1010,15 @@ version = "0.4.8"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kqueue"
|
||||
version = "1.0.4"
|
||||
|
@ -1016,6 +1064,46 @@ version = "0.2.103"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
|
||||
|
||||
[[package]]
|
||||
name = "libgit2-sys"
|
||||
version = "0.12.24+1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddbd6021eef06fb289a8f54b3c2acfdd85ff2a585dfbb24b8576325373d2152c"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"libssh2-sys",
|
||||
"libz-sys",
|
||||
"openssl-sys",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libssh2-sys"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0186af0d8f171ae6b9c4c90ec51898bad5d08a2d5e470903a50d9ad8959cbee"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"libz-sys",
|
||||
"openssl-sys",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libz-sys"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de5435b8549c16d423ed0c03dbaafe57cf6c3344744f1242520d59c9d8ecec66"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linked-hash-map"
|
||||
version = "0.5.4"
|
||||
|
@ -1070,6 +1158,12 @@ dependencies = [
|
|||
"regex-automata",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matches"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.4.1"
|
||||
|
@ -1343,6 +1437,25 @@ version = "1.8.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-probe"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.67"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69df2d8dfc6ce3aaf44b40dec6f487d5a886516cf6879c49e98e0710f310a058"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "owo-colors"
|
||||
version = "1.3.0"
|
||||
|
@ -1472,6 +1585,12 @@ version = "0.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c9b1041b4387893b91ee6746cddfc28516aff326a3519fb2adf820932c5e6cb"
|
||||
|
||||
[[package]]
|
||||
name = "polling"
|
||||
version = "2.1.0"
|
||||
|
@ -2143,6 +2262,21 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f83b2a3d4d9091d0abd7eba4dc2710b1718583bd4d8992e2190720ea38f391f7"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec_macros"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.12.0"
|
||||
|
@ -2411,6 +2545,12 @@ dependencies = [
|
|||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-linebreak"
|
||||
version = "0.1.2"
|
||||
|
@ -2420,6 +2560,15 @@ dependencies = [
|
|||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9"
|
||||
dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.8.0"
|
||||
|
@ -2444,12 +2593,30 @@ version = "0.2.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
"matches",
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
|
||||
|
||||
[[package]]
|
||||
name = "vcpkg"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.3"
|
||||
|
@ -2535,12 +2702,14 @@ name = "watchexec"
|
|||
version = "1.17.1"
|
||||
dependencies = [
|
||||
"async-recursion",
|
||||
"async-stream",
|
||||
"atomic-take",
|
||||
"clearscreen",
|
||||
"color-eyre",
|
||||
"command-group",
|
||||
"dunce",
|
||||
"futures",
|
||||
"git2",
|
||||
"globset",
|
||||
"miette",
|
||||
"nom 7.0.0",
|
||||
|
|
|
@ -29,6 +29,8 @@ regex = "1.5.4"
|
|||
thiserror = "1.0.26"
|
||||
tracing = "0.1.26"
|
||||
unicase = "2.6.0"
|
||||
async-stream = "0.3.2"
|
||||
git2 = "0.13.22"
|
||||
|
||||
[dependencies.command-group]
|
||||
version = "1.0.5"
|
||||
|
|
|
@ -185,13 +185,13 @@ pub struct InitConfig {
|
|||
}
|
||||
|
||||
impl Default for InitConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
error_handler: Box::new(()) as _,
|
||||
error_channel_size: 64,
|
||||
event_channel_size: 1024,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InitConfig {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! Error types for critical, runtime, and specialised errors.
|
||||
|
||||
use std::{path::PathBuf};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use miette::Diagnostic;
|
||||
use thiserror::Error;
|
||||
|
@ -9,7 +9,11 @@ use tokio::{
|
|||
task::JoinError,
|
||||
};
|
||||
|
||||
use crate::{action, event::Event, fs::{self, Watcher}};
|
||||
use crate::{
|
||||
action,
|
||||
event::Event,
|
||||
fs::{self, Watcher},
|
||||
};
|
||||
|
||||
/// Errors which are not recoverable and stop watchexec execution.
|
||||
#[derive(Debug, Diagnostic, Error)]
|
||||
|
|
|
@ -11,15 +11,14 @@ use unicase::UniCase;
|
|||
use crate::error::RuntimeError;
|
||||
use crate::event::{Event, Tag};
|
||||
use crate::filter::Filterer;
|
||||
use crate::project::ProjectType;
|
||||
|
||||
// to make filters
|
||||
pub use globset::Glob;
|
||||
pub use regex::Regex;
|
||||
|
||||
pub mod error;
|
||||
mod parse;
|
||||
pub mod swaplock;
|
||||
pub mod error;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TaggedFilterer {
|
||||
|
@ -121,7 +120,11 @@ impl TaggedFilterer {
|
|||
|
||||
// Ok(Some(bool)) => the match was applied, bool is the result
|
||||
// Ok(None) => for some precondition, the match was not done (mismatched tag, out of context, …)
|
||||
fn match_tag(&self, filter: &Filter, tag: &Tag) -> Result<Option<bool>, error::TaggedFiltererError> {
|
||||
fn match_tag(
|
||||
&self,
|
||||
filter: &Filter,
|
||||
tag: &Tag,
|
||||
) -> Result<Option<bool>, error::TaggedFiltererError> {
|
||||
trace!(?tag, matcher=?filter.on, "matching filter to tag");
|
||||
match (tag, filter.on) {
|
||||
(tag, Matcher::Tag) => filter.matches(tag.discriminant_name()),
|
||||
|
@ -220,7 +223,9 @@ impl TaggedFilterer {
|
|||
///
|
||||
/// This parses and compiles the glob, and wraps any error with nice [miette] diagnostics.
|
||||
pub fn glob(s: &str) -> Result<Pattern, error::TaggedFiltererError> {
|
||||
Glob::new(s).map_err(error::TaggedFiltererError::GlobParse).map(|g| Pattern::Glob(g.compile_matcher()))
|
||||
Glob::new(s)
|
||||
.map_err(error::TaggedFiltererError::GlobParse)
|
||||
.map(|g| Pattern::Glob(g.compile_matcher()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -269,38 +274,6 @@ impl Filter {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns a set of ignores for the given project type.
|
||||
pub fn for_project(project_type: ProjectType) -> Vec<Self> {
|
||||
const ERR: &str = "static pattern";
|
||||
|
||||
// TODO: use gitignores
|
||||
|
||||
match project_type {
|
||||
ProjectType::Git => vec![
|
||||
Self::from_glob_ignore("/.git").expect(ERR),
|
||||
],
|
||||
ProjectType::Mercurial => todo!(),
|
||||
ProjectType::Pijul => todo!(),
|
||||
ProjectType::Fossil => todo!(),
|
||||
ProjectType::Cargo => vec![
|
||||
Self::from_glob_ignore("/debug").expect(ERR),
|
||||
Self::from_glob_ignore("/target").expect(ERR),
|
||||
Self::from_glob_ignore("Cargo.lock").expect(ERR),
|
||||
Self::from_glob_ignore("**/*.rs.bk").expect(ERR),
|
||||
Self::from_glob_ignore("*.pdb").expect(ERR),
|
||||
],
|
||||
ProjectType::JavaScript => todo!(),
|
||||
ProjectType::Bundler => todo!(),
|
||||
ProjectType::RubyGem => todo!(),
|
||||
ProjectType::Pip => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_glob_ignore(glob: impl AsRef<str>) -> Result<Self, error::TaggedFiltererError> {
|
||||
let glob = Glob::new(glob.as_ref())?.compile_matcher();
|
||||
Ok(Self { in_path: None, on: Matcher::Path, op: Op::NotGlob, pat: Pattern::Glob(glob), negate: false })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
//! Error type for TaggedFilterer.
|
||||
|
||||
use std::{collections::HashMap};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use miette::Diagnostic;
|
||||
use thiserror::Error;
|
||||
use tokio::sync::watch::{error::SendError};
|
||||
use tokio::sync::watch::error::SendError;
|
||||
|
||||
use crate::{error::RuntimeError, filter::tagged::{Filter, Matcher}};
|
||||
use crate::{
|
||||
error::RuntimeError,
|
||||
filter::tagged::{Filter, Matcher},
|
||||
};
|
||||
|
||||
/// Errors emitted by the TaggedFilterer.
|
||||
#[derive(Debug, Diagnostic, Error)]
|
||||
|
@ -42,10 +45,10 @@ pub enum TaggedFiltererError {
|
|||
}
|
||||
|
||||
impl From<TaggedFiltererError> for RuntimeError {
|
||||
fn from(err: TaggedFiltererError) -> Self {
|
||||
Self::Filterer {
|
||||
fn from(err: TaggedFiltererError) -> Self {
|
||||
Self::Filterer {
|
||||
kind: "tagged",
|
||||
err: Box::new(err) as _,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,29 @@
|
|||
//! Find ignore files, like `.gitignore`, `.ignore`, and others.
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::{
|
||||
env,
|
||||
io::{Error, ErrorKind},
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use crate::{error::RuntimeError, project::ProjectType};
|
||||
use futures::{pin_mut, Stream, StreamExt};
|
||||
use tokio::fs::{metadata, read_dir};
|
||||
|
||||
use crate::project::ProjectType;
|
||||
|
||||
/// An ignore file.
|
||||
///
|
||||
/// This records both the path to the ignore file and some basic metadata about it: which project
|
||||
/// type it applies to if any, and which subtree it applies in if any (`None` = global ignore file).
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct IgnoreFile {
|
||||
/// The path to the ignore file.
|
||||
pub path: PathBuf,
|
||||
pub applies_in: PathBuf,
|
||||
|
||||
/// The path to the subtree the ignore file applies to, or `None` for global ignores.
|
||||
pub applies_in: Option<PathBuf>,
|
||||
|
||||
/// Which project type the ignore file applies to, or was found through.
|
||||
pub applies_to: Option<ProjectType>,
|
||||
}
|
||||
|
||||
|
@ -22,22 +38,257 @@ pub struct IgnoreFile {
|
|||
///
|
||||
/// Importantly, this should be called from the origin of the project, not a subfolder. This
|
||||
/// function will not discover the project origin, and will not traverse parent directories. Use the
|
||||
/// [`project::origin`](crate::project::origin) function for that.
|
||||
/// [`project::origins`](crate::project::origins) function for that.
|
||||
///
|
||||
/// This function also does not distinguish between project folder types, and collects all files for
|
||||
/// all supported VCSs and other project types. Use the `applies_to` field to filter the results.
|
||||
pub async fn from_origin(path: impl AsRef<Path>) -> Result<Vec<IgnoreFile>, RuntimeError> {
|
||||
todo!()
|
||||
///
|
||||
/// 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).
|
||||
///
|
||||
/// ## Special case: project-local git config specifying `core.excludesFile`
|
||||
///
|
||||
/// If the project's `.git/config` specifies a value for `core.excludesFile`, this function will
|
||||
/// return an `IgnoreFile { path: path/to/that/file, applies_in: None, applies_to: Some(ProjectType::Git) }`.
|
||||
/// This is the only case in which the `applies_in` field is None from this function. When such is
|
||||
/// received the global Git ignore files found by [`from_environment()`] **should be ignored**.
|
||||
pub async fn from_origin(path: impl AsRef<Path>) -> (Vec<IgnoreFile>, Vec<Error>) {
|
||||
let base = path.as_ref().to_owned();
|
||||
let mut files = Vec::new();
|
||||
let mut errors = Vec::new();
|
||||
|
||||
match find_file(base.join(".git/config")).await {
|
||||
Err(err) => errors.push(err),
|
||||
Ok(None) => {}
|
||||
Ok(Some(path)) => match git2::Config::open(&path) {
|
||||
Err(err) => errors.push(Error::new(ErrorKind::Other, err)),
|
||||
Ok(config) => {
|
||||
if let Ok(excludes) = config.get_path("core.excludesFile") {
|
||||
discover_file(
|
||||
&mut files,
|
||||
&mut errors,
|
||||
None,
|
||||
Some(ProjectType::Git),
|
||||
excludes,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
discover_file(
|
||||
&mut files,
|
||||
&mut errors,
|
||||
Some(base.clone()),
|
||||
Some(ProjectType::Bazaar),
|
||||
base.join(".bzrignore"),
|
||||
)
|
||||
.await;
|
||||
|
||||
discover_file(
|
||||
&mut files,
|
||||
&mut errors,
|
||||
Some(base.clone()),
|
||||
Some(ProjectType::Darcs),
|
||||
base.join("_darcs/prefs/boring"),
|
||||
)
|
||||
.await;
|
||||
|
||||
discover_file(
|
||||
&mut files,
|
||||
&mut errors,
|
||||
Some(base.clone()),
|
||||
Some(ProjectType::Fossil),
|
||||
base.join(".fossil-settings/ignore-glob"),
|
||||
)
|
||||
.await;
|
||||
|
||||
discover_file(
|
||||
&mut files,
|
||||
&mut errors,
|
||||
Some(base.clone()),
|
||||
Some(ProjectType::Git),
|
||||
base.join(".git/info/exclude"),
|
||||
)
|
||||
.await;
|
||||
|
||||
let dirs = all_dirs(base);
|
||||
pin_mut!(dirs);
|
||||
while let Some(p) = dirs.next().await {
|
||||
match p {
|
||||
Err(err) => errors.push(err),
|
||||
Ok(dir) => {
|
||||
discover_file(
|
||||
&mut files,
|
||||
&mut errors,
|
||||
Some(dir.clone()),
|
||||
None,
|
||||
dir.join(".ignore"),
|
||||
)
|
||||
.await;
|
||||
|
||||
discover_file(
|
||||
&mut files,
|
||||
&mut errors,
|
||||
Some(dir.clone()),
|
||||
Some(ProjectType::Git),
|
||||
dir.join(".gitignore"),
|
||||
)
|
||||
.await;
|
||||
|
||||
discover_file(
|
||||
&mut files,
|
||||
&mut errors,
|
||||
Some(dir.clone()),
|
||||
Some(ProjectType::Mercurial),
|
||||
dir.join(".hgignore"),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(files, errors)
|
||||
}
|
||||
|
||||
/// Finds all ignore files that apply to the current runtime.
|
||||
///
|
||||
/// This considers:
|
||||
/// - System-wide ignore files (e.g. `/etc/git/ignore`)
|
||||
/// - User-specific ignore files (e.g. `~/.gitignore`)
|
||||
/// - User-specific git ignore files (e.g. `~/.gitignore`)
|
||||
/// - Git configurable ignore files (e.g. with `core.excludesFile` in system or user config)
|
||||
/// - Other VCS ignore files in system and user locations and config (e.g. `~/.hgignore`)
|
||||
/// - Files from the `WATCHEXEC_IGNORE_FILES` environment variable (comma-separated).
|
||||
pub async fn from_environment() -> Result<Vec<IgnoreFile>, RuntimeError> {
|
||||
todo!()
|
||||
/// - `$XDG_CONFIG_HOME/watchexec/ignore`, as well as other locations (APPDATA on Windows…)
|
||||
/// - Files from the `WATCHEXEC_IGNORE_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<IgnoreFile>, Vec<Error>) {
|
||||
let mut files = Vec::new();
|
||||
let mut errors = Vec::new();
|
||||
|
||||
for path in env::var("WATCHEXEC_IGNORE_FILES")
|
||||
.unwrap_or_default()
|
||||
.split(',')
|
||||
{
|
||||
discover_file(&mut files, &mut errors, None, None, PathBuf::from(path)).await;
|
||||
}
|
||||
|
||||
let mut found_git_global = false;
|
||||
match git2::Config::open_default() {
|
||||
Err(err) => errors.push(Error::new(ErrorKind::Other, err)),
|
||||
Ok(config) => {
|
||||
if let Ok(excludes) = config.get_path("core.excludesFile") {
|
||||
if discover_file(
|
||||
&mut files,
|
||||
&mut errors,
|
||||
None,
|
||||
Some(ProjectType::Git),
|
||||
excludes,
|
||||
)
|
||||
.await
|
||||
{
|
||||
found_git_global = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !found_git_global {
|
||||
let mut tries = Vec::with_capacity(5);
|
||||
if let Ok(home) = env::var("XDG_CONFIG_HOME") {
|
||||
tries.push(Path::new(&home).join("git/ignore"));
|
||||
}
|
||||
if let Ok(home) = env::var("APPDATA") {
|
||||
tries.push(Path::new(&home).join(".gitignore"));
|
||||
}
|
||||
if let Ok(home) = env::var("USERPROFILE") {
|
||||
tries.push(Path::new(&home).join(".gitignore"));
|
||||
}
|
||||
if let Ok(home) = env::var("HOME") {
|
||||
tries.push(Path::new(&home).join(".config/git/ignore"));
|
||||
tries.push(Path::new(&home).join(".gitignore"));
|
||||
}
|
||||
|
||||
for path in tries {
|
||||
if discover_file(&mut files, &mut errors, None, Some(ProjectType::Git), path).await {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut homes = Vec::with_capacity(5);
|
||||
if let Ok(home) = env::var("XDG_CONFIG_HOME") {
|
||||
homes.push(Path::new(&home).join("watchexec/ignore"));
|
||||
}
|
||||
if let Ok(home) = env::var("APPDATA") {
|
||||
homes.push(Path::new(&home).join("watchexec/ignore"));
|
||||
}
|
||||
if let Ok(home) = env::var("USERPROFILE") {
|
||||
homes.push(Path::new(&home).join(".watchexec/ignore"));
|
||||
}
|
||||
if let Ok(home) = env::var("HOME") {
|
||||
homes.push(Path::new(&home).join(".watchexec/ignore"));
|
||||
}
|
||||
|
||||
for path in homes {
|
||||
if discover_file(&mut files, &mut errors, None, None, path).await {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
(files, errors)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
async fn discover_file(
|
||||
files: &mut Vec<IgnoreFile>,
|
||||
errors: &mut Vec<Error>,
|
||||
applies_in: Option<PathBuf>,
|
||||
applies_to: Option<ProjectType>,
|
||||
path: PathBuf,
|
||||
) -> bool {
|
||||
match find_file(path).await {
|
||||
Err(err) => {
|
||||
errors.push(err);
|
||||
false
|
||||
}
|
||||
Ok(None) => false,
|
||||
Ok(Some(path)) => {
|
||||
files.push(IgnoreFile {
|
||||
path,
|
||||
applies_in,
|
||||
applies_to,
|
||||
});
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn find_file(path: PathBuf) -> Result<Option<PathBuf>, Error> {
|
||||
match metadata(&path).await {
|
||||
Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(None),
|
||||
Err(err) => Err(err),
|
||||
Ok(meta) if meta.is_file() && meta.len() > 0 => Ok(Some(path)),
|
||||
Ok(_) => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn all_dirs(path: PathBuf) -> impl Stream<Item = Result<PathBuf, Error>> {
|
||||
async_stream::try_stream! {
|
||||
yield path.clone();
|
||||
let mut to_visit = vec![path];
|
||||
|
||||
while let Some(path) = to_visit.pop() {
|
||||
let mut dir = read_dir(&path).await?;
|
||||
while let Some(entry) = dir.next_entry().await? {
|
||||
if entry.file_type().await?.is_dir() {
|
||||
let path = entry.path();
|
||||
to_visit.push(path.clone());
|
||||
yield path;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,31 +1,35 @@
|
|||
//! Detect project type and origin.
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::{
|
||||
io::Error,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use crate::error::CriticalError;
|
||||
|
||||
pub async fn origin(path: impl AsRef<Path>) -> Result<PathBuf, CriticalError> {
|
||||
pub async fn origins(path: impl AsRef<Path>) -> Result<Vec<PathBuf>, Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Returns all project types detected at this given origin.
|
||||
///
|
||||
/// This should be called with the result of [`origin()`], or a project origin if already known; it
|
||||
/// This should be called with a result of [`origins()`], or a project origin if already known; it
|
||||
/// will not find the origin itself.
|
||||
pub async fn types(path: impl AsRef<Path>) -> Result<Vec<ProjectType>, CriticalError> {
|
||||
pub async fn types(path: impl AsRef<Path>) -> Result<Vec<ProjectType>, Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[non_exhaustive]
|
||||
pub enum ProjectType {
|
||||
Bazaar,
|
||||
Darcs,
|
||||
Fossil,
|
||||
Git,
|
||||
Mercurial,
|
||||
Pijul,
|
||||
Fossil,
|
||||
|
||||
Bundler,
|
||||
Cargo,
|
||||
JavaScript,
|
||||
Bundler,
|
||||
RubyGem,
|
||||
Pip,
|
||||
RubyGem,
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue