Skip to content

Commit 8c9f241

Browse files
committed
bubble up exit code instead of exiting in library code
1 parent 9255ac4 commit 8c9f241

2 files changed

Lines changed: 29 additions & 16 deletions

File tree

src/lib.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ pub mod workspace;
2424
pub use cli::{ArgumentParser, Command, Options, parse_arguments};
2525
pub use package::{FeatureCombinationError, Package};
2626
pub use runner::{
27-
color_spec, error_counts, print_feature_matrix, print_summary, run_cargo_command,
27+
ExitCode, color_spec, error_counts, print_feature_matrix, print_summary, run_cargo_command,
2828
warning_counts,
2929
};
3030
pub use workspace::Workspace;
@@ -107,7 +107,7 @@ pub fn run(bin_name: &str) -> eyre::Result<()> {
107107
let target = detector.detect_target(&cargo_args_owned)?;
108108
let mut evaluator = RustcCfgEvaluator::default();
109109
let result = match options.command {
110-
Some(Command::Help | Command::Version) => Ok(()),
110+
Some(Command::Help | Command::Version) => Ok(None),
111111
Some(Command::FeatureMatrix { pretty }) => print_feature_matrix_for_target(
112112
&packages,
113113
pretty,
@@ -132,7 +132,8 @@ pub fn run(bin_name: &str) -> eyre::Result<()> {
132132
};
133133

134134
match result {
135-
Ok(()) => Ok(()),
135+
Ok(Some(exit_code)) => process::exit(exit_code),
136+
Ok(None) => Ok(()),
136137
Err(err) => {
137138
if let Some(e) = err.downcast_ref::<FeatureCombinationError>() {
138139
print_feature_combination_error(e);

src/runner.rs

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ static RED: LazyLock<ColorSpec> = LazyLock::new(|| color_spec(Color::Red, true))
2020
static YELLOW: LazyLock<ColorSpec> = LazyLock::new(|| color_spec(Color::Yellow, true));
2121
static GREEN: LazyLock<ColorSpec> = LazyLock::new(|| color_spec(Color::Green, true));
2222

23+
/// An optional process exit code.
24+
///
25+
/// `None` means success (exit 0), `Some(code)` means the process should exit
26+
/// with the given code.
27+
pub type ExitCode = Option<i32>;
28+
2329
/// Build a [`ColorSpec`] with the given foreground color and bold setting.
2430
#[must_use]
2531
pub fn color_spec(color: Color, bold: bool) -> ColorSpec {
@@ -139,13 +145,17 @@ pub(crate) fn print_feature_combination_error(err: &FeatureCombinationError) {
139145

140146
/// Print an aggregated summary for all executed feature combinations.
141147
///
148+
/// Returns the [`ExitCode`] of the first failing feature combination, or
149+
/// `None` if all combinations succeeded.
150+
///
142151
/// This function is used by [`run_cargo_command`] after all packages and
143152
/// feature sets have been processed.
153+
#[must_use]
144154
pub fn print_summary(
145155
summary: Vec<Summary>,
146156
mut stdout: termcolor::StandardStream,
147157
elapsed: Duration,
148-
) {
158+
) -> ExitCode {
149159
let num_packages = summary
150160
.iter()
151161
.map(|s| &s.package_name)
@@ -201,9 +211,7 @@ pub fn print_summary(
201211
}
202212
println!();
203213

204-
if let Some(exit_code) = first_bad_exit_code {
205-
std::process::exit(exit_code);
206-
}
214+
first_bad_exit_code
207215
}
208216

209217
fn print_package_cmd(
@@ -267,7 +275,7 @@ pub fn print_feature_matrix(
267275
packages: &[&cargo_metadata::Package],
268276
pretty: bool,
269277
packages_only: bool,
270-
) -> eyre::Result<()> {
278+
) -> eyre::Result<ExitCode> {
271279
let detector = RustcTargetDetector;
272280
let target = detector.detect_target(&Vec::new())?;
273281
let mut evaluator = RustcCfgEvaluator::default();
@@ -280,7 +288,7 @@ pub(crate) fn print_feature_matrix_for_target(
280288
packages_only: bool,
281289
target: &TargetTriple,
282290
evaluator: &mut impl crate::cfg_eval::CfgEvaluator,
283-
) -> eyre::Result<()> {
291+
) -> eyre::Result<ExitCode> {
284292
let per_package_features = packages
285293
.iter()
286294
.map(|pkg| {
@@ -317,14 +325,17 @@ pub(crate) fn print_feature_matrix_for_target(
317325
serde_json::to_string(&matrix)
318326
}?;
319327
println!("{matrix}");
320-
Ok(())
328+
Ok(None)
321329
}
322330

323331
/// Run a cargo command for all requested packages and feature combinations.
324332
///
325333
/// This function drives the main execution loop by spawning cargo for each
326334
/// feature set and collecting a [`Summary`] for every run.
327335
///
336+
/// Returns the [`ExitCode`] of the first failing feature combination, or
337+
/// `None` if all combinations succeeded.
338+
///
328339
/// # Errors
329340
///
330341
/// Returns an error if a cargo process can not be spawned or if IO operations
@@ -333,7 +344,7 @@ pub fn run_cargo_command(
333344
packages: &[&cargo_metadata::Package],
334345
cargo_args: Vec<&str>,
335346
options: &Options,
336-
) -> eyre::Result<()> {
347+
) -> eyre::Result<ExitCode> {
337348
// Public API: if called directly, resolve config for the host target.
338349
let detector = RustcTargetDetector;
339350
let cargo_args_owned: Vec<String> = cargo_args.iter().map(|s| (*s).to_string()).collect();
@@ -348,7 +359,7 @@ pub(crate) fn run_cargo_command_for_target(
348359
options: &Options,
349360
target: &TargetTriple,
350361
evaluator: &mut impl crate::cfg_eval::CfgEvaluator,
351-
) -> eyre::Result<()> {
362+
) -> eyre::Result<ExitCode> {
352363
let start = Instant::now();
353364

354365
// split into cargo and extra arguments after --
@@ -460,14 +471,15 @@ pub(crate) fn run_cargo_command_for_target(
460471
)?;
461472
stdout.flush().ok();
462473
}
463-
print_summary(summary, stdout, start.elapsed());
464-
std::process::exit(exit_status.code().unwrap_or(1));
474+
let code = print_summary(summary, stdout, start.elapsed())
475+
.or(exit_status.code())
476+
.unwrap_or(1);
477+
return Ok(Some(code));
465478
}
466479
}
467480
}
468481

469-
print_summary(summary, stdout, start.elapsed());
470-
Ok(())
482+
Ok(print_summary(summary, stdout, start.elapsed()))
471483
}
472484

473485
#[cfg(test)]

0 commit comments

Comments
 (0)