1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
//! `cargo-each`: run a command over Cargo workspace members or JSON Lines
//! records.
//!
//! `cargo-each` resolves either a package selection expressed with the same
//! selectors as `cargo build` or a stream of JSON objects. It optionally
//! narrows Cargo selections with package predicates and runs a command once per
//! member, matching Cargo target, whole selected set, or JSON record. It
//! replaces hand-rolled shell loops with one cross-platform command.
//!
//! `cargo-each` ships as an executable only; it is a cargo subcommand, not a
//! library dependency.
//!
//! # Installation
//!
//! ```text
//! cargo install cargo-each
//! ```
//!
//! # Usage
//!
//! ```text
//! cargo each [SELECTION | JSON INPUT] [FILTERS] [EXECUTION] -- <COMMAND> [ARG...]
//! ```
//!
//! Everything after `--` is the command template; `cargo-each` spawns it
//! directly (argv, not a shell string) after substituting placeholders.
//! Input is either a Cargo workspace selection or JSON Lines records; the two
//! sources are deliberately mutually exclusive.
//!
//! ## Selection (mirrors `cargo build`)
//!
//! - `-p` / `--package <SPEC>` — select a member. Repeatable. `SPEC` is a
//! package name, a `name@version` spec, or a Unix glob (`tokio-*`).
//! - `--package-file <PATH>` — read package specs from a UTF-8 file, one per
//! nonempty line. Repeatable; specs are unioned with `--package`. An empty
//! file explicitly selects no members. One leading UTF-8 byte-order mark is
//! ignored.
//! - `--workspace` / `--all` — select every workspace member.
//! - `--exclude <SPEC>` — drop a member (with `--workspace`). Repeatable.
//! - `--none` — explicitly select zero members (a no-op that exits 0).
//!
//! When nothing is named the default is cargo `default-members`, exactly
//! like `cargo build`; pass `--workspace` for every member. A selector that
//! matches no member is an error, so typos fail loudly. Package files contain
//! package specs only: comments, command-line tokens, malformed input, and
//! missing, unreadable, or non-UTF-8 files are errors.
//!
//! ## Filters
//!
//! `--filter` accepts Boolean expressions using `not`, `and`, `or`, and
//! parentheses, with conventional precedence. Repeated `--filter` expressions
//! are AND-combined; use `not` to exclude matching members. Metadata values
//! containing whitespace or Boolean syntax can be double-quoted. Expression
//! atoms:
//!
//! - `lib` / `bin` / `target-kind:<kind>` — target-kind membership.
//! - `doctest` — Cargo reports at least one target with doctests enabled.
//! - `publishable` — Cargo permits publishing the package.
//! - `feature:<name>` — the package declares the feature.
//! - `dep:<name>` — the member declares `<name>` as a dependency.
//! - `metadata:<dotted.key>` — `package.metadata.<dotted.key>` is present.
//! - `metadata:<dotted.key>=<value>` — that key equals `<value>` (numeric
//! compare when both sides parse as a number, else string compare).
//!
//! ## Execution modes
//!
//! - *per-package* (default): run the command once per selected member, in
//! name order, substituting the per-package placeholders below.
//! - `--once`: run the command exactly once when the set is non-empty (skip
//! when empty), using the `{packages}` placeholder to inject the selection.
//! - `--json-lines` / `--json-lines-file`: bypass Cargo selection and run once
//! per JSON object, substituting top-level string fields through
//! `{json:key}`.
//! - `--each-target <KIND>`: run once per matching Cargo target, using
//! `{target}` plus the package placeholders. Repeated kinds are OR-combined;
//! `--target-required-feature` further narrows targets.
//!
//! `--keep-going` runs every invocation and exits non-zero if any failed
//! (default is fail-fast). `--jobs <N|auto>` bounds concurrent per-package,
//! per-target, or JSON-record work. Omitting it runs exactly one invocation at
//! a time; `auto` resolves once to the machine's available parallelism.
//! Detection failure is reported explicitly without falling back.
//! `--timeout <DURATION>` terminates
//! each invocation's Windows job object or Unix process group independently
//! (`250ms`, `30s`, or `2m`). Unix descendants can escape a process group by
//! starting a new session, so timeout cleanup is best-effort for those escaped
//! descendants.
//! `--chdir` runs each per-package or per-target command from that member crate
//! root; `--dry-run` prints commands without running them.
//!
//! ## Placeholders
//!
//! Substituted inside each command argument:
//!
//! - `{name}` — bare package name (per-package and per-target).
//! - `{spec}` — `name@version` (per-package and per-target).
//! - `{version}` — package version (per-package and per-target).
//! - `{manifest}` — absolute member `Cargo.toml` path (per-package and
//! per-target).
//! - `{target}` — Cargo target name (per-target).
//! - `{packages}` — the cargo selection flags for the resolved set
//! (`--workspace` for the whole workspace, else `--package name@version …`);
//! valid only in `--once` mode and only as a standalone argument.
//! - `{workspace-rust-version}` — the root `[workspace.package].rust-version`,
//! or root `[package].rust-version` in a single-package repository; valid in
//! Cargo-backed per-package, per-target, and `--once` modes. Expands to an
//! empty string when the root declaration is absent.
//! - `{json:key}` — the top-level string field named `key` in the current
//! record; valid only in JSON-record mode.
//!
//! Using a placeholder in the wrong mode is a usage error. Every `{json:…}`
//! sequence is validated as a JSON placeholder. Other unrecognized `{…}`
//! sequences (a typo, or a literal brace an argument needs) pass through
//! verbatim to the spawned command — there is no brace-escape, so this
//! passthrough is part of the contract.
//!
//! # Behavior
//!
//! An empty resolved selection (via `--none`, or a filter that removes every
//! member) is a **successful no-op**: `cargo-each` prints a one-line note and
//! exits 0. This is what lets callers drop bespoke nothing-to-do guards.
//! Workspace Rust-version resolution is lazy: it runs only when the command
//! uses `{workspace-rust-version}` and the resolved plan has work. An absent
//! root declaration expands to an empty string. When a root floor exists,
//! every member's resolved minimum must be present and no newer than that
//! floor; malformed or inconsistent declarations remain errors. Substitution
//! is textual, so an empty value does not remove its surrounding argv element.
//! Placeholder mode validation still runs before an empty-plan no-op.
//!
//! JSON-record mode requires one JSON object per nonempty input line and does
//! not load Cargo metadata. Referenced `{json:key}` fields must exist and be
//! strings. Invalid input is rejected before any command is spawned, records
//! retain input order and duplicates, and an empty record set is a successful
//! no-op.
//!
//! The effective worker count is the requested `--jobs` value capped by plan
//! size. An effective count of one uses sequential
//! execution with inherited standard input, output, and error even when the
//! requested value was larger. A genuinely parallel count disconnects child input and
//! buffers stdout and stderr; complete blocks are emitted in deterministic
//! plan order. Fail-fast stops launching after the first
//! observed failure, waits for running work, and chooses the final failure by
//! plan order. `--keep-going` runs the complete plan. Worker panics and
//! unexpected worker-channel disconnections become infrastructure-failure
//! outcomes instead of blocking the scheduler. Worker launch failures retain
//! output already collected at earlier plan indices. Parallel work runs in
//! plan-contiguous waves capped by the effective worker count; each completed
//! wave is emitted and dropped before the next wave starts, bounding retained
//! temporary-file storage. Untimed effective-one execution uses an ordinary
//! child, preserving terminal foreground behavior and Ctrl-C delivery; a
//! post-spawn wait failure makes one best-effort child-termination request.
//! Timed and genuinely parallel commands use a job or process group. Without
//! `--timeout`, cargo-each observes only the leader and does not kill background
//! descendants. Every genuinely parallel invocation redirects stdout and
//! stderr directly to separate unique temporary files.
//! Child writers and parent readers are separately reopened so parent seeks
//! cannot move descendant write positions. Cargo-each records each file's
//! current length when the leader completes (or immediately after the timeout
//! termination request returns), then reads exactly that finite snapshot in
//! plan order without loading unbounded output into memory. Later writes by
//! background or escaped descendants are outside the snapshot. RAII removes
//! cargo-each's directory entry, but a preserved descendant can keep the
//! backing storage allocated and growing until its inherited writer closes.
//! Capture create, reopen, length, seek, and read failures are infrastructure
//! failures.
//!
//! At a timeout, cargo-each makes one termination request for the Windows job
//! or Unix process group and returns without waiting for operating-system
//! teardown to finish. A failed termination request is an infrastructure
//! failure; otherwise the invocation is reported as timed out. On Unix, an
//! uncollected timed-out leader may remain as a zombie until cargo-each exits,
//! consuming one temporary process-table entry per timed-out invocation.
//! Child commands inherit `PATH` explicitly. On Windows this makes relative
//! program lookup honor the inherited `PATH` order instead of preferring an
//! unrelated executable beside `cargo-each`.
//! Exit `0` means all work succeeded or there was no work. In fail-fast mode a
//! command failure returns its code, a timeout returns `1`, and usage,
//! configuration, spawn, or post-spawn infrastructure failures return `2`.
//! Under `--keep-going`, any failure maps the aggregate result to `1`.
//!
//! # Examples
//!
//! Run a per-manifest tool over every library crate:
//!
//! ```text
//! cargo each --workspace --filter lib -- \
//! cargo check-external-types --manifest-path {manifest}
//! ```
//!
//! Run one clippy invocation over a computed subset, skipping when it is empty:
//!
//! ```text
//! cargo each -p crate-a -p crate-b --once -- \
//! cargo clippy {packages} --all-targets -- -D warnings
//! ```
//!
//! Run every Cargo test target that requires the `loom` feature:
//!
//! ```text
//! cargo each --workspace --each-target test --target-required-feature loom -- \
//! cargo test -p {name} --test {target} --features loom
//! ```
use ExitCode;
use Parser;
use crateCargoCli;