@@ -20,6 +20,12 @@ static RED: LazyLock<ColorSpec> = LazyLock::new(|| color_spec(Color::Red, true))
2020static YELLOW : LazyLock < ColorSpec > = LazyLock :: new ( || color_spec ( Color :: Yellow , true ) ) ;
2121static 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]
2531pub 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]
144154pub 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
209217fn 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