1use std::path::{Path, PathBuf};
4
5use fallow_output::{
6 CheckOutput, DupesOutput, FeatureFlagFinding, FeatureFlagsOutput as FeatureFlagsOutputContract,
7 GroupByMode, HealthGroup, HealthGrouping, HealthJsonOutputInput, HealthOutputInput,
8 HealthReport, RootEnvelopeMode, health_meta,
9};
10use fallow_types::output::NextStep;
11use fallow_types::output_dead_code::{
12 BoundaryCallViolationFinding, BoundaryCoverageViolationFinding, BoundaryViolationFinding,
13 CircularDependencyFinding,
14};
15use fallow_types::results::AnalysisResults;
16use fallow_types::workspace::WorkspaceDiagnostic;
17use rustc_hash::FxHashSet;
18
19use crate::{AuditAttribution, AuditSummary, AuditVerdict};
20use crate::{CloneFamilyFinding, CloneGroupFinding, DupesReportPayload, DuplicationGroup};
21
22pub const HEALTH_SCHEMA_VERSION: u32 = 7;
23
24pub type DeadCodeOutput = CheckOutput;
26
27pub type CircularDependenciesOutput = CheckOutput;
29
30pub type BoundaryViolationsOutput = CheckOutput;
32
33pub type DuplicationOutput = DupesOutput<DupesReportPayload, DuplicationGroup>;
35
36pub type FeatureFlagsOutput = FeatureFlagsOutputContract;
38
39pub type TraceExportOutput = fallow_types::trace::ExportTrace;
41
42pub type TraceFileOutput = fallow_types::trace::FileTrace;
44
45pub type TraceDependencyOutput = fallow_types::trace::DependencyTrace;
47
48pub type TraceCloneOutput = fallow_types::trace::CloneTrace;
50
51pub struct HealthJsonReportInput<'a> {
53 pub report: HealthReport,
54 pub root: &'a Path,
55 pub elapsed: std::time::Duration,
56 pub explain: bool,
57 pub grouped_by: Option<GroupByMode>,
58 pub groups: Option<Vec<HealthGroup>>,
59 pub workspace_diagnostics: Vec<WorkspaceDiagnostic>,
60 pub next_steps: Vec<NextStep>,
61 pub envelope_mode: RootEnvelopeMode,
62 pub telemetry_analysis_run_id: Option<&'a str>,
63}
64
65#[derive(Debug, Clone)]
71pub struct DeadCodeProgrammaticOutput {
72 pub output: DeadCodeOutput,
73 pub root: PathBuf,
74 pub envelope_mode: RootEnvelopeMode,
75 pub telemetry_analysis_run_id: Option<String>,
76}
77
78impl DeadCodeProgrammaticOutput {
79 #[must_use]
81 pub fn results(&self) -> &AnalysisResults {
82 &self.output.results
83 }
84
85 #[must_use]
87 pub fn root(&self) -> &Path {
88 &self.root
89 }
90}
91
92#[derive(Debug, Clone)]
98pub struct CircularDependenciesProgrammaticOutput {
99 pub output: CircularDependenciesOutput,
100 pub root: PathBuf,
101 pub envelope_mode: RootEnvelopeMode,
102 pub telemetry_analysis_run_id: Option<String>,
103}
104
105impl CircularDependenciesProgrammaticOutput {
106 #[must_use]
108 pub fn results(&self) -> &AnalysisResults {
109 &self.output.results
110 }
111
112 #[must_use]
114 pub fn circular_dependencies(&self) -> &[CircularDependencyFinding] {
115 &self.output.results.circular_dependencies
116 }
117}
118
119impl From<DeadCodeProgrammaticOutput> for CircularDependenciesProgrammaticOutput {
120 fn from(value: DeadCodeProgrammaticOutput) -> Self {
121 Self {
122 output: value.output,
123 root: value.root,
124 envelope_mode: value.envelope_mode,
125 telemetry_analysis_run_id: value.telemetry_analysis_run_id,
126 }
127 }
128}
129
130#[derive(Debug, Clone)]
135pub struct BoundaryViolationsProgrammaticOutput {
136 pub output: BoundaryViolationsOutput,
137 pub root: PathBuf,
138 pub envelope_mode: RootEnvelopeMode,
139 pub telemetry_analysis_run_id: Option<String>,
140}
141
142impl BoundaryViolationsProgrammaticOutput {
143 #[must_use]
145 pub fn results(&self) -> &AnalysisResults {
146 &self.output.results
147 }
148
149 #[must_use]
151 pub fn boundary_violations(&self) -> &[BoundaryViolationFinding] {
152 &self.output.results.boundary_violations
153 }
154
155 #[must_use]
157 pub fn boundary_coverage_violations(&self) -> &[BoundaryCoverageViolationFinding] {
158 &self.output.results.boundary_coverage_violations
159 }
160
161 #[must_use]
163 pub fn boundary_call_violations(&self) -> &[BoundaryCallViolationFinding] {
164 &self.output.results.boundary_call_violations
165 }
166}
167
168impl From<DeadCodeProgrammaticOutput> for BoundaryViolationsProgrammaticOutput {
169 fn from(value: DeadCodeProgrammaticOutput) -> Self {
170 Self {
171 output: value.output,
172 root: value.root,
173 envelope_mode: value.envelope_mode,
174 telemetry_analysis_run_id: value.telemetry_analysis_run_id,
175 }
176 }
177}
178
179#[derive(Debug, Clone)]
181pub struct DuplicationProgrammaticOutput {
182 pub output: DuplicationOutput,
183 pub root: PathBuf,
184 pub threshold: f64,
185 pub envelope_mode: RootEnvelopeMode,
186 pub telemetry_analysis_run_id: Option<String>,
187}
188
189impl DuplicationProgrammaticOutput {
190 #[must_use]
192 pub const fn report(&self) -> &DupesReportPayload {
193 &self.output.report
194 }
195
196 #[must_use]
198 pub fn clone_groups(&self) -> &[CloneGroupFinding] {
199 &self.output.report.clone_groups
200 }
201
202 #[must_use]
204 pub fn clone_families(&self) -> &[CloneFamilyFinding] {
205 &self.output.report.clone_families
206 }
207
208 #[must_use]
210 pub fn groups(&self) -> Option<&[DuplicationGroup]> {
211 self.output.groups.as_deref()
212 }
213}
214
215#[derive(Debug, Clone)]
217pub struct FeatureFlagsProgrammaticOutput {
218 pub output: FeatureFlagsOutput,
219 pub envelope_mode: RootEnvelopeMode,
220 pub telemetry_analysis_run_id: Option<String>,
221}
222
223impl FeatureFlagsProgrammaticOutput {
224 #[must_use]
226 pub fn feature_flags(&self) -> &[FeatureFlagFinding] {
227 &self.output.feature_flags
228 }
229
230 #[must_use]
232 pub const fn total_flags(&self) -> usize {
233 self.output.total_flags
234 }
235}
236
237#[derive(Debug)]
239pub struct TraceExportProgrammaticOutput {
240 pub output: TraceExportOutput,
241}
242
243impl TraceExportProgrammaticOutput {
244 #[must_use]
246 pub const fn trace(&self) -> &TraceExportOutput {
247 &self.output
248 }
249}
250
251#[derive(Debug)]
253pub struct TraceFileProgrammaticOutput {
254 pub output: TraceFileOutput,
255}
256
257impl TraceFileProgrammaticOutput {
258 #[must_use]
260 pub const fn trace(&self) -> &TraceFileOutput {
261 &self.output
262 }
263}
264
265#[derive(Debug)]
267pub struct TraceDependencyProgrammaticOutput {
268 pub output: TraceDependencyOutput,
269}
270
271impl TraceDependencyProgrammaticOutput {
272 #[must_use]
274 pub const fn trace(&self) -> &TraceDependencyOutput {
275 &self.output
276 }
277}
278
279#[derive(Debug)]
281pub struct TraceCloneProgrammaticOutput {
282 pub output: TraceCloneOutput,
283}
284
285impl TraceCloneProgrammaticOutput {
286 #[must_use]
288 pub const fn trace(&self) -> &TraceCloneOutput {
289 &self.output
290 }
291}
292
293#[derive(Debug, Clone)]
295pub struct HealthProgrammaticOutput {
296 pub report: HealthReport,
297 pub grouping: Option<HealthGrouping>,
298 pub root: PathBuf,
299 pub elapsed: std::time::Duration,
300 pub explain: bool,
301 pub workspace_diagnostics: Vec<WorkspaceDiagnostic>,
302 pub next_steps: Vec<NextStep>,
303 pub envelope_mode: RootEnvelopeMode,
304 pub telemetry_analysis_run_id: Option<String>,
305}
306
307#[derive(Debug, Clone)]
309pub struct AuditProgrammaticOutput {
310 pub verdict: AuditVerdict,
311 pub summary: AuditSummary,
312 pub attribution: AuditAttribution,
313 pub changed_files_count: usize,
314 pub base_ref: String,
315 pub base_description: Option<String>,
316 pub head_sha: Option<String>,
317 pub elapsed: std::time::Duration,
318 pub base_snapshot_skipped: Option<bool>,
319 pub base_snapshot: Option<AuditProgrammaticKeySnapshot>,
320 pub dead_code: Option<DeadCodeProgrammaticOutput>,
321 pub duplication: Option<DuplicationProgrammaticOutput>,
322 pub complexity: Option<HealthProgrammaticOutput>,
323 pub next_steps: Vec<NextStep>,
324 pub envelope_mode: RootEnvelopeMode,
325 pub telemetry_analysis_run_id: Option<String>,
326}
327
328#[derive(Debug, Clone, Default)]
330pub struct AuditProgrammaticKeySnapshot {
331 pub dead_code: FxHashSet<String>,
332 pub health: FxHashSet<String>,
333 pub dupes: FxHashSet<String>,
334}
335
336#[derive(Debug, Clone)]
338pub struct DecisionSurfaceProgrammaticOutput {
339 pub surface: fallow_output::DecisionSurface,
340 pub elapsed: std::time::Duration,
341 pub envelope_mode: RootEnvelopeMode,
342 pub telemetry_analysis_run_id: Option<String>,
343}
344
345pub fn serialize_health_report_json(
351 input: HealthJsonReportInput<'_>,
352) -> Result<serde_json::Value, serde_json::Error> {
353 let root_prefix = format!("{}/", input.root.display());
354 fallow_output::serialize_health_json_output(HealthJsonOutputInput {
355 output: HealthOutputInput {
356 schema_version: HEALTH_SCHEMA_VERSION,
357 version: env!("CARGO_PKG_VERSION").to_string(),
358 elapsed: input.elapsed,
359 report: input.report,
360 grouped_by: input.grouped_by,
361 groups: input.groups,
362 meta: input.explain.then(health_meta),
363 workspace_diagnostics: input.workspace_diagnostics,
364 next_steps: input.next_steps,
365 },
366 root_prefix: Some(&root_prefix),
367 envelope_mode: input.envelope_mode,
368 analysis_run_id: input.telemetry_analysis_run_id,
369 })
370}