Skip to main content

fallow_cli/health_types/
grouped.rs

1//! Per-group health output for `--group-by`.
2//!
3//! When health is invoked with `--group-by package` (or any other grouping
4//! mode), the orchestrator partitions the project's files by the resolver and
5//! emits one [`HealthGroup`] per bucket. Each group carries its own
6//! [`VitalSigns`] and [`HealthScore`] computed from the files in that group
7//! alone, plus the per-file output (findings, file scores, hotspots, large
8//! functions, refactoring targets) restricted to the same subset.
9
10use serde::Serialize;
11
12use crate::health_types::{
13    FileHealthScore, HealthFinding, HealthScore, HotspotEntry, LargeFunctionEntry,
14    RefactoringTarget, VitalSigns,
15};
16
17/// A health report scoped to a single group.
18///
19/// `key` is the group label produced by the resolver (workspace package name,
20/// CODEOWNERS owner, directory, or section). `owners` is populated only for
21/// `--group-by section` (mirrors dead-code grouped output).
22///
23/// Per-group `vital_signs` and `health_score` are recomputed from the
24/// files in the group, so they answer "what is the health of workspace X" in
25/// a single invocation. `files_analyzed` and `functions_above_threshold`
26/// summarise the subset for parity with the project-level
27/// [`crate::health_types::HealthSummary`].
28#[derive(Debug, Clone, Serialize)]
29pub struct HealthGroup {
30    /// Group label.
31    pub key: String,
32    /// Section default owners (`--group-by section` only).
33    #[serde(skip_serializing_if = "Option::is_none")]
34    pub owners: Option<Vec<String>>,
35    /// Files participating in this group after workspace and ignore filters.
36    pub files_analyzed: usize,
37    /// Number of findings in this group, mirroring the project-level
38    /// `summary.functions_above_threshold` semantics post-baseline /
39    /// post-`--top` truncation. When `--top` was supplied this reflects the
40    /// rendered finding count, not the un-truncated total.
41    pub functions_above_threshold: usize,
42    /// Per-group vital signs (None when `--score-only` suppressed them).
43    #[serde(skip_serializing_if = "Option::is_none")]
44    pub vital_signs: Option<VitalSigns>,
45    /// Per-group health score (None when `--score` was not requested).
46    #[serde(skip_serializing_if = "Option::is_none")]
47    pub health_score: Option<HealthScore>,
48    /// Findings restricted to files in this group.
49    #[serde(skip_serializing_if = "Vec::is_empty")]
50    pub findings: Vec<HealthFinding>,
51    /// File scores restricted to files in this group.
52    #[serde(skip_serializing_if = "Vec::is_empty")]
53    pub file_scores: Vec<FileHealthScore>,
54    /// Hotspots restricted to files in this group.
55    #[serde(skip_serializing_if = "Vec::is_empty")]
56    pub hotspots: Vec<HotspotEntry>,
57    /// Large functions in files belonging to this group.
58    #[serde(skip_serializing_if = "Vec::is_empty")]
59    pub large_functions: Vec<LargeFunctionEntry>,
60    /// Refactoring targets in files belonging to this group.
61    #[serde(skip_serializing_if = "Vec::is_empty")]
62    pub targets: Vec<RefactoringTarget>,
63}
64
65/// Wrapper carrying the resolver mode label alongside the partitioned groups.
66///
67/// Stored on `crate::health::HealthResult` when `--group-by` is active and
68/// consumed by formatters that either render grouped data directly or annotate
69/// per-finding machine output with the group key.
70#[derive(Debug, Clone)]
71pub struct HealthGrouping {
72    /// Resolver mode label (`"package"`, `"owner"`, `"directory"`, `"section"`).
73    pub mode: &'static str,
74    /// Groups in the same order the resolver produced them.
75    pub groups: Vec<HealthGroup>,
76}