mirror of
https://github.com/watchexec/watchexec.git
synced 2024-09-28 22:21:33 +02:00
[api] Make watchexec take a Handler rather than a callback
Instead of special-casing the callback, which is the path least-taken, switch the internals to a Handler model, where the default behaviour is an implementation of a Handler, and external callers can implement their own Handlers and pass them in. While doing so, change all unwraps in run::run to returning Errs, and expand the watchexec Error enum to accommodate. That should make it easier to use as a library. Also, differentiate between "manual" and "on update" runs. For now the only manual run is the initial run, but this paves the way for e.g. keyboard- or signal- triggered runs.
This commit is contained in:
parent
ac3a4f0717
commit
aae5a216b0
56
src/cli.rs
56
src/cli.rs
@ -1,12 +1,11 @@
|
||||
use std::path::MAIN_SEPARATOR;
|
||||
use std::process::Command;
|
||||
use clap::{App, Arg, Error};
|
||||
use error;
|
||||
use std::{ffi::OsString, fs::canonicalize, path::{MAIN_SEPARATOR, PathBuf}, process::Command};
|
||||
|
||||
use clap::{App, Arg, Error, ArgMatches};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Args {
|
||||
pub cmd: Vec<String>,
|
||||
pub paths: Vec<String>,
|
||||
pub paths: Vec<PathBuf>,
|
||||
pub filters: Vec<String>,
|
||||
pub ignores: Vec<String>,
|
||||
pub clear_screen: bool,
|
||||
@ -32,7 +31,19 @@ pub fn clear_screen() {
|
||||
let _ = Command::new("tput").arg("reset").status();
|
||||
}
|
||||
|
||||
pub fn init_app<'a, 'b>() -> App<'a, 'b> {
|
||||
pub fn get_args() -> error::Result<Args> {
|
||||
get_args_impl(None::<&[&str]>)
|
||||
}
|
||||
|
||||
pub fn get_args_from<I, T>(from: I) -> error::Result<Args>
|
||||
where I: IntoIterator<Item=T>, T: Into<OsString> + Clone
|
||||
{
|
||||
get_args_impl(Some(from))
|
||||
}
|
||||
|
||||
fn get_args_impl<I, T>(from: Option<I>) -> error::Result<Args>
|
||||
where I: IntoIterator<Item=T>, T: Into<OsString> + Clone
|
||||
{
|
||||
let app = App::new("watchexec")
|
||||
.version(crate_version!())
|
||||
.about("Execute commands when watched files change")
|
||||
@ -116,12 +127,18 @@ pub fn init_app<'a, 'b>() -> App<'a, 'b> {
|
||||
.short("n")
|
||||
.long("no-shell"))
|
||||
.arg(Arg::with_name("once").short("1").hidden(true));
|
||||
app
|
||||
}
|
||||
|
||||
pub fn process_args(args: ArgMatches) -> Args{
|
||||
let cmd: Vec<String> = values_t!(args.values_of("command"), String).unwrap();
|
||||
let paths = values_t!(args.values_of("path"), String).unwrap_or(vec![String::from(".")]);
|
||||
let args = match from {
|
||||
None => app.get_matches(),
|
||||
Some(i) => app.get_matches_from(i)
|
||||
};
|
||||
|
||||
let cmd: Vec<String> = values_t!(args.values_of("command"), String)?;
|
||||
let str_paths = values_t!(args.values_of("path"), String).unwrap_or(vec![".".into()]);
|
||||
let mut paths = vec![];
|
||||
for path in str_paths {
|
||||
paths.push(canonicalize(&path).map_err(|e| error::Error::Canonicalization(path, e))?);
|
||||
}
|
||||
|
||||
// Treat --kill as --signal SIGKILL (for compatibility with older syntax)
|
||||
let signal = if args.is_present("kill") {
|
||||
@ -131,7 +148,7 @@ pub fn process_args(args: ArgMatches) -> Args{
|
||||
args.value_of("signal").map(str::to_string)
|
||||
};
|
||||
|
||||
let mut filters = values_t!(args.values_of("filter"), String).unwrap_or(vec![]);
|
||||
let mut filters = values_t!(args.values_of("filter"), String).unwrap_or(Vec::new());
|
||||
|
||||
if let Some(extensions) = args.values_of("extensions") {
|
||||
for exts in extensions {
|
||||
@ -159,7 +176,7 @@ pub fn process_args(args: ArgMatches) -> Args{
|
||||
if args.occurrences_of("no-default-ignore") == 0 {
|
||||
ignores.extend(default_ignores)
|
||||
};
|
||||
ignores.extend(values_t!(args.values_of("ignore"), String).unwrap_or(vec![]));
|
||||
ignores.extend(values_t!(args.values_of("ignore"), String).unwrap_or(Vec::new()));
|
||||
|
||||
let poll_interval = if args.occurrences_of("poll") > 0 {
|
||||
value_t!(args.value_of("poll"), u32).unwrap_or_else(|e| e.exit())
|
||||
@ -185,7 +202,7 @@ pub fn process_args(args: ArgMatches) -> Args{
|
||||
.exit();
|
||||
}
|
||||
|
||||
Args {
|
||||
Ok(Args {
|
||||
cmd: cmd,
|
||||
paths: paths,
|
||||
filters: filters,
|
||||
@ -201,12 +218,5 @@ pub fn process_args(args: ArgMatches) -> Args{
|
||||
once: args.is_present("once"),
|
||||
poll: args.occurrences_of("poll") > 0,
|
||||
poll_interval: poll_interval,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unknown_lints)]
|
||||
#[allow(or_fun_call)]
|
||||
pub fn get_args() -> Args {
|
||||
let args = init_app().get_matches();
|
||||
process_args(args)
|
||||
})
|
||||
}
|
19
src/error.rs
19
src/error.rs
@ -1,14 +1,17 @@
|
||||
use clap;
|
||||
use globset;
|
||||
use notify;
|
||||
use std::{error::Error as StdError, fmt, io};
|
||||
use std::{error::Error as StdError, fmt, io, sync::PoisonError};
|
||||
|
||||
pub type Result<T> = ::std::result::Result<T, Error>;
|
||||
|
||||
pub enum Error {
|
||||
Canonicalization(String, io::Error),
|
||||
Clap(clap::Error),
|
||||
Glob(globset::Error),
|
||||
Io(io::Error),
|
||||
Notify(notify::Error),
|
||||
PoisonedLock,
|
||||
}
|
||||
|
||||
impl StdError for Error {
|
||||
@ -19,6 +22,12 @@ impl StdError for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<clap::Error> for Error {
|
||||
fn from(err: clap::Error) -> Self {
|
||||
Error::Clap(err)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<globset::Error> for Error {
|
||||
fn from(err: globset::Error) -> Self {
|
||||
Error::Glob(err)
|
||||
@ -40,6 +49,12 @@ impl From<notify::Error> for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> From<PoisonError<T>> for Error {
|
||||
fn from(_err: PoisonError<T>) -> Self {
|
||||
Error::PoisonedLock
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
@ -47,9 +62,11 @@ impl fmt::Display for Error {
|
||||
"{} error: {}",
|
||||
match self {
|
||||
Error::Canonicalization(_, _) => "Path",
|
||||
Error::Clap(_) => "Argument",
|
||||
Error::Glob(_) => "Globset",
|
||||
Error::Io(_) => "I/O",
|
||||
Error::Notify(_) => "Notify",
|
||||
Error::PoisonedLock => "Internal",
|
||||
},
|
||||
match self {
|
||||
Error::Canonicalization(path, err) => {
|
||||
|
@ -2,5 +2,5 @@ extern crate watchexec;
|
||||
use watchexec::{cli, error, run};
|
||||
|
||||
fn main() -> error::Result<()> {
|
||||
run(cli::get_args(), None::<fn(_)>)
|
||||
run(cli::get_args()?)
|
||||
}
|
||||
|
@ -14,18 +14,18 @@ pub struct NotificationFilter {
|
||||
|
||||
impl NotificationFilter {
|
||||
pub fn new(
|
||||
filters: Vec<String>,
|
||||
ignores: Vec<String>,
|
||||
filters: &[String],
|
||||
ignores: &[String],
|
||||
ignore_files: Gitignore,
|
||||
) -> error::Result<NotificationFilter> {
|
||||
let mut filter_set_builder = GlobSetBuilder::new();
|
||||
for f in &filters {
|
||||
for f in filters {
|
||||
filter_set_builder.add(Glob::new(f)?);
|
||||
debug!("Adding filter: \"{}\"", f);
|
||||
}
|
||||
|
||||
let mut ignore_set_builder = GlobSetBuilder::new();
|
||||
for i in &ignores {
|
||||
for i in ignores {
|
||||
let mut ignore_path = Path::new(i).to_path_buf();
|
||||
if ignore_path.is_relative() && !i.starts_with("*") {
|
||||
ignore_path = Path::new("**").join(&ignore_path);
|
||||
@ -74,7 +74,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_allows_everything_by_default() {
|
||||
let filter = NotificationFilter::new(vec![], vec![], gitignore::load(&vec![])).unwrap();
|
||||
let filter = NotificationFilter::new(&[], &[], gitignore::load(&[])).unwrap();
|
||||
|
||||
assert!(!filter.is_excluded(&Path::new("foo")));
|
||||
}
|
||||
@ -82,9 +82,9 @@ mod tests {
|
||||
#[test]
|
||||
fn test_filename() {
|
||||
let filter = NotificationFilter::new(
|
||||
vec![],
|
||||
vec![String::from("test.json")],
|
||||
gitignore::load(&vec![]),
|
||||
&[],
|
||||
&["test.json".into()],
|
||||
gitignore::load(&[]),
|
||||
).unwrap();
|
||||
|
||||
assert!(filter.is_excluded(&Path::new("/path/to/test.json")));
|
||||
@ -93,8 +93,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_multiple_filters() {
|
||||
let filters = vec![String::from("*.rs"), String::from("*.toml")];
|
||||
let filter = NotificationFilter::new(filters, vec![], gitignore::load(&vec![])).unwrap();
|
||||
let filters = &["*.rs".into(), "*.toml".into()];
|
||||
let filter = NotificationFilter::new(filters, &[], gitignore::load(&[])).unwrap();
|
||||
|
||||
assert!(!filter.is_excluded(&Path::new("hello.rs")));
|
||||
assert!(!filter.is_excluded(&Path::new("Cargo.toml")));
|
||||
@ -103,8 +103,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_multiple_ignores() {
|
||||
let ignores = vec![String::from("*.rs"), String::from("*.toml")];
|
||||
let filter = NotificationFilter::new(vec![], ignores, gitignore::load(&vec![])).unwrap();
|
||||
let ignores = &["*.rs".into(), "*.toml".into()];
|
||||
let filter = NotificationFilter::new(&[], ignores, gitignore::load(&vec![])).unwrap();
|
||||
|
||||
assert!(filter.is_excluded(&Path::new("hello.rs")));
|
||||
assert!(filter.is_excluded(&Path::new("Cargo.toml")));
|
||||
@ -113,9 +113,9 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_ignores_take_precedence() {
|
||||
let ignores = vec![String::from("*.rs"), String::from("*.toml")];
|
||||
let ignores = &["*.rs".into(), "*.toml".into()];
|
||||
let filter =
|
||||
NotificationFilter::new(ignores.clone(), ignores, gitignore::load(&vec![])).unwrap();
|
||||
NotificationFilter::new(ignores, ignores, gitignore::load(&[])).unwrap();
|
||||
|
||||
assert!(filter.is_excluded(&Path::new("hello.rs")));
|
||||
assert!(filter.is_excluded(&Path::new("Cargo.toml")));
|
||||
|
301
src/run.rs
301
src/run.rs
@ -5,7 +5,7 @@ use std::sync::mpsc::{channel, Receiver};
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::time::Duration;
|
||||
|
||||
use cli;
|
||||
use cli::{Args, clear_screen};
|
||||
use env_logger;
|
||||
use error::{Error, Result};
|
||||
use gitignore;
|
||||
@ -32,40 +32,53 @@ fn init_logger(debug: bool) {
|
||||
.init();
|
||||
}
|
||||
|
||||
pub fn run<F>(args: cli::Args, cb: Option<F>) -> Result<()> where F: Fn(Vec<PathOp>) {
|
||||
let child_process: Arc<RwLock<Option<Process>>> = Arc::new(RwLock::new(None));
|
||||
let weak_child = Arc::downgrade(&child_process);
|
||||
pub trait Handler {
|
||||
/// Initialises the `Handler` with a copy of the arguments.
|
||||
fn new(args: Args) -> Result<Self> where Self: Sized;
|
||||
|
||||
// Convert signal string to the corresponding integer
|
||||
let signal = signal::new(args.signal);
|
||||
/// Called through a manual request, such as an initial run.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A `Result` which means:
|
||||
///
|
||||
/// - `Err`: an error has occurred while processing, quit.
|
||||
/// - `Ok(false)`: everything is fine and the loop can continue.
|
||||
/// - `Ok(true)`: everything is fine but we should gracefully stop.
|
||||
fn on_manual(&mut self) -> Result<bool>;
|
||||
|
||||
signal::install_handler(move |sig: Signal| {
|
||||
if let Some(lock) = weak_child.upgrade() {
|
||||
let strong = lock.read().unwrap();
|
||||
if let Some(ref child) = *strong {
|
||||
match sig {
|
||||
Signal::SIGCHLD => child.reap(), // SIGCHLD is special, initiate reap()
|
||||
_ => child.signal(sig),
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
/// Called through a file-update request.
|
||||
///
|
||||
/// # Parameters
|
||||
///
|
||||
/// - `ops`: The list of events that triggered this update.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A `Result` which means:
|
||||
///
|
||||
/// - `Err`: an error has occurred while processing, quit.
|
||||
/// - `Ok(true)`: everything is fine and the loop can continue.
|
||||
/// - `Ok(false)`: everything is fine but we should gracefully stop.
|
||||
fn on_update(&mut self, ops: Vec<PathOp>) -> Result<bool>;
|
||||
}
|
||||
|
||||
/// Starts watching, and calls a handler when something happens.
|
||||
///
|
||||
/// Given an argument structure and a `Handler` type, starts the watcher
|
||||
/// loop (blocking until done).
|
||||
pub fn watch<H>(args: Args) -> Result<()> where H: Handler {
|
||||
init_logger(args.debug);
|
||||
let mut handler = H::new(args.clone())?;
|
||||
|
||||
let mut paths = vec![];
|
||||
for path in args.paths {
|
||||
paths.push(canonicalize(&path).map_err(|e| Error::Canonicalization(path, e))?);
|
||||
}
|
||||
|
||||
let gitignore = gitignore::load(if args.no_vcs_ignore { &[] } else { &paths });
|
||||
let filter = NotificationFilter::new(args.filters, args.ignores, gitignore)?;
|
||||
let gitignore = gitignore::load(if args.no_vcs_ignore { &[] } else { &args.paths });
|
||||
let filter = NotificationFilter::new(&args.filters, &args.ignores, gitignore)?;
|
||||
|
||||
let (tx, rx) = channel();
|
||||
let poll = args.poll.clone();
|
||||
#[cfg(target_os = "linux")]
|
||||
let poll_interval = args.poll_interval.clone();
|
||||
let watcher = Watcher::new(tx.clone(), &paths, args.poll, args.poll_interval).or_else(|err| {
|
||||
let watcher = Watcher::new(tx.clone(), &args.paths, args.poll, args.poll_interval).or_else(|err| {
|
||||
if poll {
|
||||
return Err(err);
|
||||
}
|
||||
@ -82,7 +95,7 @@ pub fn run<F>(args: cli::Args, cb: Option<F>) -> Result<()> where F: Fn(Vec<Path
|
||||
}
|
||||
|
||||
if fallback {
|
||||
return Watcher::new(tx, &paths, true, poll_interval);
|
||||
return Watcher::new(tx, &args.paths, true, poll_interval);
|
||||
}
|
||||
}
|
||||
|
||||
@ -93,114 +106,152 @@ pub fn run<F>(args: cli::Args, cb: Option<F>) -> Result<()> where F: Fn(Vec<Path
|
||||
warn!("Polling for changes every {} ms", args.poll_interval);
|
||||
}
|
||||
|
||||
// Start child process initially, if necessary
|
||||
// Call handler initially, if necessary
|
||||
if args.run_initially && !args.once {
|
||||
if args.clear_screen {
|
||||
cli::clear_screen();
|
||||
clear_screen();
|
||||
}
|
||||
|
||||
let mut guard = child_process.write().unwrap();
|
||||
*guard = Some(process::spawn(&args.cmd, vec![], args.no_shell));
|
||||
}
|
||||
|
||||
//Decide if callback cb function or direct execution should be used
|
||||
let has_cb: bool = cb.is_some();
|
||||
if has_cb {
|
||||
let fcb = cb.unwrap();
|
||||
loop {
|
||||
debug!("Waiting for filesystem activity");
|
||||
let paths = wait_fs(&rx, &filter, args.debounce);
|
||||
if let Some(path) = paths.get(0) {
|
||||
debug!("Path updated: {:?}", path);
|
||||
}
|
||||
//Execute callback
|
||||
fcb(paths);
|
||||
}
|
||||
}else{
|
||||
loop {
|
||||
debug!("Waiting for filesystem activity");
|
||||
let paths = wait_fs(&rx, &filter, args.debounce);
|
||||
if let Some(path) = paths.get(0) {
|
||||
debug!("Path updated: {:?}", path);
|
||||
}
|
||||
|
||||
// We have three scenarios here:
|
||||
//
|
||||
// 1. Make sure the previous run was ended, then run the command again
|
||||
// 2. Just send a specified signal to the child, do nothing more
|
||||
// 3. Send SIGTERM to the child, wait for it to exit, then run the command again
|
||||
// 4. Send a specified signal to the child, wait for it to exit, then run the command again
|
||||
//
|
||||
let scenario = (args.restart, signal.is_some());
|
||||
|
||||
match scenario {
|
||||
// Custom restart behaviour (--restart was given, and --signal specified):
|
||||
// Send specified signal to the child, wait for it to exit, then run the command again
|
||||
(true, true) => {
|
||||
signal_process(&child_process, signal, true);
|
||||
|
||||
// Launch child process
|
||||
if args.clear_screen {
|
||||
cli::clear_screen();
|
||||
}
|
||||
|
||||
debug!("Launching child process");
|
||||
{
|
||||
let mut guard = child_process.write().unwrap();
|
||||
*guard = Some(process::spawn(&args.cmd, paths, args.no_shell));
|
||||
}
|
||||
}
|
||||
|
||||
// Default restart behaviour (--restart was given, but --signal wasn't specified):
|
||||
// Send SIGTERM to the child, wait for it to exit, then run the command again
|
||||
(true, false) => {
|
||||
let sigterm = signal::new(Some("SIGTERM".to_owned()));
|
||||
signal_process(&child_process, sigterm, true);
|
||||
|
||||
// Launch child process
|
||||
if args.clear_screen {
|
||||
cli::clear_screen();
|
||||
}
|
||||
|
||||
debug!("Launching child process");
|
||||
{
|
||||
let mut guard = child_process.write().unwrap();
|
||||
*guard = Some(process::spawn(&args.cmd, paths, args.no_shell));
|
||||
}
|
||||
}
|
||||
|
||||
// SIGHUP scenario: --signal was given, but --restart was not
|
||||
// Just send a signal (e.g. SIGHUP) to the child, do nothing more
|
||||
(false, true) => signal_process(&child_process, signal, false),
|
||||
|
||||
// Default behaviour (neither --signal nor --restart specified):
|
||||
// Make sure the previous run was ended, then run the command again
|
||||
(false, false) => {
|
||||
signal_process(&child_process, None, true);
|
||||
|
||||
// Launch child process
|
||||
if args.clear_screen {
|
||||
cli::clear_screen();
|
||||
}
|
||||
|
||||
debug!("Launching child process");
|
||||
{
|
||||
let mut guard = child_process.write().unwrap();
|
||||
*guard = Some(process::spawn(&args.cmd, paths, args.no_shell));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle once option for integration testing
|
||||
if args.once {
|
||||
signal_process(&child_process, signal, false);
|
||||
break;
|
||||
}
|
||||
if !handler.on_manual()? {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
loop {
|
||||
debug!("Waiting for filesystem activity");
|
||||
let paths = wait_fs(&rx, &filter, args.debounce);
|
||||
debug!("Paths updated: {:?}", paths);
|
||||
|
||||
if args.clear_screen {
|
||||
clear_screen();
|
||||
}
|
||||
|
||||
if !handler.on_update(paths)? {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub struct ExecHandler {
|
||||
args: Args,
|
||||
signal: Option<Signal>,
|
||||
child_process: Arc<RwLock<Option<Process>>>,
|
||||
}
|
||||
|
||||
impl Handler for ExecHandler {
|
||||
fn new(args: Args) -> Result<Self> {
|
||||
let child_process: Arc<RwLock<Option<Process>>> = Arc::new(RwLock::new(None));
|
||||
let weak_child = Arc::downgrade(&child_process);
|
||||
|
||||
// Convert signal string to the corresponding integer
|
||||
let signal = signal::new(args.signal.clone());
|
||||
|
||||
signal::install_handler(move |sig: Signal| {
|
||||
if let Some(lock) = weak_child.upgrade() {
|
||||
let strong = lock.read().unwrap();
|
||||
if let Some(ref child) = *strong {
|
||||
match sig {
|
||||
Signal::SIGCHLD => child.reap(), // SIGCHLD is special, initiate reap()
|
||||
_ => child.signal(sig),
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Ok(Self { args, signal, child_process })
|
||||
}
|
||||
|
||||
fn on_manual(&mut self) -> Result<bool> {
|
||||
let mut guard = self.child_process.write()?;
|
||||
*guard = Some(process::spawn(&self.args.cmd, Vec::new(), self.args.no_shell));
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn on_update(&mut self, ops: Vec<PathOp>) -> Result<bool> {
|
||||
// We have three scenarios here:
|
||||
//
|
||||
// 1. Make sure the previous run was ended, then run the command again
|
||||
// 2. Just send a specified signal to the child, do nothing more
|
||||
// 3. Send SIGTERM to the child, wait for it to exit, then run the command again
|
||||
// 4. Send a specified signal to the child, wait for it to exit, then run the command again
|
||||
//
|
||||
let scenario = (self.args.restart, self.signal.is_some());
|
||||
|
||||
match scenario {
|
||||
// Custom restart behaviour (--restart was given, and --signal specified):
|
||||
// Send specified signal to the child, wait for it to exit, then run the command again
|
||||
(true, true) => {
|
||||
signal_process(&self.child_process, self.signal, true);
|
||||
|
||||
// Launch child process
|
||||
if self.args.clear_screen {
|
||||
clear_screen();
|
||||
}
|
||||
|
||||
debug!("Launching child process");
|
||||
{
|
||||
let mut guard = self.child_process.write()?;
|
||||
*guard = Some(process::spawn(&self.args.cmd, ops, self.args.no_shell));
|
||||
}
|
||||
}
|
||||
|
||||
// Default restart behaviour (--restart was given, but --signal wasn't specified):
|
||||
// Send SIGTERM to the child, wait for it to exit, then run the command again
|
||||
(true, false) => {
|
||||
let sigterm = signal::new(Some("SIGTERM".into()));
|
||||
signal_process(&self.child_process, sigterm, true);
|
||||
|
||||
// Launch child process
|
||||
if self.args.clear_screen {
|
||||
clear_screen();
|
||||
}
|
||||
|
||||
debug!("Launching child process");
|
||||
{
|
||||
let mut guard = self.child_process.write()?;
|
||||
*guard = Some(process::spawn(&self.args.cmd, ops, self.args.no_shell));
|
||||
}
|
||||
}
|
||||
|
||||
// SIGHUP scenario: --signal was given, but --restart was not
|
||||
// Just send a signal (e.g. SIGHUP) to the child, do nothing more
|
||||
(false, true) => signal_process(&self.child_process, self.signal, false),
|
||||
|
||||
// Default behaviour (neither --signal nor --restart specified):
|
||||
// Make sure the previous run was ended, then run the command again
|
||||
(false, false) => {
|
||||
signal_process(&self.child_process, None, true);
|
||||
|
||||
// Launch child process
|
||||
if self.args.clear_screen {
|
||||
clear_screen();
|
||||
}
|
||||
|
||||
debug!("Launching child process");
|
||||
{
|
||||
let mut guard = self.child_process.write()?;
|
||||
*guard = Some(process::spawn(&self.args.cmd, ops, self.args.no_shell));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle once option for integration testing
|
||||
if self.args.once {
|
||||
signal_process(&self.child_process, self.signal, false);
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(args: Args) -> Result<()> {
|
||||
watch::<ExecHandler>(args)
|
||||
}
|
||||
|
||||
fn wait_fs(rx: &Receiver<Event>, filter: &NotificationFilter, debounce: u64) -> Vec<PathOp> {
|
||||
let mut paths = vec![];
|
||||
let mut cache = HashMap::new();
|
||||
|
Loading…
Reference in New Issue
Block a user