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}