Handle actions correctly, and add useful options from watchexec

This commit is contained in:
Félix Saparelli 2022-07-08 22:19:51 +12:00
parent c268fcfc37
commit 268b1a530a
No known key found for this signature in database
GPG Key ID: B948C4BAE44FC474
5 changed files with 496 additions and 268 deletions

343
Cargo.lock generated
View File

@ -50,20 +50,6 @@ dependencies = [
"os_str_bytes",
]
[[package]]
name = "assert_cmd"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c98233c6673d8601ab23e77eb38f999c51100d46c5703b17288c57fddf3a1ffe"
dependencies = [
"bstr",
"doc-comment",
"predicates",
"predicates-core",
"predicates-tree",
"wait-timeout",
]
[[package]]
name = "async-broadcast"
version = "0.4.0"
@ -183,9 +169,9 @@ dependencies = [
[[package]]
name = "async-task"
version = "4.2.0"
version = "4.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30696a84d817107fc028e049980e09d5e140e8da8f1caeb17e8e950658a3cea9"
checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524"
[[package]]
name = "async-trait"
@ -266,9 +252,9 @@ dependencies = [
[[package]]
name = "backtrace"
version = "0.3.65"
version = "0.3.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11a17d453482a265fd5f8479f2a3f405566e6ca627837aaddb85af8b1ab8ef61"
checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7"
dependencies = [
"addr2line",
"cc",
@ -303,9 +289,7 @@ version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
dependencies = [
"lazy_static",
"memchr",
"regex-automata",
]
[[package]]
@ -331,24 +315,23 @@ name = "cargo-watch"
version = "8.1.2"
dependencies = [
"argfile",
"assert_cmd",
"clap",
"clap_complete",
"clap_complete_fig",
"console-subscriber",
"duct",
"dunce",
"embed-resource",
"futures",
"insta",
"miette",
"mimalloc",
"notify-rust",
"predicates",
"shlex",
"tempfile",
"tokio",
"tracing",
"tracing-subscriber",
"wait-timeout",
"trycmd",
"watchexec",
"wild",
]
@ -442,6 +425,16 @@ dependencies = [
"winapi",
]
[[package]]
name = "combine"
version = "4.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a604e93b79d1808327a6fca85a6f2d69de66461e7620f5a4cbf5fb4d1d7c948"
dependencies = [
"bytes",
"memchr",
]
[[package]]
name = "command-group"
version = "1.0.8"
@ -454,6 +447,23 @@ dependencies = [
"winapi",
]
[[package]]
name = "concolor"
version = "0.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "015267563b1df20adccdd00cb05257b1dfbea70a04928e9cf88ffb850c1a40af"
dependencies = [
"atty",
"bitflags",
"concolor-query",
]
[[package]]
name = "concolor-query"
version = "0.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6417fe6fc03a8b533fd2177742eeb39a90c7233eedec7bac96d4d6b69a09449"
[[package]]
name = "concurrent-queue"
version = "1.2.2"
@ -463,19 +473,6 @@ dependencies = [
"cache-padded",
]
[[package]]
name = "console"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28b32d32ca44b70c3e4acd7db1babf555fa026e385fb95f18028f88848b3c31"
dependencies = [
"encode_unicode",
"libc",
"once_cell",
"terminal_size",
"winapi",
]
[[package]]
name = "console-api"
version = "0.3.0"
@ -512,6 +509,15 @@ dependencies = [
"tracing-subscriber",
]
[[package]]
name = "content_inspector"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7bda66e858c683005a53a9a60c69a4aca7eeaa45d124526e389f7aec8e62f38"
dependencies = [
"memchr",
]
[[package]]
name = "crc32fast"
version = "1.3.2"
@ -531,6 +537,31 @@ dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e"
dependencies = [
"cfg-if 1.0.0",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07db9d94cbd326813772c968ccd25999e5f8ae22f4f8d1b11effa37ef6ce281d"
dependencies = [
"autocfg",
"cfg-if 1.0.0",
"crossbeam-utils",
"memoffset",
"once_cell",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.10"
@ -552,12 +583,6 @@ dependencies = [
"syn",
]
[[package]]
name = "difflib"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
[[package]]
name = "dirs"
version = "2.0.2"
@ -610,10 +635,16 @@ dependencies = [
]
[[package]]
name = "doc-comment"
version = "0.3.3"
name = "duct"
version = "0.13.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
checksum = "0fc6a0a59ed0888e0041cf708e66357b7ae1a82f1c67247e1f93b5e0818f7d8d"
dependencies = [
"libc",
"once_cell",
"os_pipe 0.9.2",
"shared_child",
]
[[package]]
name = "dunce"
@ -646,12 +677,6 @@ dependencies = [
"winreg",
]
[[package]]
name = "encode_unicode"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
[[package]]
name = "enumflags2"
version = "0.7.5"
@ -732,15 +757,6 @@ dependencies = [
"miniz_oxide",
]
[[package]]
name = "float-cmp"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
dependencies = [
"num-traits",
]
[[package]]
name = "fnv"
version = "1.0.7"
@ -1058,10 +1074,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "hyper"
version = "0.14.19"
name = "humantime-serde"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42dc3c131584288d375f2d07f822b0cb012d8c6fb899a5b9fdb3cb7eb9b6004f"
checksum = "57a3db5ea5923d99402c94e9feb261dc5ee9b4efa158b0315f788cf549cc200c"
dependencies = [
"humantime",
"serde",
]
[[package]]
name = "hyper"
version = "0.14.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac"
dependencies = [
"bytes",
"futures-channel",
@ -1158,20 +1184,6 @@ dependencies = [
"libc",
]
[[package]]
name = "insta"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4126dd76ebfe2561486a1bd6738a33d2029ffb068a99ac446b7f8c77b2e58dbc"
dependencies = [
"console",
"once_cell",
"serde",
"serde_json",
"serde_yaml",
"similar",
]
[[package]]
name = "instant"
version = "0.1.12"
@ -1243,12 +1255,6 @@ dependencies = [
"cc",
]
[[package]]
name = "linked-hash-map"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
[[package]]
name = "lock_api"
version = "0.4.7"
@ -1546,9 +1552,9 @@ dependencies = [
[[package]]
name = "object"
version = "0.28.4"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424"
checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53"
dependencies = [
"memchr",
]
@ -1569,6 +1575,26 @@ dependencies = [
"pin-project-lite",
]
[[package]]
name = "os_pipe"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb233f06c2307e1f5ce2ecad9f8121cffbbee2c95428f44ea85222e460d0d213"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "os_pipe"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c92f2b54f081d635c77e7120862d48db8e91f7f21cef23ab1b4fe9971c59f55"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "os_str_bytes"
version = "6.1.0"
@ -1733,36 +1759,6 @@ version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]]
name = "predicates"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5aab5be6e4732b473071984b3164dbbfb7a3674d30ea5ff44410b6bcd960c3c"
dependencies = [
"difflib",
"float-cmp",
"itertools",
"normalize-line-endings",
"predicates-core",
"regex",
]
[[package]]
name = "predicates-core"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da1c2388b1513e1b605fcec39a95e0a9e8ef088f71443ef37099fa9ae6673fcb"
[[package]]
name = "predicates-tree"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d86de6de25020a36c6d3643a86d9a6a9f552107c0559c60ea03551b5e16c032"
dependencies = [
"predicates-core",
"termtree",
]
[[package]]
name = "proc-macro-crate"
version = "1.1.3"
@ -1956,6 +1952,30 @@ dependencies = [
"rand_core 0.5.1",
]
[[package]]
name = "rayon"
version = "1.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d"
dependencies = [
"autocfg",
"crossbeam-deque",
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-utils",
"num_cpus",
]
[[package]]
name = "redox_syscall"
version = "0.2.13"
@ -2095,18 +2115,6 @@ dependencies = [
"syn",
]
[[package]]
name = "serde_yaml"
version = "0.8.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "707d15895415db6628332b737c838b88c598522e4dc70647e59b72312924aebc"
dependencies = [
"indexmap",
"ryu",
"serde",
"yaml-rust",
]
[[package]]
name = "sha1"
version = "0.6.1"
@ -2131,6 +2139,16 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "shared_child"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6be9f7d5565b1483af3e72975e2dee33879b3b86bd48c0929fccf6585d79e65a"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "shlex"
version = "1.1.0"
@ -2176,6 +2194,32 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043"
[[package]]
name = "snapbox"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "767a1d5da232b6959cd1bd5c9e8db8a7cce09c3038e89deedb49a549a2aefd93"
dependencies = [
"concolor",
"content_inspector",
"dunce",
"filetime",
"normalize-line-endings",
"os_pipe 1.0.1",
"similar",
"snapbox-macros",
"tempfile",
"wait-timeout",
"walkdir",
"yansi",
]
[[package]]
name = "snapbox-macros"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c01dea7e04cbb27ef4c86e9922184608185f7cd95c1763bc30d727cda4a5e930"
[[package]]
name = "socket2"
version = "0.4.4"
@ -2322,12 +2366,6 @@ dependencies = [
"phf_codegen",
]
[[package]]
name = "termtree"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "507e9898683b6c43a9aa55b64259b721b52ba226e0f3779137e50ad114a4c90b"
[[package]]
name = "textwrap"
version = "0.15.0"
@ -2454,6 +2492,18 @@ dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.14.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5376256e44f2443f8896ac012507c19a012df0fe8758b55246ae51a2279db51f"
dependencies = [
"combine",
"indexmap",
"itertools",
"serde",
]
[[package]]
name = "tonic"
version = "0.7.2"
@ -2616,6 +2666,22 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
[[package]]
name = "trycmd"
version = "0.13.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffb4185126cc904642173a54c185083f410c86d1202ada6761aacf7c40829f13"
dependencies = [
"glob",
"humantime",
"humantime-serde",
"rayon",
"serde",
"shlex",
"snapbox",
"toml_edit",
]
[[package]]
name = "uds_windows"
version = "1.0.2"
@ -2935,13 +3001,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3"
[[package]]
name = "yaml-rust"
version = "0.4.5"
name = "yansi"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
dependencies = [
"linked-hash-map",
]
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
[[package]]
name = "zbus"

View File

@ -64,18 +64,11 @@ notify-rust = "4.5.8"
embed-resource = "1.7.3"
[dev-dependencies]
assert_cmd = "1.0.1"
clap_complete = "3.0.4"
clap_complete_fig = "3.0.2"
insta = "1.7.1"
predicates = "2.0.0"
wait-timeout = "0.2.0"
[dev-dependencies.clap]
version = "3.0.10"
features = [
"derive",
]
clap_complete = "3.2.3"
clap_complete_fig = "3.2.4"
duct = "0.13.5"
tempfile = "3.3.0"
trycmd = "0.13.4"
[features]
dev-console = ["console-subscriber"]

View File

@ -1,6 +1,4 @@
use std::{ffi::OsString, path::PathBuf};
use clap::Parser;
use std::path::PathBuf;
const OPTSET_FILTERING: &str = "FILTERING";
const OPTSET_COMMAND: &str = "COMMAND";
@ -10,80 +8,143 @@ const OPTSET_OUTPUT: &str = "OUTPUT";
const OPTSET_BEHAVIOUR: &str = "BEHAVIOUR";
const OPTSET_WORKSPACES: &str = "WORKSPACES";
#[derive(Debug, Clone, Parser)]
#[clap(name = "cargo-watch", bin_name = "cargo", version)]
pub struct App {
#[clap(subcommand)]
pub command: Command,
}
#[derive(Debug, Clone, clap::Subcommand)]
#[clap(name = "cargo-watch", bin_name = "cargo", version)]
pub enum Command {
#[clap(name = "watch")]
Watch(Args),
}
// --watch-when-idle is now default
#[derive(Debug, Clone, clap::Args)]
#[clap(author, version, about, long_about = None)]
#[derive(Debug, Clone, clap::Parser)]
#[clap(name = "cargo-watch", about, version)]
pub struct Args {
#[clap(hide = true, long = "testing-only--once")]
pub once: bool,
/// Show the help
#[clap(
short = 'h',
long = "help",
help_heading = OPTSET_DEBUGGING,
)]
pub help: bool,
/// Show the version
#[clap(
short = 'V',
long = "version",
help_heading = OPTSET_DEBUGGING,
)]
pub version: bool,
/// Clear the screen before each run
#[clap(short = 'c', long = "clear", help_heading = OPTSET_OUTPUT)]
#[clap(
short = 'c',
long = "clear",
help_heading = OPTSET_OUTPUT,
)]
pub clear: bool,
/// Show debug output
#[clap(long = "debug", help_heading = OPTSET_DEBUGGING)]
#[clap(
long = "debug",
help_heading = OPTSET_DEBUGGING,
)]
pub debug: bool,
/// Show paths that changed
#[clap(long = "why", help_heading = OPTSET_DEBUGGING)]
#[clap(
long = "why",
help_heading = OPTSET_DEBUGGING,
)]
pub why: bool,
/// Ignore nothing, not even target/ and .git/
#[clap(long = "ignore-nothing", help_heading = OPTSET_FILTERING)]
#[clap(
long = "ignore-nothing",
help_heading = OPTSET_FILTERING,
)]
pub ignore_nothing: bool,
/// Dont use .gitignore files
#[clap(long = "no-gitignore", help_heading = OPTSET_FILTERING)]
#[clap(
long = "no-gitignore",
help_heading = OPTSET_FILTERING,
)]
pub no_gitignore: bool,
/// Dont use .ignore files
#[clap(long = "no-ignore", help_heading = OPTSET_FILTERING)]
#[clap(
long = "no-ignore",
help_heading = OPTSET_FILTERING,
)]
pub no_ignore: bool,
/// Dont restart command while its still running
#[clap(long = "no-restart", help_heading = OPTSET_BEHAVIOUR)]
#[clap(
long = "no-restart",
help_heading = OPTSET_BEHAVIOUR,
)]
pub no_restart: bool,
/// Reserves for workspace support
#[clap(long = "all", hide = true, help_heading = OPTSET_WORKSPACES)]
#[clap(
long = "all",
hide = true,
help_heading = OPTSET_WORKSPACES,
)]
pub packages_all: bool,
/// Force use of polling for file changes
#[clap(long = "poll", help_heading = OPTSET_BEHAVIOUR)]
#[clap(
long = "poll",
help_heading = OPTSET_BEHAVIOUR,
)]
pub poll: bool,
/// Postpone first run until a file changes
#[clap(long = "postpone", help_heading = OPTSET_BEHAVIOUR)]
#[clap(
long = "postpone",
help_heading = OPTSET_BEHAVIOUR,
)]
pub postpone: bool,
/// Sleep some time before running commands.
///
/// This adds a delay after a change triggers a run, before actually running the command set.
/// Equivalent to `-s 'sleep 1'`, except it doesn't spawn a command and is portable.
#[clap(
long,
value_name = "seconds",
forbid_empty_values = true,
help_heading = OPTSET_BEHAVIOUR,
)]
pub delay_run: Option<u64>,
/// Quit after a set amount of triggers.
///
/// This is mainly useful for testing. Note that it will quit after number "triggers", not
/// "runs". In cases where a trigger does nothing (doesn't restart the command set), it will
/// still count down one.
#[clap(
long,
value_name = "number",
forbid_empty_values = true,
help_heading = OPTSET_BEHAVIOUR,
)]
pub quit_after_n: Option<u8>,
/// Feature(s) passed to cargo invocations
///
/// This is passed to cargo commands specified with `-x` only, and
/// which start with `b`, `check`, `doc`, `r`, `test`, or `install`.
#[clap(long = "features", help_heading = OPTSET_COMMAND)]
#[clap(
long = "features",
help_heading = OPTSET_COMMAND,
)]
pub features: Vec<String>,
/// Suppress output from cargo watch itself
///
/// By default, cargo watch will print a message to stderr when the
/// command starts and finishes.
#[clap(short = 'q', long = "quiet", help_heading = OPTSET_OUTPUT)]
#[clap(
short = 'q',
long = "quiet",
help_heading = OPTSET_OUTPUT,
)]
pub quiet: bool,
/// Cargo command(s) to execute on changes
@ -181,69 +242,114 @@ pub struct Args {
/// Shell to use for --shell commands, or `none` for direct execution.
///
/// This applies only to `--shell` / `-s` commands, `--exec` / `-x` cargo commands are
/// executed directly, without a shell. The option applies to all subsequent shell
/// commands:
/// This applies only to --shell|-s commands; --exec|-x cargo commands are executed directly,
/// without a shell. The option applies to all *subsequent* shell commands:
///
/// cargo watch --use-shell=zsh -s one -s two
/// $ cargo watch --use-shell=zsh -s one -s two
///
/// will use zsh for commands one and two, but:
///
/// cargo watch -s one --use-shell=zsh -s two
/// $ cargo watch -s one --use-shell=zsh -s two
///
/// will only use zsh for the second one.
///
/// As a convenience, if only one --use-shell is provided and it is used after all command
/// arguments, it is interpreted as if it was given first:
///
/// cargo watch -s one -s two --use-shell=zsh
/// $ cargo watch -s one -s two --use-shell=zsh
///
/// will run both one and two with zsh. (Otherwise the option would do nothing.)
///
/// The first word must be the shell program, but it can be followed by options to pass to
/// the shell program:
///
/// cargo watch --use-shell='bash -s globext' -- 'ls **'
/// $ cargo watch --use-shell='bash -s globext' -- 'ls **'
///
/// On Windows, defaults to Powershell. Elsewhere, defaults to $SHELL, falling back to `sh`
/// if not available.
// TODO: check that the bash shopt makes any sense
// TODO: check that clap doesn't eat all this nice layouting
#[clap(long = "use-shell", value_name = "shell", help_heading = OPTSET_ENVIRONMENT,
#[clap(
short = 'S',
long = "use-shell",
value_name = "shell",
multiple_occurrences = true,
forbid_empty_values = true,
min_values = 1,
number_of_values = 1,
)]
help_heading = OPTSET_COMMAND,
)]
pub use_shell: Vec<String>,
/// Change working directory of the command
///
/// This defaults to the crate or workspace root.
#[clap(short = 'C', long = "workdir", value_name = "path", help_heading = OPTSET_ENVIRONMENT)]
#[clap(
short = 'C',
long = "workdir",
value_name = "path",
help_heading = OPTSET_ENVIRONMENT,
)]
pub workdir: Option<PathBuf>,
/// Send a desktop notification on command start and end
///
/// The message will include success or failure, with the exit code
/// returned by the command.
#[clap(short = 'N', long = "notify", help_heading = OPTSET_OUTPUT)]
#[cfg_attr(target_os = "freebsd", clap(hide = true))]
#[clap(
short = 'N',
long = "notify",
help_heading = OPTSET_OUTPUT,
)]
pub notif: bool,
/// Inject RUST_BACKTRACE=value into the command's environment
/// Inject environment variables into the commands' environments.
#[clap(
short = 'E',
long = "env",
value_name = "key=value",
multiple_occurrences = true,
forbid_empty_values = true,
min_values = 1,
number_of_values = 1,
help_heading = OPTSET_ENVIRONMENT,
)]
pub env_vars: Vec<String>,
/// Inject RUST_BACKTRACE=value into the commands' environments.
///
/// Examples: -B=1, -B=full
#[clap(short = 'B', help_heading = OPTSET_ENVIRONMENT)]
pub env_backtrace: bool,
#[clap(
short = 'B',
value_name = "RUST_BACKTRACE value",
forbid_empty_values = true,
help_heading = OPTSET_ENVIRONMENT,
)]
pub env_backtrace: Option<String>,
/// Inject RUST_LOG=value into the command's environment
/// Inject RUST_LOG=value into the commands' environments.
///
/// Examples: -L=debug, -L=info,cratename::module=debug
#[clap(short = 'L', help_heading = OPTSET_ENVIRONMENT)]
pub env_log: bool,
#[clap(
short = 'L',
value_name = "RUST_LOG value",
forbid_empty_values = true,
help_heading = OPTSET_ENVIRONMENT,
)]
pub env_log: Option<String>,
/// Dont inject CARGO_WATCH_* variables in the environment.
#[clap(
long = "no-auto-env",
help_heading = OPTSET_ENVIRONMENT,
)]
pub no_auto_env: bool,
/// Full command to run. -x and -s will be ignored!
#[clap(raw = true, value_name = "trailing command", help_heading = OPTSET_COMMAND)]
pub cmd_trail: Option<OsString>,
#[clap(
raw = true,
value_name = "trailing command",
help_heading = OPTSET_COMMAND,
)]
pub cmd_trail: Option<String>,
}

View File

@ -1,15 +1,24 @@
pub use init::init;
pub use runtime::runtime;
use argfile::{expand_args_from, parse_fromfile};
use clap::Parser;
use tracing::info;
use crate::args::*;
use clap::Parser;
pub use init::init;
pub use runtime::runtime;
mod init;
mod runtime;
pub fn get_args() -> (Args, Vec<&'static str>) {
let args = wild::args_os();
let args = argfile::expand_args_from(args, argfile::parse_fromfile, argfile::PREFIX).unwrap();
let mut args = expand_args_from(args, parse_fromfile, argfile::PREFIX).unwrap();
// Filter extraneous arg when invoked by cargo
// `cargo-watch` gives ["/path/to/cargo-watch"]
// `cargo watch` gives ["/path/to/cargo-watch", "watch"]
if args.len() > 1 && args[1] == "watch" {
args.remove(1);
}
let command_order = args
.iter()
@ -21,7 +30,8 @@ pub fn get_args() -> (Args, Vec<&'static str>) {
})
.collect::<Vec<_>>();
let app = App::parse_from(args);
let Command::Watch(args) = app.command;
info!(?args, "arguments before parsing");
let args = Args::parse_from(args);
info!(?args, ?command_order, "arguments parsed");
(args, command_order)
}

View File

@ -1,6 +1,16 @@
use std::{convert::Infallible, env, time::Duration};
use std::{
convert::Infallible,
env,
path::PathBuf,
sync::{
atomic::{AtomicU8, Ordering},
Arc,
},
time::Duration,
};
use miette::{miette, IntoDiagnostic, Report, Result};
use tracing::{debug, info};
use watchexec::{
action::{Action, Outcome, PostSpawn, PreSpawn},
command::{Command, Shell},
@ -20,11 +30,18 @@ use crate::args::Args;
pub fn runtime(args: &Args, command_order: Vec<&'static str>) -> Result<RuntimeConfig> {
let mut config = RuntimeConfig::default();
let mut pathset = args.watch.clone();
if pathset.is_empty() {
pathset = vec![PathBuf::from(".")];
}
config.pathset(&pathset);
let features = if args.features.is_empty() {
None
} else {
Some(args.features.join(","))
};
info!(?features, "features");
let mut used_shell = if args.use_shell.len() == 1 && command_order.last() == Some(&"use-shell")
{
@ -32,47 +49,55 @@ pub fn runtime(args: &Args, command_order: Vec<&'static str>) -> Result<RuntimeC
} else {
None
};
debug!(?used_shell, "initial used shell");
let mut commands = Vec::with_capacity(args.cmd_cargo.len() + args.cmd_shell.len());
if let Some(trailing) = &args.cmd_trail {
info!("use the trailing command");
config.command(shell_command(&trailing, args.use_shell.first())?);
} else if args.cmd_cargo.is_empty() && args.cmd_shell.is_empty() {
info!("use the default command");
config.command(cargo_command("check", &features)?);
} else {
info!("use the optioned commands");
let mut commands = Vec::with_capacity(args.cmd_cargo.len() + args.cmd_shell.len());
let mut cargos = args.cmd_cargo.iter();
let mut shells = args.cmd_shell.iter();
let mut use_shells = args.use_shell.iter();
let mut cargos = args.cmd_cargo.iter();
let mut shells = args.cmd_shell.iter();
let mut use_shells = args.use_shell.iter();
for c in command_order {
match c {
"cargo" => {
commands.push(cargo_command(
cargos
.next()
.ok_or_else(|| miette!("Argument-order mismatch, this is a bug"))?,
&features,
)?);
for c in command_order {
match c {
"cargo" => {
commands.push(cargo_command(
cargos
.next()
.ok_or_else(|| miette!("Argument-order mismatch, this is a bug"))?,
&features,
)?);
}
"shell" => {
commands.push(shell_command(
shells
.next()
.ok_or_else(|| miette!("Argument-order mismatch, this is a bug"))?,
used_shell.as_ref(),
)?);
}
"use-shell" => {
used_shell.replace(
use_shells
.next()
.ok_or_else(|| miette!("Argument-order mismatch, this is a bug"))?
.clone(),
);
}
_ => {}
}
"shell" => {
commands.push(shell_command(
shells
.next()
.ok_or_else(|| miette!("Argument-order mismatch, this is a bug"))?,
&used_shell,
)?);
}
"use-shell" => {
used_shell.replace(
use_shells
.next()
.ok_or_else(|| miette!("Argument-order mismatch, this is a bug"))?
.clone(),
);
}
_ => {}
}
config.commands(commands);
}
config.commands(commands);
config.pathset(&args.watch);
if let Some(delay) = &args.delay {
let delay = if delay.ends_with("ms") {
let d: u64 = delay.trim_end_matches("ms").parse().into_diagnostic()?;
@ -100,6 +125,7 @@ pub fn runtime(args: &Args, command_order: Vec<&'static str>) -> Result<RuntimeC
// config.command_grouped(args.process_group);
let quiet = args.quiet;
let clear = args.clear;
let notif = args.notif;
let on_busy = if args.no_restart {
@ -117,7 +143,8 @@ pub fn runtime(args: &Args, command_order: Vec<&'static str>) -> Result<RuntimeC
// .unwrap_or(SubSignal::Terminate);
let print_events = args.why;
let once = args.once;
let quit_after_n = args.quit_after_n.map(|n| Arc::new(AtomicU8::new(n)));
let delay_run = args.delay_run.map(Duration::from_secs);
config.on_action(move |action: Action| {
let fut = async { Ok::<(), Infallible>(()) };
@ -128,11 +155,6 @@ pub fn runtime(args: &Args, command_order: Vec<&'static str>) -> Result<RuntimeC
}
}
if once {
action.outcome(Outcome::both(Outcome::Start, Outcome::wait(Outcome::Exit)));
return fut;
}
let signals: Vec<MainSignal> = action.events.iter().flat_map(|e| e.signals()).collect();
let has_paths = action
.events
@ -178,7 +200,7 @@ pub fn runtime(args: &Args, command_order: Vec<&'static str>) -> Result<RuntimeC
Some(ProcessEnd::Exception(ex)) => {
(format!("Command ended by exception {:#x}", ex), true)
}
Some(ProcessEnd::Success) => ("Command was successful".to_string(), false),
Some(ProcessEnd::Success) => ("Command was successful".to_string(), !quiet),
None => ("Command completed".to_string(), false),
};
@ -198,35 +220,61 @@ pub fn runtime(args: &Args, command_order: Vec<&'static str>) -> Result<RuntimeC
});
}
if let Some(runs) = quit_after_n.clone() {
if runs.load(Ordering::SeqCst) == 0 {
debug!("quitting after n triggers");
action.outcome(Outcome::Exit);
return fut;
}
}
action.outcome(Outcome::DoNothing);
return fut;
}
}
let when_running = match (clear, on_busy) {
(_, "do-nothing") => Outcome::DoNothing,
(true, "restart") => {
Outcome::both(Outcome::Stop, Outcome::both(Outcome::Clear, Outcome::Start))
if let Some(runs) = quit_after_n.clone() {
if runs.load(Ordering::SeqCst) == 0 {
debug!("quitting after n triggers");
action.outcome(Outcome::Exit);
return fut;
}
(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), start)
} else {
start
};
let when_idle = start.clone();
let when_running = match on_busy {
"do-nothing" => Outcome::DoNothing,
"restart" => Outcome::both(Outcome::Stop, start),
// "signal" => Outcome::Signal(signal),
// "queue" => Outcome::wait(start),
_ => Outcome::DoNothing,
};
if let Some(runs) = quit_after_n.clone() {
let remaining = runs.fetch_sub(1, Ordering::SeqCst);
if remaining > 0 {
debug!(?remaining, "getting closer to quitting");
}
}
action.outcome(Outcome::if_running(when_running, when_idle));
fut
});
let no_env = false; // args.is_present("no-environment");
let no_env = args.no_auto_env;
config.on_pre_spawn(move |prespawn: PreSpawn| async move {
if !no_env {
let envs = summarise_events_to_env(prespawn.events.iter());
@ -237,6 +285,10 @@ pub fn runtime(args: &Args, command_order: Vec<&'static str>) -> Result<RuntimeC
}
}
if !quiet {
eprintln!("[[Running `{}`]]", prespawn.command);
}
Ok::<(), Infallible>(())
});
@ -279,7 +331,9 @@ fn cmd_shell(s: String) -> Shell {
Shell::Unix(s)
}
fn cargo_command(arg: &String, features: &Option<String>) -> Result<Command> {
fn cargo_command(arg: &str, features: &Option<String>) -> Result<Command> {
debug!(command=?arg, ?features, "building a cargo command");
let mut lexed = shlex::split(arg).ok_or_else(|| miette!("Command is not valid: {:?}", arg))?;
let subc = lexed
.get(0)
@ -305,7 +359,9 @@ fn cargo_command(arg: &String, features: &Option<String>) -> Result<Command> {
})
}
fn shell_command(arg: &String, use_shell: &Option<String>) -> Result<Command> {
fn shell_command(arg: &str, use_shell: Option<&String>) -> Result<Command> {
debug!(command=?arg, ?use_shell, "building a shelled command");
let (shell, shell_args) = if let Some(sh) = use_shell {
let mut lexed_shell = shlex::split(&sh)
.ok_or_else(|| miette!("Shell invocation syntax is invalid: {:?}", sh))?;
@ -334,6 +390,6 @@ fn shell_command(arg: &String, use_shell: &Option<String>) -> Result<Command> {
Ok(Command::Shell {
shell,
args: shell_args,
command: arg.clone(),
command: arg.into(),
})
}