Skip to main content

fallow_engine/health/
execute.rs

1//! Command-neutral health analysis execution.
2//!
3//! This module owns the health pipeline (scoring, hotspots, targets, grouping,
4//! coverage gaps, vital signs, report assembly) so that the CLI and the
5//! programmatic API can both run health analysis without the CLI orchestration
6//! layer. CLI-only concerns (config loading, telemetry sinks, the runtime
7//! coverage sidecar, ownership-resolver construction, and error rendering) are
8//! threaded in through the [`HealthSeams`] carrier and the typed result.
9
10use std::process::ExitCode;
11use std::time::Instant;
12
13use super::{HealthExecutionOptions, HealthSeams};
14
15use super::core_pipeline::{
16    HealthCoreSectionsInput, HealthPreparedCore, prepare_health_core_sections,
17};
18use super::output_build::{
19    HealthOutputContext, HealthOutputContextInput, build_health_output_parts,
20    prepare_health_output_context,
21};
22use super::pipeline::{HealthPipelineInputs, HealthPipelineTimings, HealthScopeInputs};
23use super::result::{HealthFinalizeInput, finalize_health_result};
24use super::scope::prepare_health_scope;
25
26pub type HealthOptions<'a> = HealthExecutionOptions<'a>;
27
28/// Typed health analysis result generic over the CLI-owned grouping resolver.
29pub type HealthResultGeneric<R> = super::HealthAnalysisResult<R>;
30
31/// Run the command-neutral health analysis pipeline.
32///
33/// Config loading, discovery, and parsing are the CLI's responsibility (they
34/// touch the parser cache and config telemetry); the caller passes the resolved
35/// [`HealthPipelineInputs`] plus the pre-resolved [`HealthScopeInputs`] and the
36/// [`HealthSeams`] callbacks. The returned result carries the typed health
37/// report plus the caller's grouping resolver for downstream rendering.
38///
39/// # Errors
40///
41/// Returns the CLI exit code emitted by a failing analysis or invalid input.
42pub fn execute_health_inner<'a, R: super::HealthGroupResolver>(
43    opts: &HealthOptions<'a>,
44    input: HealthPipelineInputs,
45    scope_inputs: HealthScopeInputs<'a, R>,
46    seams: &HealthSeams<'_>,
47) -> Result<HealthResultGeneric<R>, ExitCode> {
48    let start = Instant::now();
49    let HealthPipelineInputs {
50        config,
51        files,
52        modules,
53        config_ms,
54        discover_ms,
55        parse_ms,
56        parse_cpu_ms,
57        shared_parse,
58        pre_computed_analysis,
59        workspace_diagnostics,
60    } = input;
61    let timings = HealthPipelineTimings {
62        config: config_ms,
63        discover: discover_ms,
64        parse: parse_ms,
65        parse_cpu: parse_cpu_ms,
66        shared_parse,
67    };
68
69    let scope = prepare_health_scope(opts, &config, &files, scope_inputs);
70
71    let HealthPreparedCore {
72        findings_data,
73        analysis_data,
74        derived_sections,
75        vital_data,
76        report_coverage_gaps,
77        enforce_coverage_gaps,
78        has_istanbul_coverage,
79        needs_file_scores,
80    } = prepare_health_core_sections(HealthCoreSectionsInput {
81        opts,
82        config: &config,
83        files: &files,
84        modules: &modules,
85        scope: &scope,
86        pre_computed_analysis,
87        seams,
88    })?;
89
90    let HealthOutputContext { build, sections } =
91        prepare_health_output_context(HealthOutputContextInput {
92            config: &config,
93            files: &files,
94            modules: &modules,
95            scope: &scope,
96            needs_file_scores,
97            report_coverage_gaps,
98            has_istanbul_coverage,
99            findings_data,
100            analysis_data,
101            derived_sections,
102            vital_data,
103            timings,
104            start: &start,
105        });
106
107    let output = build_health_output_parts(opts, &build, sections);
108
109    Ok(finalize_health_result(HealthFinalizeInput {
110        opts,
111        config,
112        files: &files,
113        modules: &modules,
114        scope,
115        output,
116        elapsed: start.elapsed(),
117        should_fail_on_coverage_gaps: enforce_coverage_gaps,
118        workspace_diagnostics,
119    }))
120}
121
122#[cfg(test)]
123#[path = "execute_tests.rs"]
124mod execute_tests;