parent
92d05755c9
commit
5c2c80a6e0
|
@ -8,6 +8,7 @@ use std::{
|
|||
|
||||
use clap::{crate_version, Arg, ArgMatches, Command};
|
||||
use miette::{Context, IntoDiagnostic, Result};
|
||||
use tracing::debug;
|
||||
|
||||
const OPTSET_FILTERING: &str = "Filtering options";
|
||||
const OPTSET_COMMAND: &str = "Command options";
|
||||
|
@ -108,6 +109,12 @@ pub fn get_args(tagged_filterer: bool) -> Result<ArgMatches> {
|
|||
.help("Wait until first change to execute command")
|
||||
.short('p')
|
||||
.long("postpone"))
|
||||
.arg(Arg::new("delay-run")
|
||||
.help_heading(Some(OPTSET_BEHAVIOUR))
|
||||
.help("Whenever starting the command, sleep some seconds first")
|
||||
.long("delay-run")
|
||||
.takes_value(true)
|
||||
.value_name("seconds"))
|
||||
.arg(Arg::new("poll")
|
||||
.help_heading(Some(OPTSET_BEHAVIOUR))
|
||||
.help("Force polling mode (interval in milliseconds)")
|
||||
|
@ -262,5 +269,6 @@ pub fn get_args(tagged_filterer: bool) -> Result<ArgMatches> {
|
|||
}
|
||||
}
|
||||
|
||||
debug!(?raw_args, "parsing arguments");
|
||||
Ok(app.get_matches_from(raw_args))
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use std::{
|
|||
use clap::ArgMatches;
|
||||
use miette::{miette, IntoDiagnostic, Result};
|
||||
use notify_rust::Notification;
|
||||
use tracing::debug;
|
||||
use tracing::{debug, debug_span};
|
||||
use watchexec::{
|
||||
action::{Action, Outcome, PostSpawn, PreSpawn},
|
||||
command::{Command, Shell},
|
||||
|
@ -20,6 +20,7 @@ use watchexec::{
|
|||
};
|
||||
|
||||
pub fn runtime(args: &ArgMatches) -> Result<RuntimeConfig> {
|
||||
let _span = debug_span!("args-runtime").entered();
|
||||
let mut config = RuntimeConfig::default();
|
||||
|
||||
config.command(interpret_command_args(args)?);
|
||||
|
@ -74,6 +75,12 @@ pub fn runtime(args: &ArgMatches) -> Result<RuntimeConfig> {
|
|||
|
||||
let print_events = args.is_present("print-events");
|
||||
let once = args.is_present("once");
|
||||
let delay_run = args
|
||||
.value_of("delay-run")
|
||||
.map(|d| u64::from_str(d))
|
||||
.transpose()
|
||||
.into_diagnostic()?
|
||||
.map(Duration::from_secs);
|
||||
|
||||
config.on_action(move |action: Action| {
|
||||
let fut = async { Ok::<(), Infallible>(()) };
|
||||
|
@ -85,7 +92,14 @@ pub fn runtime(args: &ArgMatches) -> Result<RuntimeConfig> {
|
|||
}
|
||||
|
||||
if once {
|
||||
action.outcome(Outcome::both(Outcome::Start, Outcome::wait(Outcome::Exit)));
|
||||
action.outcome(Outcome::both(
|
||||
if let Some(delay) = &delay_run {
|
||||
Outcome::both(Outcome::Sleep(delay.clone()), Outcome::Start)
|
||||
} else {
|
||||
Outcome::Start
|
||||
},
|
||||
Outcome::wait(Outcome::Exit),
|
||||
));
|
||||
return fut;
|
||||
}
|
||||
|
||||
|
@ -158,24 +172,27 @@ pub fn runtime(args: &ArgMatches) -> Result<RuntimeConfig> {
|
|||
}
|
||||
}
|
||||
|
||||
let when_running = match (clear, on_busy.as_str()) {
|
||||
(_, "do-nothing") => Outcome::DoNothing,
|
||||
(true, "restart") => {
|
||||
Outcome::both(Outcome::Stop, Outcome::both(Outcome::Clear, Outcome::Start))
|
||||
}
|
||||
(false, "restart") => Outcome::both(Outcome::Stop, Outcome::Start),
|
||||
(_, "signal") => Outcome::Signal(signal),
|
||||
(true, "queue") => Outcome::wait(Outcome::both(Outcome::Clear, Outcome::Start)),
|
||||
(false, "queue") => Outcome::wait(Outcome::Start),
|
||||
_ => Outcome::DoNothing,
|
||||
};
|
||||
|
||||
let when_idle = if clear {
|
||||
let start = if clear {
|
||||
Outcome::both(Outcome::Clear, Outcome::Start)
|
||||
} else {
|
||||
Outcome::Start
|
||||
};
|
||||
|
||||
let start = if let Some(delay) = &delay_run {
|
||||
Outcome::both(Outcome::Sleep(delay.clone()), start)
|
||||
} else {
|
||||
start
|
||||
};
|
||||
|
||||
let when_idle = start.clone();
|
||||
let when_running = match on_busy.as_str() {
|
||||
"do-nothing" => Outcome::DoNothing,
|
||||
"restart" => start,
|
||||
"signal" => Outcome::Signal(signal),
|
||||
"queue" => Outcome::wait(start),
|
||||
_ => Outcome::DoNothing,
|
||||
};
|
||||
|
||||
action.outcome(Outcome::if_running(when_running, when_idle));
|
||||
|
||||
fut
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#![deny(rust_2018_idioms)]
|
||||
|
||||
use std::{fs::File, sync::Mutex, env::var};
|
||||
use std::{env::var, fs::File, sync::Mutex};
|
||||
|
||||
use miette::{IntoDiagnostic, Result};
|
||||
use tracing::debug;
|
||||
|
@ -39,7 +39,9 @@ async fn main() -> Result<()> {
|
|||
let verbosity = args.occurrences_of("verbose");
|
||||
let log_file = if let Some(file) = args.value_of("log-file") {
|
||||
Some(File::create(file).into_diagnostic()?)
|
||||
} else { None };
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut builder = tracing_subscriber::fmt().with_env_filter(match verbosity {
|
||||
0 => "watchexec-cli=warn",
|
||||
|
@ -54,7 +56,11 @@ async fn main() -> Result<()> {
|
|||
}
|
||||
|
||||
if let Some(writer) = log_file {
|
||||
builder.json().with_writer(Mutex::new(writer)).try_init().ok();
|
||||
builder
|
||||
.json()
|
||||
.with_writer(Mutex::new(writer))
|
||||
.try_init()
|
||||
.ok();
|
||||
} else if verbosity > 3 {
|
||||
builder.pretty().try_init().ok();
|
||||
} else {
|
||||
|
@ -62,7 +68,7 @@ async fn main() -> Result<()> {
|
|||
}
|
||||
}
|
||||
|
||||
debug!(version=%env!("CARGO_PKG_VERSION"), "constructing Watchexec from CLI");
|
||||
debug!(version=%env!("CARGO_PKG_VERSION"), ?args, "constructing Watchexec from CLI");
|
||||
|
||||
let init = config::init(&args)?;
|
||||
let mut runtime = config::runtime(&args)?;
|
||||
|
|
|
@ -45,6 +45,9 @@ Behaviour options:
|
|||
-d, --debounce <milliseconds>
|
||||
Set the timeout between detected change and command execution, defaults to 50ms
|
||||
|
||||
--delay-run <seconds>
|
||||
Whenever starting the command, sleep some seconds first
|
||||
|
||||
--force-poll <interval>
|
||||
Force polling mode (interval in milliseconds)
|
||||
|
||||
|
|
|
@ -46,6 +46,9 @@ Behaviour options:
|
|||
-d, --debounce <milliseconds>
|
||||
Set the timeout between detected change and command execution, defaults to 50ms
|
||||
|
||||
--delay-run <seconds>
|
||||
Whenever starting the command, sleep some seconds first
|
||||
|
||||
--force-poll <interval>
|
||||
Force polling mode (interval in milliseconds)
|
||||
|
||||
|
|
|
@ -155,7 +155,9 @@ impl OutcomeWorker {
|
|||
}
|
||||
|
||||
(_, Outcome::Sleep(time)) => {
|
||||
trace!(?time, "sleeping");
|
||||
notry!(sleep(time));
|
||||
trace!(?time, "done sleeping");
|
||||
}
|
||||
|
||||
(_, Outcome::Clear) => {
|
||||
|
|
|
@ -59,26 +59,19 @@ pub enum Command {
|
|||
pub enum Shell {
|
||||
/// Use the given string as a unix shell invocation.
|
||||
///
|
||||
/// This means two things:
|
||||
/// - the program is invoked with `-c` followed by the command, and
|
||||
/// - the string will be split on space, and the resulting vec used as execvp(3) arguments:
|
||||
/// first is the shell program, rest are additional arguments (which come before the `-c`
|
||||
/// mentioned above). This is a very simplistic approach deliberately: it will not support
|
||||
/// quoted arguments, for example. Use [`Shell::None`] with a custom command vec for that.
|
||||
/// This is invoked with `-c` followed by the command.
|
||||
Unix(String),
|
||||
|
||||
/// Use the Windows CMD.EXE shell.
|
||||
///
|
||||
/// This is invoked with `/C` followed by the command.
|
||||
/// This is `cmd.exe` invoked with `/C` followed by the command.
|
||||
#[cfg(windows)]
|
||||
Cmd,
|
||||
|
||||
/// Use Powershell, on Windows or elsewhere.
|
||||
///
|
||||
/// This is invoked with `-Command` followed by the command.
|
||||
///
|
||||
/// This is preferred over `Unix("pwsh")`, though that will also work on unices due to
|
||||
/// Powershell supporting the `-c` short option.
|
||||
/// This is `powershell.exe` invoked with `-Command` followed by the command on Windows.
|
||||
/// On unices, it is equivalent to `Unix("pwsh")`.
|
||||
Powershell,
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
use super::{Command, Shell};
|
||||
use command_group::AsyncCommandGroup;
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
use std::{fmt, path::Path, sync::Arc, time::Duration};
|
||||
|
||||
use tracing::debug;
|
||||
|
||||
use crate::{
|
||||
action::{Action, PostSpawn, PreSpawn},
|
||||
command::Command,
|
||||
|
@ -20,6 +22,9 @@ use crate::{
|
|||
/// marked non-exhaustive such that new options may be added without breaking change. You can make
|
||||
/// changes through the fields directly, or use the convenience (chainable!) methods instead.
|
||||
///
|
||||
/// Another advantage of using the convenience methods is that each one contains a call to the
|
||||
/// [`debug!`] macro, providing insight into what config your application sets for "free".
|
||||
///
|
||||
/// You should see the detailed documentation on [fs::WorkingData][crate::fs::WorkingData] and
|
||||
/// [action::WorkingData][crate::action::WorkingData] for important information and particulars
|
||||
/// about each field, especially the handlers.
|
||||
|
@ -46,11 +51,13 @@ impl RuntimeConfig {
|
|||
P: AsRef<Path>,
|
||||
{
|
||||
self.fs.pathset = pathset.into_iter().map(|p| p.as_ref().into()).collect();
|
||||
debug!(pathset=?self.fs.pathset, "RuntimeConfig: pathset");
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the file watcher type to use.
|
||||
pub fn file_watcher(&mut self, watcher: Watcher) -> &mut Self {
|
||||
debug!(?watcher, "RuntimeConfig: watcher");
|
||||
self.fs.watcher = watcher;
|
||||
self
|
||||
}
|
||||
|
@ -58,11 +65,13 @@ impl RuntimeConfig {
|
|||
/// Set the action throttle.
|
||||
pub fn action_throttle(&mut self, throttle: impl Into<Duration>) -> &mut Self {
|
||||
self.action.throttle = throttle.into();
|
||||
debug!(throttle=?self.action.throttle, "RuntimeConfig: throttle");
|
||||
self
|
||||
}
|
||||
|
||||
/// Toggle whether to use process groups or not.
|
||||
pub fn command_grouped(&mut self, grouped: bool) -> &mut Self {
|
||||
debug!(?grouped, "RuntimeConfig: command_grouped");
|
||||
self.action.grouped = grouped;
|
||||
self
|
||||
}
|
||||
|
@ -71,6 +80,7 @@ impl RuntimeConfig {
|
|||
///
|
||||
/// This is a convenience for `.commands(vec![Command...])`.
|
||||
pub fn command(&mut self, command: Command) -> &mut Self {
|
||||
debug!(?command, "RuntimeConfig: command");
|
||||
self.action.commands = vec![command];
|
||||
self
|
||||
}
|
||||
|
@ -78,23 +88,27 @@ impl RuntimeConfig {
|
|||
/// Set the commands to run on action.
|
||||
pub fn commands(&mut self, commands: impl Into<Vec<Command>>) -> &mut Self {
|
||||
self.action.commands = commands.into();
|
||||
debug!(commands=?self.action.commands, "RuntimeConfig: commands");
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the filterer implementation to use.
|
||||
pub fn filterer(&mut self, filterer: Arc<dyn Filterer>) -> &mut Self {
|
||||
debug!(?filterer, "RuntimeConfig: filterer");
|
||||
self.action.filterer = filterer;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the action handler.
|
||||
pub fn on_action(&mut self, handler: impl Handler<Action> + Send + 'static) -> &mut Self {
|
||||
debug!("RuntimeConfig: on_action");
|
||||
self.action.action_handler = HandlerLock::new(Box::new(handler));
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the pre-spawn handler.
|
||||
pub fn on_pre_spawn(&mut self, handler: impl Handler<PreSpawn> + Send + 'static) -> &mut Self {
|
||||
debug!("RuntimeConfig: on_pre_spawn");
|
||||
self.action.pre_spawn_handler = HandlerLock::new(Box::new(handler));
|
||||
self
|
||||
}
|
||||
|
@ -104,6 +118,7 @@ impl RuntimeConfig {
|
|||
&mut self,
|
||||
handler: impl Handler<PostSpawn> + Send + 'static,
|
||||
) -> &mut Self {
|
||||
debug!("RuntimeConfig: on_post_spawn");
|
||||
self.action.post_spawn_handler = HandlerLock::new(Box::new(handler));
|
||||
self
|
||||
}
|
||||
|
@ -168,6 +183,7 @@ impl InitConfig {
|
|||
///
|
||||
/// See the [documentation on the field](InitConfig#structfield.error_handler) for more details.
|
||||
pub fn on_error(&mut self, handler: impl Handler<ErrorHook> + Send + 'static) -> &mut Self {
|
||||
debug!("InitConfig: on_error");
|
||||
self.error_handler = Box::new(handler) as _;
|
||||
self
|
||||
}
|
||||
|
@ -176,6 +192,7 @@ impl InitConfig {
|
|||
///
|
||||
/// See the [documentation on the field](InitConfig#structfield.error_channel_size) for more details.
|
||||
pub fn error_channel_size(&mut self, size: usize) -> &mut Self {
|
||||
debug!(?size, "InitConfig: error_channel_size");
|
||||
self.error_channel_size = size;
|
||||
self
|
||||
}
|
||||
|
@ -184,6 +201,7 @@ impl InitConfig {
|
|||
///
|
||||
/// See the [documentation on the field](InitConfig#structfield.event_channel_size) for more details.
|
||||
pub fn event_channel_size(&mut self, size: usize) -> &mut Self {
|
||||
debug!(?size, "InitConfig: event_channel_size");
|
||||
self.event_channel_size = size;
|
||||
self
|
||||
}
|
||||
|
|
|
@ -157,10 +157,6 @@ pub enum RuntimeError {
|
|||
),
|
||||
|
||||
/// Error emitted by a [`Filterer`](crate::filter::Filterer).
|
||||
///
|
||||
/// With built-in filterers this will probably be a dynbox of
|
||||
/// [`TaggedFiltererError`](crate::error::TaggedFiltererError), but it is
|
||||
/// possible to use a custom filterer which emits a different error type.
|
||||
#[error("{kind} filterer: {err}")]
|
||||
#[diagnostic(code(watchexec::runtime::filterer))]
|
||||
Filterer {
|
||||
|
|
Loading…
Reference in New Issue