Move Args and builder to Config, and deprecate old names

This commit is contained in:
Félix Saparelli 2021-04-11 01:23:24 +12:00
parent 31fd818247
commit cd3b8c6cba
No known key found for this signature in database
GPG Key ID: B948C4BAE44FC474
4 changed files with 124 additions and 93 deletions

View File

@ -1,12 +1,12 @@
//! CLI arguments and library Args struct
//! CLI arguments and library Config struct
//!
//! The [`Args`] struct is not constructable, use [`ArgsBuilder`].
//! The [`Config`] struct is not constructable, use [`ConfigBuilder`].
//!
//! # Examples
//!
//! ```
//! # use watchexec::cli::ArgsBuilder;
//! ArgsBuilder::default()
//! # use watchexec::cli::ConfigBuilder;
//! ConfigBuilder::default()
//! .cmd(vec!["echo hello world".into()])
//! .paths(vec![".".into()])
//! .build()
@ -22,85 +22,13 @@ use std::{
process::Command,
};
/// Arguments to the watcher
#[derive(Builder, Clone, Debug)]
#[builder(setter(into, strip_option))]
#[builder(build_fn(validate = "Self::validate"))]
#[non_exhaustive]
pub struct Args {
/// Command to execute in popen3 format (first program, rest arguments).
pub cmd: Vec<String>,
/// List of paths to watch for changes.
pub paths: Vec<PathBuf>,
/// Positive filters (trigger only on matching changes). Glob format.
#[builder(default)]
pub filters: Vec<String>,
/// Negative filters (do not trigger on matching changes). Glob format.
#[builder(default)]
pub ignores: Vec<String>,
/// Clear the screen before each run.
#[builder(default)]
pub clear_screen: bool,
/// If Some, send that signal (e.g. SIGHUP) to the child on change.
#[builder(default)]
pub signal: Option<String>,
/// If true, kill the child if it's still running when a change comes in.
#[builder(default)]
pub restart: bool,
/// Interval to debounce the changes. (milliseconds)
#[builder(default = "500")]
pub debounce: u64,
/// Run the commands right after starting.
#[builder(default = "true")]
pub run_initially: bool,
/// Do not wrap the commands in a shell.
#[builder(default)]
pub no_shell: bool,
/// Ignore metadata changes.
#[builder(default)]
pub no_meta: bool,
/// Do not set WATCHEXEC_*_PATH environment variables for child process.
#[builder(default)]
pub no_environment: bool,
/// Skip auto-loading .gitignore files
#[builder(default)]
pub no_vcs_ignore: bool,
/// Skip auto-loading .ignore files
#[builder(default)]
pub no_ignore: bool,
/// For testing only, always set to false.
#[builder(setter(skip))]
#[builder(default)]
#[doc(hidden)]
pub once: bool,
/// Force using the polling backend.
#[builder(default)]
pub poll: bool,
/// Interval for polling. (milliseconds)
#[builder(default = "1000")]
pub poll_interval: u32,
#[builder(default)]
pub watch_when_idle: bool,
}
use crate::config::{Config, ConfigBuilder};
impl ArgsBuilder {
fn validate(&self) -> Result<(), String> {
if self.cmd.as_ref().map_or(true, Vec::is_empty) {
return Err("cmd must not be empty".into());
}
#[deprecated(since = "1.15.0", note = "Config has moved to config::Config")]
pub type Args = Config;
if self.paths.as_ref().map_or(true, Vec::is_empty) {
return Err("paths must not be empty".into());
}
Ok(())
}
#[deprecated(since = "1.15.0", note = "does nothing. set the log level instead")]
pub fn debug(&mut self, _: impl Into<bool>) -> &mut Self {
self
}
}
#[deprecated(since = "1.15.0", note = "ConfigBuilder has moved to config::ConfigBuilder")]
pub type ArgsBuilder = ConfigBuilder;
/// Clear the screen.
#[cfg(target_family = "windows")]
@ -120,12 +48,12 @@ pub fn clear_screen() {
}
#[deprecated(since = "1.15.0", note = "this will be removed from the library API. use the builder")]
pub fn get_args() -> error::Result<(Args, LevelFilter)> {
pub fn get_args() -> error::Result<(Config, LevelFilter)> {
get_args_impl(None::<&[&str]>)
}
#[deprecated(since = "1.15.0", note = "this will be removed from the library API. use the builder")]
pub fn get_args_from<I, T>(from: I) -> error::Result<(Args, LevelFilter)>
pub fn get_args_from<I, T>(from: I) -> error::Result<(Config, LevelFilter)>
where
I: IntoIterator<Item = T>,
T: Into<OsString> + Clone,
@ -133,7 +61,7 @@ where
get_args_impl(Some(from))
}
fn get_args_impl<I, T>(from: Option<I>) -> error::Result<(Args, LevelFilter)>
fn get_args_impl<I, T>(from: Option<I>) -> error::Result<(Config, LevelFilter)>
where
I: IntoIterator<Item = T>,
T: Into<OsString> + Clone,
@ -243,7 +171,7 @@ where
Some(i) => app.get_matches_from(i),
};
let mut builder = ArgsBuilder::default();
let mut builder = ConfigBuilder::default();
let cmd: Vec<String> = values_t!(args.values_of("command"), String).map_err(|err| err.to_string())?;
builder.cmd(cmd);

96
src/config.rs Normal file
View File

@ -0,0 +1,96 @@
//! Configuration for watchexec.
//!
//! The [`Config`] struct is not constructable, use [`ConfigBuilder`].
//!
//! # Examples
//!
//! ```
//! # use watchexec::config::ConfigBuilder;
//! ConfigBuilder::default()
//! .cmd(vec!["echo hello world".into()])
//! .paths(vec![".".into()])
//! .build()
//! .expect("mission failed");
//! ```
use std::path::PathBuf;
/// Arguments to the watcher
#[derive(Builder, Clone, Debug)]
#[builder(setter(into, strip_option))]
#[builder(build_fn(validate = "Self::validate"))]
#[non_exhaustive]
pub struct Config {
/// Command to execute in popen3 format (first program, rest arguments).
pub cmd: Vec<String>,
/// List of paths to watch for changes.
pub paths: Vec<PathBuf>,
/// Positive filters (trigger only on matching changes). Glob format.
#[builder(default)]
pub filters: Vec<String>,
/// Negative filters (do not trigger on matching changes). Glob format.
#[builder(default)]
pub ignores: Vec<String>,
/// Clear the screen before each run.
#[builder(default)]
pub clear_screen: bool,
/// If Some, send that signal (e.g. SIGHUP) to the child on change.
#[builder(default)]
pub signal: Option<String>,
/// If true, kill the child if it's still running when a change comes in.
#[builder(default)]
pub restart: bool,
/// Interval to debounce the changes. (milliseconds)
#[builder(default = "500")]
pub debounce: u64,
/// Run the commands right after starting.
#[builder(default = "true")]
pub run_initially: bool,
/// Do not wrap the commands in a shell.
#[builder(default)]
pub no_shell: bool,
/// Ignore metadata changes.
#[builder(default)]
pub no_meta: bool,
/// Do not set WATCHEXEC_*_PATH environment variables for child process.
#[builder(default)]
pub no_environment: bool,
/// Skip auto-loading .gitignore files
#[builder(default)]
pub no_vcs_ignore: bool,
/// Skip auto-loading .ignore files
#[builder(default)]
pub no_ignore: bool,
/// For testing only, always set to false.
#[builder(setter(skip))]
#[builder(default)]
#[doc(hidden)]
pub once: bool,
/// Force using the polling backend.
#[builder(default)]
pub poll: bool,
/// Interval for polling. (milliseconds)
#[builder(default = "1000")]
pub poll_interval: u32,
#[builder(default)]
pub watch_when_idle: bool,
}
impl ConfigBuilder {
fn validate(&self) -> Result<(), String> {
if self.cmd.as_ref().map_or(true, Vec::is_empty) {
return Err("cmd must not be empty".into());
}
if self.paths.as_ref().map_or(true, Vec::is_empty) {
return Err("paths must not be empty".into());
}
Ok(())
}
#[deprecated(since = "1.15.0", note = "does nothing. set the log level instead")]
pub fn debug(&mut self, _: impl Into<bool>) -> &mut Self {
self
}
}

View File

@ -24,6 +24,7 @@ extern crate log;
extern crate lazy_static;
pub mod cli;
pub mod config;
pub mod error;
mod gitignore;
mod ignore;
@ -34,5 +35,10 @@ pub mod run;
mod signal;
mod watcher;
pub use cli::{Args, ArgsBuilder};
pub use run::{run, watch, Handler};
#[deprecated(since = "1.15.0", note = "Config has moved to config::Config")]
pub type Args = config::Config;
#[deprecated(since = "1.15.0", note = "ConfigBuilder has moved to config::ConfigBuilder")]
pub type ArgsBuilder = config::ConfigBuilder;

View File

@ -1,4 +1,5 @@
use crate::cli::{clear_screen, Args};
use crate::cli::{clear_screen};
use crate::config::Config;
use crate::error::{Error, Result};
use crate::gitignore;
use crate::ignore;
@ -48,9 +49,9 @@ pub trait Handler {
///
/// Not called again; any changes will never be picked up.
///
/// The `Args` instance should be created using `ArgsBuilder` rather than direct initialisation
/// The `Config` instance should be created using `ConfigBuilder` rather than direct initialisation
/// to resist potential breaking changes (see semver policy on crate root).
fn args(&self) -> Args;
fn args(&self) -> Config;
}
/// Starts watching, and calls a handler when something happens.
@ -130,13 +131,13 @@ where
}
pub struct ExecHandler {
args: Args,
args: Config,
signal: Option<Signal>,
child_process: Arc<RwLock<Option<Process>>>,
}
impl ExecHandler {
pub fn new(args: Args) -> Result<Self> {
pub fn new(args: Config) -> Result<Self> {
let child_process: Arc<RwLock<Option<Process>>> = Arc::new(RwLock::new(None));
let weak_child = Arc::downgrade(&child_process);
@ -190,7 +191,7 @@ impl ExecHandler {
}
impl Handler for ExecHandler {
fn args(&self) -> Args {
fn args(&self) -> Config {
self.args.clone()
}
@ -267,7 +268,7 @@ impl Handler for ExecHandler {
}
}
pub fn run(args: Args) -> Result<()> {
pub fn run(args: Config) -> Result<()> {
watch(&ExecHandler::new(args)?)
}