Extract errors into one thing and return a result more often
This commit is contained in:
parent
a6163cc599
commit
2c5c145042
|
@ -0,0 +1,62 @@
|
|||
use globset;
|
||||
use notify;
|
||||
use std::{error::Error as StdError, fmt, io};
|
||||
|
||||
pub type Result<T> = ::std::result::Result<T, Error>;
|
||||
|
||||
pub enum Error {
|
||||
Canonicalization(String, io::Error),
|
||||
Glob(globset::Error),
|
||||
Io(io::Error),
|
||||
Notify(notify::Error),
|
||||
}
|
||||
|
||||
impl StdError for Error {}
|
||||
|
||||
impl From<globset::Error> for Error {
|
||||
fn from(err: globset::Error) -> Self {
|
||||
Error::Glob(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(err: io::Error) -> Self {
|
||||
Error::Io(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<notify::Error> for Error {
|
||||
fn from(err: notify::Error) -> Self {
|
||||
match err {
|
||||
notify::Error::Io(err) => Error::Io(err),
|
||||
other => Error::Notify(other),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{} error: {}",
|
||||
match self {
|
||||
Error::Canonicalization(_, _) => "Path",
|
||||
Error::Glob(_) => "Globset",
|
||||
Error::Io(_) => "I/O",
|
||||
Error::Notify(_) => "Notify",
|
||||
},
|
||||
match self {
|
||||
Error::Canonicalization(path, err) => {
|
||||
format!("couldn't canonicalize '{}':\n{}", path, err)
|
||||
}
|
||||
err => format!("{}", err),
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::Display::fmt(self, f)
|
||||
}
|
||||
}
|
|
@ -113,9 +113,9 @@ impl Gitignore {
|
|||
|
||||
impl GitignoreFile {
|
||||
pub fn new(path: &Path) -> Result<GitignoreFile, Error> {
|
||||
let mut file = try!(fs::File::open(path));
|
||||
let mut file = fs::File::open(path)?;
|
||||
let mut contents = String::new();
|
||||
try!(file.read_to_string(&mut contents));
|
||||
file.read_to_string(&mut contents)?;
|
||||
|
||||
let lines = contents.lines().collect();
|
||||
let root = path.parent().unwrap();
|
||||
|
@ -138,14 +138,14 @@ impl GitignoreFile {
|
|||
pat = pat + "/**";
|
||||
}
|
||||
|
||||
let glob = try!(GlobBuilder::new(&pat).literal_separator(true).build());
|
||||
let glob = GlobBuilder::new(&pat).literal_separator(true).build()?;
|
||||
|
||||
builder.add(glob);
|
||||
patterns.push(p);
|
||||
}
|
||||
|
||||
Ok(GitignoreFile {
|
||||
set: try!(builder.build()),
|
||||
set: builder.build()?,
|
||||
patterns: patterns,
|
||||
root: root.to_owned(),
|
||||
})
|
||||
|
|
|
@ -19,6 +19,7 @@ extern crate winapi;
|
|||
extern crate mktemp;
|
||||
|
||||
pub mod cli;
|
||||
pub mod error;
|
||||
mod gitignore;
|
||||
mod notification_filter;
|
||||
mod pathop;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
extern crate watchexec;
|
||||
use watchexec::{cli, run};
|
||||
use watchexec::{cli, error, run};
|
||||
|
||||
fn main() -> run::Result<()> {
|
||||
fn main() -> error::Result<()> {
|
||||
run(cli::get_args())
|
||||
}
|
||||
|
|
|
@ -1,36 +1,26 @@
|
|||
extern crate glob;
|
||||
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
|
||||
use globset;
|
||||
use error;
|
||||
use gitignore::Gitignore;
|
||||
use globset::{Glob, GlobSet, GlobSetBuilder};
|
||||
|
||||
use gitignore;
|
||||
use std::path::Path;
|
||||
|
||||
pub struct NotificationFilter {
|
||||
filters: GlobSet,
|
||||
filter_count: usize,
|
||||
ignores: GlobSet,
|
||||
ignore_files: gitignore::Gitignore,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
Glob(globset::Error),
|
||||
Io(io::Error),
|
||||
ignore_files: Gitignore,
|
||||
}
|
||||
|
||||
impl NotificationFilter {
|
||||
pub fn new(
|
||||
filters: Vec<String>,
|
||||
ignores: Vec<String>,
|
||||
ignore_files: gitignore::Gitignore,
|
||||
) -> Result<NotificationFilter, Error> {
|
||||
ignore_files: Gitignore,
|
||||
) -> error::Result<NotificationFilter> {
|
||||
let mut filter_set_builder = GlobSetBuilder::new();
|
||||
for f in &filters {
|
||||
filter_set_builder.add(try!(Glob::new(f)));
|
||||
|
||||
filter_set_builder.add(Glob::new(f)?);
|
||||
debug!("Adding filter: \"{}\"", f);
|
||||
}
|
||||
|
||||
|
@ -41,18 +31,14 @@ impl NotificationFilter {
|
|||
ignore_path = Path::new("**").join(&ignore_path);
|
||||
}
|
||||
let pattern = ignore_path.to_str().unwrap();
|
||||
ignore_set_builder.add(try!(Glob::new(pattern)));
|
||||
|
||||
ignore_set_builder.add(Glob::new(pattern)?);
|
||||
debug!("Adding ignore: \"{}\"", pattern);
|
||||
}
|
||||
|
||||
let filter_set = try!(filter_set_builder.build());
|
||||
let ignore_set = try!(ignore_set_builder.build());
|
||||
|
||||
Ok(NotificationFilter {
|
||||
filters: filter_set,
|
||||
filters: filter_set_builder.build()?,
|
||||
filter_count: filters.len(),
|
||||
ignores: ignore_set,
|
||||
ignores: ignore_set_builder.build()?,
|
||||
ignore_files: ignore_files,
|
||||
})
|
||||
}
|
||||
|
@ -80,18 +66,6 @@ impl NotificationFilter {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<io::Error> for Error {
|
||||
fn from(err: io::Error) -> Error {
|
||||
Error::Io(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<globset::Error> for Error {
|
||||
fn from(err: globset::Error) -> Error {
|
||||
Error::Glob(err)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::NotificationFilter;
|
||||
|
|
88
src/run.rs
88
src/run.rs
|
@ -1,23 +1,22 @@
|
|||
use std::collections::HashMap;
|
||||
use std::fs::canonicalize;
|
||||
use std::io::Write;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::mpsc::{channel, Receiver};
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::time::Duration;
|
||||
|
||||
use cli;
|
||||
use env_logger;
|
||||
use error::{Error, Result};
|
||||
use gitignore;
|
||||
use log;
|
||||
use notification_filter::NotificationFilter;
|
||||
use notify::Error;
|
||||
use notify;
|
||||
use pathop::PathOp;
|
||||
use process::{self, Process};
|
||||
use signal::{self, Signal};
|
||||
use watcher::{Event, Watcher};
|
||||
|
||||
pub type Result<T> = ::std::result::Result<T, Box<::std::error::Error>>;
|
||||
|
||||
fn init_logger(debug: bool) {
|
||||
let mut log_builder = env_logger::Builder::new();
|
||||
let level = if debug {
|
||||
|
@ -32,21 +31,6 @@ fn init_logger(debug: bool) {
|
|||
.init();
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn should_switch_to_poll(e: &Error) -> bool {
|
||||
use nix::libc;
|
||||
|
||||
match e {
|
||||
&Error::Io(ref e) if e.raw_os_error() == Some(libc::ENOSPC) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
fn should_switch_to_poll(_: &Error) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn run(args: cli::Args) -> Result<()> {
|
||||
let child_process: Arc<RwLock<Option<Process>>> = Arc::new(RwLock::new(None));
|
||||
let weak_child = Arc::downgrade(&child_process);
|
||||
|
@ -68,48 +52,39 @@ pub fn run(args: cli::Args) -> Result<()> {
|
|||
|
||||
init_logger(args.debug);
|
||||
|
||||
let paths: Result<Vec<PathBuf>> = args
|
||||
.paths
|
||||
.iter()
|
||||
.map(|p| {
|
||||
Ok(Path::new(&p)
|
||||
.canonicalize()
|
||||
.map_err(|e| format!("Unable to canonicalize path: \"{}\", {}", p, e))?
|
||||
.to_owned())
|
||||
})
|
||||
.collect();
|
||||
let paths = paths?;
|
||||
let mut paths = vec![];
|
||||
for path in args.paths {
|
||||
paths.push(canonicalize(&path).map_err(|e| Error::Canonicalization(path, e))?);
|
||||
}
|
||||
|
||||
let gitignore = if !args.no_vcs_ignore {
|
||||
gitignore::load(&paths)
|
||||
} else {
|
||||
gitignore::load(&[])
|
||||
};
|
||||
|
||||
let filter = NotificationFilter::new(args.filters, args.ignores, gitignore)
|
||||
.expect("unable to create notification filter");
|
||||
let gitignore = gitignore::load(if args.no_vcs_ignore { &[] } else { &paths });
|
||||
let filter = NotificationFilter::new(args.filters, args.ignores, gitignore)?;
|
||||
|
||||
let (tx, rx) = channel();
|
||||
let watcher = match Watcher::new(tx.clone(), &paths, args.poll, args.poll_interval) {
|
||||
Ok(watcher) => watcher,
|
||||
Err(ref e) if !args.poll && should_switch_to_poll(e) => {
|
||||
warn!(
|
||||
"System notification limit is too small, \
|
||||
falling back to polling mode."
|
||||
);
|
||||
if cfg!(target_os = "linux") {
|
||||
warn!(
|
||||
"For better performance increase system limit: \n \
|
||||
sysctl fs.inotify.max_user_watches=524288"
|
||||
);
|
||||
let (poll, poll_interval) = (args.poll, args.poll_interval).clone();
|
||||
let watcher = Watcher::new(tx.clone(), &paths, args.poll, args.poll_interval).or_else(|err| {
|
||||
if poll {
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
use nix::libc;
|
||||
let mut fallback = false;
|
||||
if let notify::Error::Io(ref e) = err {
|
||||
if e.raw_os_error() == Some(libc::ENOSPC) {
|
||||
warn!("System notification limit is too small, falling back to polling mode. For better performance increase system limit:\n\tsysctl fs.inotify.max_user_watches=524288");
|
||||
fallback = true;
|
||||
}
|
||||
}
|
||||
|
||||
if fallback {
|
||||
return Watcher::new(tx, &paths, true, poll_interval);
|
||||
}
|
||||
Watcher::new(tx, &paths, true, args.poll_interval)
|
||||
.expect("polling watcher should always work")
|
||||
}
|
||||
Err(e) => {
|
||||
panic!("Error setting up watcher: {}", e);
|
||||
}
|
||||
};
|
||||
|
||||
Err(err)
|
||||
})?;
|
||||
|
||||
if watcher.is_polling() {
|
||||
warn!("Polling for changes every {} ms", args.poll_interval);
|
||||
|
@ -205,6 +180,7 @@ pub fn run(args: cli::Args) -> Result<()> {
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -31,17 +31,17 @@ impl Watcher {
|
|||
use notify::Watcher;
|
||||
|
||||
let imp = if poll {
|
||||
let mut watcher = try!(PollWatcher::with_delay_ms(tx, interval_ms));
|
||||
let mut watcher = PollWatcher::with_delay_ms(tx, interval_ms)?;
|
||||
for path in paths {
|
||||
try!(watcher.watch(path, RecursiveMode::Recursive));
|
||||
watcher.watch(path, RecursiveMode::Recursive)?;
|
||||
debug!("Watching {:?}", path);
|
||||
}
|
||||
|
||||
WatcherImpl::Poll(watcher)
|
||||
} else {
|
||||
let mut watcher = try!(raw_watcher(tx));
|
||||
let mut watcher = raw_watcher(tx)?;
|
||||
for path in paths {
|
||||
try!(watcher.watch(path, RecursiveMode::Recursive));
|
||||
watcher.watch(path, RecursiveMode::Recursive)?;
|
||||
debug!("Watching {:?}", path);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue