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)]
67pub struct CombinedProgrammaticOutput {
68 pub dead_code: Option<DeadCodeProgrammaticOutput>,
69 pub duplication: Option<DuplicationProgrammaticOutput>,
70 pub health: Option<HealthProgrammaticOutput>,
71 pub root: PathBuf,
72 pub elapsed: std::time::Duration,
73 pub explain: bool,
74 pub next_steps: Vec<NextStep>,
75 pub envelope_mode: RootEnvelopeMode,
76 pub telemetry_analysis_run_id: Option<String>,
77}
78
79#[derive(Debug, Clone)]
85pub struct DeadCodeProgrammaticOutput {
86 pub output: DeadCodeOutput,
87 pub root: PathBuf,
88 pub config_fixable: bool,
89 pub envelope_mode: RootEnvelopeMode,
90 pub telemetry_analysis_run_id: Option<String>,
91}
92
93impl DeadCodeProgrammaticOutput {
94 #[must_use]
96 pub fn results(&self) -> &AnalysisResults {
97 &self.output.results
98 }
99
100 #[must_use]
102 pub fn root(&self) -> &Path {
103 &self.root
104 }
105}
106
107#[derive(Debug, Clone)]
113pub struct CircularDependenciesProgrammaticOutput {
114 pub output: CircularDependenciesOutput,
115 pub root: PathBuf,
116 pub envelope_mode: RootEnvelopeMode,
117 pub telemetry_analysis_run_id: Option<String>,
118}
119
120impl CircularDependenciesProgrammaticOutput {
121 #[must_use]
123 pub fn results(&self) -> &AnalysisResults {
124 &self.output.results
125 }
126
127 #[must_use]
129 pub fn circular_dependencies(&self) -> &[CircularDependencyFinding] {
130 &self.output.results.circular_dependencies
131 }
132}
133
134impl From<DeadCodeProgrammaticOutput> for CircularDependenciesProgrammaticOutput {
135 fn from(value: DeadCodeProgrammaticOutput) -> Self {
136 Self {
137 output: value.output,
138 root: value.root,
139 envelope_mode: value.envelope_mode,
140 telemetry_analysis_run_id: value.telemetry_analysis_run_id,
141 }
142 }
143}
144
145#[derive(Debug, Clone)]
150pub struct BoundaryViolationsProgrammaticOutput {
151 pub output: BoundaryViolationsOutput,
152 pub root: PathBuf,
153 pub envelope_mode: RootEnvelopeMode,
154 pub telemetry_analysis_run_id: Option<String>,
155}
156
157impl BoundaryViolationsProgrammaticOutput {
158 #[must_use]
160 pub fn results(&self) -> &AnalysisResults {
161 &self.output.results
162 }
163
164 #[must_use]
166 pub fn boundary_violations(&self) -> &[BoundaryViolationFinding] {
167 &self.output.results.boundary_violations
168 }
169
170 #[must_use]
172 pub fn boundary_coverage_violations(&self) -> &[BoundaryCoverageViolationFinding] {
173 &self.output.results.boundary_coverage_violations
174 }
175
176 #[must_use]
178 pub fn boundary_call_violations(&self) -> &[BoundaryCallViolationFinding] {
179 &self.output.results.boundary_call_violations
180 }
181}
182
183impl From<DeadCodeProgrammaticOutput> for BoundaryViolationsProgrammaticOutput {
184 fn from(value: DeadCodeProgrammaticOutput) -> Self {
185 Self {
186 output: value.output,
187 root: value.root,
188 envelope_mode: value.envelope_mode,
189 telemetry_analysis_run_id: value.telemetry_analysis_run_id,
190 }
191 }
192}
193
194#[derive(Debug, Clone)]
196pub struct DuplicationProgrammaticOutput {
197 pub output: DuplicationOutput,
198 pub root: PathBuf,
199 pub threshold: f64,
200 pub envelope_mode: RootEnvelopeMode,
201 pub telemetry_analysis_run_id: Option<String>,
202}
203
204impl DuplicationProgrammaticOutput {
205 #[must_use]
207 pub const fn report(&self) -> &DupesReportPayload {
208 &self.output.report
209 }
210
211 #[must_use]
213 pub fn clone_groups(&self) -> &[CloneGroupFinding] {
214 &self.output.report.clone_groups
215 }
216
217 #[must_use]
219 pub fn clone_families(&self) -> &[CloneFamilyFinding] {
220 &self.output.report.clone_families
221 }
222
223 #[must_use]
225 pub fn groups(&self) -> Option<&[DuplicationGroup]> {
226 self.output.groups.as_deref()
227 }
228}
229
230#[derive(Debug, Clone)]
232pub struct FeatureFlagsProgrammaticOutput {
233 pub output: FeatureFlagsOutput,
234 pub envelope_mode: RootEnvelopeMode,
235 pub telemetry_analysis_run_id: Option<String>,
236}
237
238impl FeatureFlagsProgrammaticOutput {
239 #[must_use]
241 pub fn feature_flags(&self) -> &[FeatureFlagFinding] {
242 &self.output.feature_flags
243 }
244
245 #[must_use]
247 pub const fn total_flags(&self) -> usize {
248 self.output.total_flags
249 }
250}
251
252#[derive(Debug)]
254pub struct TraceExportProgrammaticOutput {
255 pub output: TraceExportOutput,
256}
257
258impl TraceExportProgrammaticOutput {
259 #[must_use]
261 pub const fn trace(&self) -> &TraceExportOutput {
262 &self.output
263 }
264}
265
266#[derive(Debug)]
268pub struct TraceFileProgrammaticOutput {
269 pub output: TraceFileOutput,
270}
271
272impl TraceFileProgrammaticOutput {
273 #[must_use]
275 pub const fn trace(&self) -> &TraceFileOutput {
276 &self.output
277 }
278}
279
280#[derive(Debug)]
282pub struct TraceDependencyProgrammaticOutput {
283 pub output: TraceDependencyOutput,
284}
285
286impl TraceDependencyProgrammaticOutput {
287 #[must_use]
289 pub const fn trace(&self) -> &TraceDependencyOutput {
290 &self.output
291 }
292}
293
294#[derive(Debug)]
296pub struct TraceCloneProgrammaticOutput {
297 pub output: TraceCloneOutput,
298}
299
300impl TraceCloneProgrammaticOutput {
301 #[must_use]
303 pub const fn trace(&self) -> &TraceCloneOutput {
304 &self.output
305 }
306}
307
308#[derive(Debug, Clone)]
310pub struct HealthProgrammaticOutput {
311 pub report: HealthReport,
312 pub grouping: Option<HealthGrouping>,
313 pub root: PathBuf,
314 pub elapsed: std::time::Duration,
315 pub explain: bool,
316 pub workspace_diagnostics: Vec<WorkspaceDiagnostic>,
317 pub next_steps: Vec<NextStep>,
318 pub envelope_mode: RootEnvelopeMode,
319 pub telemetry_analysis_run_id: Option<String>,
320}
321
322#[derive(Debug, Clone)]
324pub struct AuditProgrammaticOutput {
325 pub verdict: AuditVerdict,
326 pub summary: AuditSummary,
327 pub attribution: AuditAttribution,
328 pub changed_files_count: usize,
329 pub base_ref: String,
330 pub base_description: Option<String>,
331 pub head_sha: Option<String>,
332 pub elapsed: std::time::Duration,
333 pub base_snapshot_skipped: Option<bool>,
334 pub base_snapshot: Option<AuditProgrammaticKeySnapshot>,
335 pub dead_code: Option<DeadCodeProgrammaticOutput>,
336 pub duplication: Option<DuplicationProgrammaticOutput>,
337 pub complexity: Option<HealthProgrammaticOutput>,
338 pub next_steps: Vec<NextStep>,
339 pub envelope_mode: RootEnvelopeMode,
340 pub telemetry_analysis_run_id: Option<String>,
341}
342
343#[derive(Debug, Clone, Default)]
345pub struct AuditProgrammaticKeySnapshot {
346 pub dead_code: FxHashSet<String>,
347 pub health: FxHashSet<String>,
348 pub dupes: FxHashSet<String>,
349}
350
351#[derive(Debug, Clone)]
353pub struct DecisionSurfaceProgrammaticOutput {
354 pub surface: fallow_output::DecisionSurface,
355 pub elapsed: std::time::Duration,
356 pub envelope_mode: RootEnvelopeMode,
357 pub telemetry_analysis_run_id: Option<String>,
358}
359
360pub fn serialize_health_report_json(
366 input: HealthJsonReportInput<'_>,
367) -> Result<serde_json::Value, serde_json::Error> {
368 let root_prefix = format!("{}/", input.root.display());
369 fallow_output::serialize_health_json_output(HealthJsonOutputInput {
370 output: HealthOutputInput {
371 schema_version: HEALTH_SCHEMA_VERSION,
372 version: env!("CARGO_PKG_VERSION").to_string(),
373 elapsed: input.elapsed,
374 report: input.report,
375 grouped_by: input.grouped_by,
376 groups: input.groups,
377 meta: input.explain.then(health_meta),
378 workspace_diagnostics: input.workspace_diagnostics,
379 next_steps: input.next_steps,
380 },
381 root_prefix: Some(&root_prefix),
382 envelope_mode: input.envelope_mode,
383 analysis_run_id: input.telemetry_analysis_run_id,
384 })
385}