Skip to main content

fallow_cli/
health_types.rs

1//! Health / complexity analysis report types.
2//!
3//! Separated from the `health` command module so that report formatters
4//! (which are compiled as part of both the lib and bin targets) can
5//! reference these types without pulling in binary-only dependencies.
6
7/// Result of complexity analysis for reporting.
8#[derive(Debug, serde::Serialize)]
9pub struct HealthReport {
10    /// Functions exceeding thresholds.
11    pub findings: Vec<HealthFinding>,
12    /// Summary statistics.
13    pub summary: HealthSummary,
14    /// Per-file health scores (only populated with `--file-scores`).
15    #[serde(skip_serializing_if = "Vec::is_empty")]
16    pub file_scores: Vec<FileHealthScore>,
17}
18
19/// A single function that exceeds a complexity threshold.
20#[derive(Debug, serde::Serialize)]
21pub struct HealthFinding {
22    /// Absolute file path.
23    pub path: std::path::PathBuf,
24    /// Function name.
25    pub name: String,
26    /// 1-based line number.
27    pub line: u32,
28    /// 0-based column.
29    pub col: u32,
30    /// Cyclomatic complexity.
31    pub cyclomatic: u16,
32    /// Cognitive complexity.
33    pub cognitive: u16,
34    /// Number of lines in the function.
35    pub line_count: u32,
36    /// Which threshold was exceeded.
37    pub exceeded: ExceededThreshold,
38}
39
40/// Which complexity threshold was exceeded.
41#[derive(Debug, serde::Serialize)]
42#[serde(rename_all = "snake_case")]
43pub enum ExceededThreshold {
44    /// Only cyclomatic exceeded.
45    Cyclomatic,
46    /// Only cognitive exceeded.
47    Cognitive,
48    /// Both thresholds exceeded.
49    Both,
50}
51
52/// Summary statistics for the health report.
53#[derive(Debug, serde::Serialize)]
54pub struct HealthSummary {
55    /// Number of files analyzed.
56    pub files_analyzed: usize,
57    /// Total number of functions found.
58    pub functions_analyzed: usize,
59    /// Number of functions above threshold.
60    pub functions_above_threshold: usize,
61    /// Configured cyclomatic threshold.
62    pub max_cyclomatic_threshold: u16,
63    /// Configured cognitive threshold.
64    pub max_cognitive_threshold: u16,
65    /// Number of files scored (only set with `--file-scores`).
66    #[serde(skip_serializing_if = "Option::is_none")]
67    pub files_scored: Option<usize>,
68    /// Average maintainability index across all scored files (only set with `--file-scores`).
69    #[serde(skip_serializing_if = "Option::is_none")]
70    pub average_maintainability: Option<f64>,
71}
72
73/// Per-file health score combining complexity, coupling, and dead code metrics.
74///
75/// Files with zero functions (barrel files, re-export files) are excluded by default.
76///
77/// ## Maintainability Index Formula
78///
79/// ```text
80/// fan_out_penalty = min(ln(fan_out + 1) × 4, 15)
81/// maintainability = 100
82///     - (complexity_density × 30)
83///     - (dead_code_ratio × 20)
84///     - fan_out_penalty
85/// ```
86///
87/// Clamped to \[0, 100\]. Higher is better.
88///
89/// - **complexity_density**: total cyclomatic complexity / lines of code
90/// - **dead_code_ratio**: fraction of value exports (excluding type-only exports) with zero references (0.0–1.0)
91/// - **fan_out_penalty**: logarithmic scaling with cap at 15 points; reflects diminishing marginal risk of additional imports
92#[derive(Debug, Clone, serde::Serialize)]
93pub struct FileHealthScore {
94    /// File path (absolute; stripped to relative in output).
95    pub path: std::path::PathBuf,
96    /// Number of files that import this file.
97    pub fan_in: usize,
98    /// Number of files this file imports.
99    pub fan_out: usize,
100    /// Fraction of value exports with zero references (0.0–1.0). Files with no value exports get 0.0.
101    /// Type-only exports (interfaces, type aliases) are excluded from both numerator and denominator
102    /// to avoid inflating the ratio for well-typed codebases that export props types alongside components.
103    pub dead_code_ratio: f64,
104    /// Total cyclomatic complexity / lines of code.
105    pub complexity_density: f64,
106    /// Weighted composite score (0–100, higher is better).
107    pub maintainability_index: f64,
108    /// Sum of cyclomatic complexity across all functions.
109    pub total_cyclomatic: u32,
110    /// Sum of cognitive complexity across all functions.
111    pub total_cognitive: u32,
112    /// Number of functions in this file.
113    pub function_count: usize,
114    /// Total lines of code (from line_offsets).
115    pub lines: u32,
116}