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        dead_code_results,
60        styling_artifacts,
61        pre_computed_duplication,
62        workspaces,
63        workspace_diagnostics,
64    } = input;
65    let timings = HealthPipelineTimings {
66        config: config_ms,
67        discover: discover_ms,
68        parse: parse_ms,
69        parse_cpu: parse_cpu_ms,
70        shared_parse,
71    };
72
73    let scope = prepare_health_scope(opts, &config, &files, scope_inputs);
74
75    let HealthPreparedCore {
76        findings_data,
77        analysis_data,
78        derived_sections,
79        vital_data,
80        report_coverage_gaps,
81        enforce_coverage_gaps,
82        has_istanbul_coverage,
83        needs_file_scores,
84    } = prepare_health_core_sections(HealthCoreSectionsInput {
85        opts,
86        config: &config,
87        files: &files,
88        modules: &modules,
89        scope: &scope,
90        pre_computed_analysis,
91        pre_computed_duplication,
92        seams,
93    })?;
94
95    let HealthOutputContext { build, sections } =
96        prepare_health_output_context(HealthOutputContextInput {
97            config: &config,
98            modules: &modules,
99            scope: &scope,
100            needs_file_scores,
101            report_coverage_gaps,
102            has_istanbul_coverage,
103            findings_data,
104            analysis_data,
105            derived_sections,
106            vital_data,
107            timings,
108            workspaces: &workspaces,
109            start: &start,
110        });
111
112    let output = build_health_output_parts(opts, &build, sections);
113
114    Ok(finalize_health_result(HealthFinalizeInput {
115        opts,
116        config,
117        files: &files,
118        modules: &modules,
119        scope,
120        output,
121        elapsed: start.elapsed(),
122        should_fail_on_coverage_gaps: enforce_coverage_gaps,
123        dead_code_results: dead_code_results.as_ref(),
124        styling_artifacts: styling_artifacts.as_ref(),
125        workspace_diagnostics,
126    }))
127}
128
129#[cfg(test)]
130#[path = "execute_tests.rs"]
131mod execute_tests;