Formatting

This commit is contained in:
Félix Saparelli 2018-09-08 20:08:36 +12:00
parent 9c60148b66
commit a6163cc599
10 changed files with 263 additions and 181 deletions

View File

@ -135,24 +135,26 @@ pub fn get_args() -> Args {
if let Some(extensions) = args.values_of("extensions") {
for exts in extensions {
filters.extend(exts.split(',')
.filter(|ext| !ext.is_empty())
.map(|ext| format!("*.{}", ext.replace(".", ""))));
filters.extend(
exts.split(',')
.filter(|ext| !ext.is_empty())
.map(|ext| format!("*.{}", ext.replace(".", ""))),
);
}
}
let mut ignores = vec![];
let default_ignores = vec![format!("**{}.DS_Store", MAIN_SEPARATOR),
String::from("*.py[co]"),
String::from("#*#"),
String::from(".#*"),
String::from(".*.sw?"),
String::from(".*.sw?x"),
format!("**{}.git{}**", MAIN_SEPARATOR, MAIN_SEPARATOR),
format!("**{}.hg{}**", MAIN_SEPARATOR, MAIN_SEPARATOR),
format!("**{}.svn{}**", MAIN_SEPARATOR, MAIN_SEPARATOR)];
let default_ignores = vec![
format!("**{}.DS_Store", MAIN_SEPARATOR),
String::from("*.py[co]"),
String::from("#*#"),
String::from(".#*"),
String::from(".*.sw?"),
String::from(".*.sw?x"),
format!("**{}.git{}**", MAIN_SEPARATOR, MAIN_SEPARATOR),
format!("**{}.hg{}**", MAIN_SEPARATOR, MAIN_SEPARATOR),
format!("**{}.svn{}**", MAIN_SEPARATOR, MAIN_SEPARATOR),
];
if args.occurrences_of("no-default-ignore") == 0 {
ignores.extend(default_ignores)
@ -167,9 +169,9 @@ pub fn get_args() -> Args {
let debounce = if args.occurrences_of("debounce") > 0 {
value_t!(args.value_of("debounce"), u64).unwrap_or_else(|e| e.exit())
} else {
500
};
} else {
500
};
if signal.is_some() && args.is_present("postpone") {
// TODO: Error::argument_conflict() might be the better fit, usage was unclear, though

View File

@ -88,7 +88,8 @@ impl Gitignore {
}
pub fn is_excluded(&self, path: &Path) -> bool {
let mut applicable_files: Vec<&GitignoreFile> = self.files
let mut applicable_files: Vec<&GitignoreFile> = self
.files
.iter()
.filter(|f| path.starts_with(&f.root))
.collect();
@ -144,11 +145,10 @@ impl GitignoreFile {
}
Ok(GitignoreFile {
set: try!(builder.build()),
patterns: patterns,
root: root.to_owned(),
})
set: try!(builder.build()),
patterns: patterns,
root: root.to_owned(),
})
}
#[cfg(test)]
@ -167,9 +167,9 @@ impl GitignoreFile {
for &i in matches.iter().rev() {
let pattern = &self.patterns[i];
return match pattern.pattern_type {
PatternType::Whitelist => MatchResult::Whitelist,
PatternType::Ignore => MatchResult::Ignore,
};
PatternType::Whitelist => MatchResult::Whitelist,
PatternType::Ignore => MatchResult::Ignore,
};
}
MatchResult::None
@ -223,7 +223,6 @@ impl Pattern {
}
}
impl From<globset::Error> for Error {
fn from(error: globset::Error) -> Error {
Error::GlobSet(error)
@ -236,7 +235,6 @@ impl From<io::Error> for Error {
}
}
#[cfg(test)]
mod tests {
use super::GitignoreFile;

View File

@ -1,19 +1,19 @@
#[macro_use]
extern crate clap;
extern crate globset;
extern crate env_logger;
extern crate globset;
#[macro_use]
extern crate log;
#[macro_use]
extern crate lazy_static;
extern crate notify;
#[cfg(windows)]
extern crate kernel32;
#[cfg(unix)]
extern crate nix;
#[cfg(windows)]
extern crate winapi;
#[cfg(windows)]
extern crate kernel32;
#[cfg(test)]
extern crate mktemp;
@ -21,10 +21,10 @@ extern crate mktemp;
pub mod cli;
mod gitignore;
mod notification_filter;
mod pathop;
mod process;
pub mod run;
mod signal;
mod watcher;
mod pathop;
pub use run::run;

View File

@ -1,7 +1,6 @@
extern crate watchexec;
use watchexec::{cli, run};
use std::error::Error;
fn main() -> Result<(), Box<Error>> {
fn main() -> run::Result<()> {
run(cli::get_args())
}

View File

@ -22,10 +22,11 @@ pub enum Error {
}
impl NotificationFilter {
pub fn new(filters: Vec<String>,
ignores: Vec<String>,
ignore_files: gitignore::Gitignore)
-> Result<NotificationFilter, Error> {
pub fn new(
filters: Vec<String>,
ignores: Vec<String>,
ignore_files: gitignore::Gitignore,
) -> Result<NotificationFilter, Error> {
let mut filter_set_builder = GlobSetBuilder::new();
for f in &filters {
filter_set_builder.add(try!(Glob::new(f)));
@ -49,11 +50,11 @@ impl NotificationFilter {
let ignore_set = try!(ignore_set_builder.build());
Ok(NotificationFilter {
filters: filter_set,
filter_count: filters.len(),
ignores: ignore_set,
ignore_files: ignore_files,
})
filters: filter_set,
filter_count: filters.len(),
ignores: ignore_set,
ignore_files: ignore_files,
})
}
pub fn is_excluded(&self, path: &Path) -> bool {
@ -93,8 +94,8 @@ impl From<globset::Error> for Error {
#[cfg(test)]
mod tests {
use gitignore;
use super::NotificationFilter;
use gitignore;
use std::path::Path;
#[test]
@ -106,7 +107,11 @@ mod tests {
#[test]
fn test_filename() {
let filter = NotificationFilter::new(vec![], vec![String::from("test.json")], gitignore::load(&vec![])).unwrap();
let filter = NotificationFilter::new(
vec![],
vec![String::from("test.json")],
gitignore::load(&vec![]),
).unwrap();
assert!(filter.is_excluded(&Path::new("/path/to/test.json")));
assert!(filter.is_excluded(&Path::new("test.json")));
@ -135,8 +140,8 @@ mod tests {
#[test]
fn test_ignores_take_precedence() {
let ignores = vec![String::from("*.rs"), String::from("*.toml")];
let filter = NotificationFilter::new(ignores.clone(), ignores, gitignore::load(&vec![]))
.unwrap();
let filter =
NotificationFilter::new(ignores.clone(), ignores, gitignore::load(&vec![])).unwrap();
assert!(filter.is_excluded(&Path::new("hello.rs")));
assert!(filter.is_excluded(&Path::new("Cargo.toml")));

View File

@ -1,7 +1,6 @@
use notify::op;
use std::path::{Path, PathBuf};
/// Info about a path and its corresponding `notify` event
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct PathOp {
@ -41,4 +40,3 @@ impl PathOp {
op_.contains(op::CHMOD)
}
}

View File

@ -1,6 +1,6 @@
use std::path::PathBuf;
use std::collections::{HashMap, HashSet};
use pathop::PathOp;
use std::collections::{HashMap, HashSet};
use std::path::PathBuf;
pub fn spawn(cmd: &Vec<String>, updated_paths: Vec<PathOp>, no_shell: bool) -> Process {
self::imp::Process::new(cmd, updated_paths, no_shell).expect("unable to spawn process")
@ -11,48 +11,56 @@ pub use self::imp::Process;
fn needs_wrapping(s: &String) -> bool {
s.contains(|ch| match ch {
' ' | '\t' | '\'' | '"' => true,
_ => false
_ => false,
})
}
#[cfg(target_family = "unix")]
fn wrap_in_quotes(s: &String) -> String {
format!("'{}'", if s.contains('\'') {
s.replace('\'', "'\"'\"'")
} else {
s.clone()
})
format!(
"'{}'",
if s.contains('\'') {
s.replace('\'', "'\"'\"'")
} else {
s.clone()
}
)
}
#[cfg(target_family = "windows")]
fn wrap_in_quotes(s: &String) -> String {
format!("\"{}\"", if s.contains('"') {
s.replace('"', "\"\"")
} else {
s.clone()
})
format!(
"\"{}\"",
if s.contains('"') {
s.replace('"', "\"\"")
} else {
s.clone()
}
)
}
fn wrap_commands(cmd: &Vec<String>) -> Vec<String> {
cmd.iter().map(|fragment| {
if needs_wrapping(fragment) {
wrap_in_quotes(fragment)
} else {
fragment.clone()
}
}).collect()
cmd.iter()
.map(|fragment| {
if needs_wrapping(fragment) {
wrap_in_quotes(fragment)
} else {
fragment.clone()
}
})
.collect()
}
#[cfg(target_family = "unix")]
mod imp {
use nix::{self, Error};
use super::wrap_commands;
use nix::libc::*;
use nix::{self, Error};
use pathop::PathOp;
use signal::Signal;
use std::io::{self, Result};
use std::process::Command;
use std::sync::*;
use super::wrap_commands;
use signal::Signal;
use pathop::PathOp;
pub struct Process {
pgid: pid_t,
@ -71,7 +79,11 @@ mod imp {
#[allow(unknown_lints)]
#[allow(mutex_atomic)]
impl Process {
pub fn new(cmd: &Vec<String>, updated_paths: Vec<PathOp>, no_shell: bool) -> Result<Process> {
pub fn new(
cmd: &Vec<String>,
updated_paths: Vec<PathOp>,
no_shell: bool,
) -> Result<Process> {
use nix::unistd::*;
use std::os::unix::process::CommandExt;
@ -99,16 +111,15 @@ mod imp {
}
command
.before_exec(|| setpgid(Pid::from_raw(0), Pid::from_raw(0))
.map_err(from_nix_error))
.before_exec(|| setpgid(Pid::from_raw(0), Pid::from_raw(0)).map_err(from_nix_error))
.spawn()
.and_then(|p| {
Ok(Process {
pgid: p.id() as i32,
lock: Mutex::new(false),
cvar: Condvar::new(),
})
})
Ok(Process {
pgid: p.id() as i32,
lock: Mutex::new(false),
cvar: Condvar::new(),
})
})
}
pub fn reap(&self) {
@ -118,8 +129,9 @@ mod imp {
let mut finished = true;
loop {
match waitpid(Pid::from_raw(-self.pgid), Some(WaitPidFlag::WNOHANG)) {
Ok(WaitStatus::Exited(_, _)) |
Ok(WaitStatus::Signaled(_, _, _)) => finished = finished && true,
Ok(WaitStatus::Exited(_, _)) | Ok(WaitStatus::Signaled(_, _, _)) => {
finished = finished && true
}
Ok(_) => {
finished = false;
break;
@ -151,7 +163,6 @@ mod imp {
unsafe {
killpg(self.pgid, sig);
}
}
pub fn wait(&self) {
@ -165,16 +176,16 @@ mod imp {
#[cfg(target_family = "windows")]
mod imp {
use super::wrap_commands;
use kernel32::*;
use pathop::PathOp;
use signal::Signal;
use std::io;
use std::io::Result;
use std::mem;
use std::process::Command;
use std::ptr;
use super::wrap_commands;
use kernel32::*;
use winapi::*;
use signal::Signal;
use pathop::PathOp;
pub struct Process {
job: HANDLE,
@ -188,7 +199,11 @@ mod imp {
}
impl Process {
pub fn new(cmd: &Vec<String>, updated_paths: Vec<PathOp>, no_shell: bool) -> Result<Process> {
pub fn new(
cmd: &Vec<String>,
updated_paths: Vec<PathOp>,
no_shell: bool,
) -> Result<Process> {
use std::os::windows::io::IntoRawHandle;
use std::os::windows::process::CommandExt;
@ -201,31 +216,43 @@ mod imp {
panic!("failed to create job object: {}", last_err());
}
let completion_port = unsafe { CreateIoCompletionPort(INVALID_HANDLE_VALUE, ptr::null_mut(), 0, 1) };
let completion_port =
unsafe { CreateIoCompletionPort(INVALID_HANDLE_VALUE, ptr::null_mut(), 0, 1) };
if job.is_null() {
panic!("unable to create IO completion port for job: {}", last_err());
panic!(
"unable to create IO completion port for job: {}",
last_err()
);
}
let mut associate_completion: JOBOBJECT_ASSOCIATE_COMPLETION_PORT = unsafe { mem::zeroed() };
let mut associate_completion: JOBOBJECT_ASSOCIATE_COMPLETION_PORT =
unsafe { mem::zeroed() };
associate_completion.completion_key = job;
associate_completion.completion_port = completion_port;
unsafe {
let r = SetInformationJobObject(job,
JobObjectAssociateCompletionPortInformation,
&mut associate_completion as *mut _ as LPVOID,
mem::size_of_val(&associate_completion) as DWORD);
let r = SetInformationJobObject(
job,
JobObjectAssociateCompletionPortInformation,
&mut associate_completion as *mut _ as LPVOID,
mem::size_of_val(&associate_completion) as DWORD,
);
if r == 0 {
panic!("failed to associate completion port with job: {}", last_err());
panic!(
"failed to associate completion port with job: {}",
last_err()
);
}
}
let mut info: JOBOBJECT_EXTENDED_LIMIT_INFORMATION = unsafe { mem::zeroed() };
info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
let r = unsafe {
SetInformationJobObject(job,
JobObjectExtendedLimitInformation,
&mut info as *mut _ as LPVOID,
mem::size_of_val(&info) as DWORD)
SetInformationJobObject(
job,
JobObjectExtendedLimitInformation,
&mut info as *mut _ as LPVOID,
mem::size_of_val(&info) as DWORD,
)
};
if r == 0 {
panic!("failed to set job info: {}", last_err());
@ -250,19 +277,20 @@ mod imp {
command.env(name, val);
}
command
.spawn()
.and_then(|p| {
let handle = p.into_raw_handle();
let r = unsafe { AssignProcessToJobObject(job, handle) };
if r == 0 {
panic!("failed to add to job object: {}", last_err());
}
command.spawn().and_then(|p| {
let handle = p.into_raw_handle();
let r = unsafe { AssignProcessToJobObject(job, handle) };
if r == 0 {
panic!("failed to add to job object: {}", last_err());
}
resume_threads(handle);
resume_threads(handle);
Ok(Process { job: job, completion_port: completion_port })
})
Ok(Process {
job: job,
completion_port: completion_port,
})
})
}
pub fn reap(&self) {}
@ -279,7 +307,13 @@ mod imp {
let mut code: DWORD = 0;
let mut key: ULONG_PTR = 0;
let mut overlapped: LPOVERLAPPED = mem::uninitialized();
GetQueuedCompletionStatus(self.completion_port, &mut code, &mut key, &mut overlapped, INFINITE);
GetQueuedCompletionStatus(
self.completion_port,
&mut code,
&mut key,
&mut overlapped,
INFINITE,
);
if code == JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO && (key as HANDLE) == self.job {
break;
@ -307,7 +341,15 @@ mod imp {
let child_id = GetProcessId(child_process);
let h = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
let mut entry = THREADENTRY32 { dwSize: 28, cntUsage: 0, th32ThreadID: 0, th32OwnerProcessID: 0, tpBasePri: 0, tpDeltaPri: 0, dwFlags: 0};
let mut entry = THREADENTRY32 {
dwSize: 28,
cntUsage: 0,
th32ThreadID: 0,
th32OwnerProcessID: 0,
tpBasePri: 0,
tpDeltaPri: 0,
dwFlags: 0,
};
let mut result = Thread32First(h, &mut entry);
while result != 0 {
@ -325,7 +367,6 @@ mod imp {
}
}
/// Collect `PathOp` details into op-categories to pass onto the exec'd command as env-vars
///
/// WRITTEN -> `notify::ops::WRITE`, `notify::ops::CLOSE_WRITE`
@ -339,11 +380,13 @@ fn collect_path_env_vars(pathops: &[PathOp]) -> Vec<(String, String)> {
#[cfg(not(target_family = "unix"))]
const ENV_SEP: &'static str = ";";
let mut by_op = HashMap::new(); // Paths as `String`s collected by `notify::op`
let mut all_pathbufs = HashSet::new(); // All unique `PathBuf`s
let mut by_op = HashMap::new(); // Paths as `String`s collected by `notify::op`
let mut all_pathbufs = HashSet::new(); // All unique `PathBuf`s
for pathop in pathops {
if let Some(op) = pathop.op { // ignore pathops that don't have a `notify::op` set
if let Some(s) = pathop.path.to_str() { // ignore invalid utf8 paths
if let Some(op) = pathop.op {
// ignore pathops that don't have a `notify::op` set
if let Some(s) = pathop.path.to_str() {
// ignore invalid utf8 paths
all_pathbufs.insert(pathop.path.clone());
let e = by_op.entry(op).or_insert_with(Vec::new);
e.push(s.to_owned());
@ -358,29 +401,35 @@ fn collect_path_env_vars(pathops: &[PathOp]) -> Vec<(String, String)> {
let common_path = if all_pathbufs.len() > 1 {
let all_pathbufs: Vec<PathBuf> = all_pathbufs.into_iter().collect();
get_longest_common_path(&all_pathbufs)
} else { None };
} else {
None
};
if let Some(ref common_path) = common_path {
vars.push(("WATCHEXEC_COMMON_PATH".to_string(), common_path.to_string()));
}
for (op, paths) in by_op {
let key = match op {
op if PathOp::is_create(op) => "WATCHEXEC_CREATED_PATH",
op if PathOp::is_remove(op) => "WATCHEXEC_REMOVED_PATH",
op if PathOp::is_rename(op) => "WATCHEXEC_RENAMED_PATH",
op if PathOp::is_write(op) => "WATCHEXEC_WRITTEN_PATH",
op if PathOp::is_meta(op) => "WATCHEXEC_META_CHANGED_PATH",
_ => continue, // ignore `notify::op::RESCAN`s
op if PathOp::is_create(op) => "WATCHEXEC_CREATED_PATH",
op if PathOp::is_remove(op) => "WATCHEXEC_REMOVED_PATH",
op if PathOp::is_rename(op) => "WATCHEXEC_RENAMED_PATH",
op if PathOp::is_write(op) => "WATCHEXEC_WRITTEN_PATH",
op if PathOp::is_meta(op) => "WATCHEXEC_META_CHANGED_PATH",
_ => continue, // ignore `notify::op::RESCAN`s
};
let paths = if let Some(ref common_path) = common_path {
paths.iter().map(|path_str| path_str.trim_left_matches(common_path).to_string()).collect::<Vec<_>>()
} else { paths };
paths
.iter()
.map(|path_str| path_str.trim_left_matches(common_path).to_string())
.collect::<Vec<_>>()
} else {
paths
};
vars.push((key.to_string(), paths.as_slice().join(ENV_SEP)));
}
vars
}
fn get_longest_common_path(paths: &[PathBuf]) -> Option<String> {
match paths.len() {
0 => return None,
@ -413,18 +462,17 @@ fn get_longest_common_path(paths: &[PathBuf]) -> Option<String> {
result.to_str().map(|ref_val| ref_val.to_string())
}
#[cfg(test)]
#[cfg(target_family = "unix")]
mod tests {
use std::path::PathBuf;
use std::collections::HashSet;
use notify;
use pathop::PathOp;
use std::collections::HashSet;
use std::path::PathBuf;
use super::spawn;
use super::get_longest_common_path;
use super::collect_path_env_vars;
use super::get_longest_common_path;
use super::spawn;
use super::wrap_commands;
#[test]
@ -478,23 +526,26 @@ mod tests {
let single_result = get_longest_common_path(&single_path).unwrap();
assert_eq!(single_result, "/tmp/random/");
let common_paths = vec![PathBuf::from("/tmp/logs/hi"),
PathBuf::from("/tmp/logs/bye"),
PathBuf::from("/tmp/logs/bye"),
PathBuf::from("/tmp/logs/fly")];
let common_paths = vec![
PathBuf::from("/tmp/logs/hi"),
PathBuf::from("/tmp/logs/bye"),
PathBuf::from("/tmp/logs/bye"),
PathBuf::from("/tmp/logs/fly"),
];
let common_result = get_longest_common_path(&common_paths).unwrap();
assert_eq!(common_result, "/tmp/logs");
let diverging_paths = vec![PathBuf::from("/tmp/logs/hi"), PathBuf::from("/var/logs/hi")];
let diverging_result = get_longest_common_path(&diverging_paths).unwrap();
assert_eq!(diverging_result, "/");
let uneven_paths = vec![PathBuf::from("/tmp/logs/hi"),
PathBuf::from("/tmp/logs/"),
PathBuf::from("/tmp/logs/bye")];
let uneven_paths = vec![
PathBuf::from("/tmp/logs/hi"),
PathBuf::from("/tmp/logs/"),
PathBuf::from("/tmp/logs/bye"),
];
let uneven_result = get_longest_common_path(&uneven_paths).unwrap();
assert_eq!(uneven_result, "/tmp/logs");
@ -503,17 +554,35 @@ mod tests {
#[test]
fn pathops_collect_to_env_vars() {
let pathops = vec![
PathOp::new(&PathBuf::from("/tmp/logs/hi"), Some(notify::op::CREATE), None),
PathOp::new(&PathBuf::from("/tmp/logs/hey/there"), Some(notify::op::CREATE), None),
PathOp::new(&PathBuf::from("/tmp/logs/bye"), Some(notify::op::REMOVE), None),
PathOp::new(
&PathBuf::from("/tmp/logs/hi"),
Some(notify::op::CREATE),
None,
),
PathOp::new(
&PathBuf::from("/tmp/logs/hey/there"),
Some(notify::op::CREATE),
None,
),
PathOp::new(
&PathBuf::from("/tmp/logs/bye"),
Some(notify::op::REMOVE),
None,
),
];
let expected_vars = vec![
("WATCHEXEC_COMMON_PATH".to_string(), "/tmp/logs".to_string()),
("WATCHEXEC_REMOVED_PATH".to_string(), "/bye".to_string()),
("WATCHEXEC_CREATED_PATH".to_string(), "/hi:/hey/there".to_string()),
(
"WATCHEXEC_CREATED_PATH".to_string(),
"/hi:/hey/there".to_string(),
),
];
let vars = collect_path_env_vars(&pathops);
assert_eq!(vars.iter().collect::<HashSet<_>>(), expected_vars.iter().collect::<HashSet<_>>());
assert_eq!(
vars.iter().collect::<HashSet<_>>(),
expected_vars.iter().collect::<HashSet<_>>()
);
}
}
@ -568,4 +637,3 @@ mod tests {
);
}
}

View File

@ -1,22 +1,22 @@
use std::collections::HashMap;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::sync::{Arc, RwLock};
use std::sync::mpsc::{channel, Receiver};
use std::sync::{Arc, RwLock};
use std::time::Duration;
use cli;
use env_logger;
use gitignore;
use log;
use notify::Error;
use notification_filter::NotificationFilter;
use notify::Error;
use pathop::PathOp;
use process::{self, Process};
use signal::{self, Signal};
use watcher::{Event, Watcher};
use pathop::PathOp;
type Result<T> = ::std::result::Result<T, Box<::std::error::Error>>;
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();
@ -68,14 +68,15 @@ pub fn run(args: cli::Args) -> Result<()> {
init_logger(args.debug);
let paths: Result<Vec<PathBuf>> = args.paths
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())
})
Ok(Path::new(&p)
.canonicalize()
.map_err(|e| format!("Unable to canonicalize path: \"{}\", {}", p, e))?
.to_owned())
})
.collect();
let paths = paths?;
@ -92,11 +93,15 @@ pub fn run(args: cli::Args) -> Result<()> {
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");
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"
);
}
Watcher::new(tx, &paths, true, args.poll_interval)
.expect("polling watcher should always work")

View File

@ -72,11 +72,12 @@ pub fn new(signal_name: Option<String>) -> Option<Signal> {
#[cfg(unix)]
pub fn install_handler<F>(handler: F)
where F: Fn(self::Signal) + 'static + Send + Sync
where
F: Fn(self::Signal) + 'static + Send + Sync,
{
use std::thread;
use nix::libc::c_int;
use nix::sys::signal::*;
use std::thread;
// Mask all signals interesting to us. The mask propagates
// to all threads started after this point.
@ -90,8 +91,7 @@ pub fn install_handler<F>(handler: F)
mask.add(SIGCHLD);
mask.add(SIGUSR1);
mask.add(SIGUSR2);
mask.thread_set_mask()
.expect("unable to set signal mask");
mask.thread_set_mask().expect("unable to set signal mask");
set_handler(handler);
@ -99,10 +99,14 @@ pub fn install_handler<F>(handler: F)
pub extern "C" fn sigchld_handler(_: c_int) {}
unsafe {
let _ = sigaction(SIGCHLD,
&SigAction::new(SigHandler::Handler(sigchld_handler),
SaFlags::empty(),
SigSet::empty()));
let _ = sigaction(
SIGCHLD,
&SigAction::new(
SigHandler::Handler(sigchld_handler),
SaFlags::empty(),
SigSet::empty(),
),
);
}
// Spawn a thread to catch these signals
@ -137,7 +141,8 @@ pub fn install_handler<F>(handler: F)
#[cfg(windows)]
pub fn install_handler<F>(handler: F)
where F: Fn(self::Signal) + 'static + Send + Sync
where
F: Fn(self::Signal) + 'static + Send + Sync,
{
use kernel32::SetConsoleCtrlHandler;
use winapi::{BOOL, DWORD, FALSE, TRUE};
@ -162,7 +167,8 @@ fn invoke(sig: self::Signal) {
}
fn set_handler<F>(handler: F)
where F: Fn(self::Signal) + 'static + Send + Sync
where
F: Fn(self::Signal) + 'static + Send + Sync,
{
*CLEANUP.lock().unwrap() = Some(Box::new(handler));
}

View File

@ -1,7 +1,7 @@
use std::path::PathBuf;
use std::sync::mpsc::Sender;
use notify::{PollWatcher, RecommendedWatcher, RecursiveMode, raw_watcher};
use notify::{raw_watcher, PollWatcher, RecommendedWatcher, RecursiveMode};
/// Thin wrapper over the notify crate
///
@ -13,8 +13,8 @@ pub struct Watcher {
watcher_impl: WatcherImpl,
}
pub use notify::RawEvent as Event;
pub use notify::Error;
pub use notify::RawEvent as Event;
enum WatcherImpl {
Recommended(RecommendedWatcher),
@ -22,11 +22,12 @@ enum WatcherImpl {
}
impl Watcher {
pub fn new(tx: Sender<Event>,
paths: &[PathBuf],
poll: bool,
interval_ms: u32)
-> Result<Watcher, Error> {
pub fn new(
tx: Sender<Event>,
paths: &[PathBuf],
poll: bool,
interval_ms: u32,
) -> Result<Watcher, Error> {
use notify::Watcher;
let imp = if poll {