use std::path::{Path, PathBuf};
use fallow_output::{
CheckOutput, DupesOutput, FeatureFlagFinding, FeatureFlagsOutput as FeatureFlagsOutputContract,
GroupByMode, HealthGroup, HealthGrouping, HealthJsonOutputInput, HealthOutputInput,
HealthReport, RootEnvelopeMode, health_meta,
};
use fallow_types::output::NextStep;
use fallow_types::output_dead_code::{
BoundaryCallViolationFinding, BoundaryCoverageViolationFinding, BoundaryViolationFinding,
CircularDependencyFinding,
};
use fallow_types::results::AnalysisResults;
use fallow_types::workspace::WorkspaceDiagnostic;
use rustc_hash::FxHashSet;
use crate::{AuditAttribution, AuditSummary, AuditVerdict};
use crate::{CloneFamilyFinding, CloneGroupFinding, DupesReportPayload, DuplicationGroup};
pub const HEALTH_SCHEMA_VERSION: u32 = 7;
pub type DeadCodeOutput = CheckOutput;
pub type CircularDependenciesOutput = CheckOutput;
pub type BoundaryViolationsOutput = CheckOutput;
pub type DuplicationOutput = DupesOutput<DupesReportPayload, DuplicationGroup>;
pub type FeatureFlagsOutput = FeatureFlagsOutputContract;
pub type TraceExportOutput = fallow_types::trace::ExportTrace;
pub type TraceFileOutput = fallow_types::trace::FileTrace;
pub type TraceDependencyOutput = fallow_types::trace::DependencyTrace;
pub type TraceCloneOutput = fallow_types::trace::CloneTrace;
pub struct HealthJsonReportInput<'a> {
pub report: HealthReport,
pub root: &'a Path,
pub elapsed: std::time::Duration,
pub explain: bool,
pub grouped_by: Option<GroupByMode>,
pub groups: Option<Vec<HealthGroup>>,
pub workspace_diagnostics: Vec<WorkspaceDiagnostic>,
pub next_steps: Vec<NextStep>,
pub envelope_mode: RootEnvelopeMode,
pub telemetry_analysis_run_id: Option<&'a str>,
}
#[derive(Debug, Clone)]
pub struct CombinedProgrammaticOutput {
pub dead_code: Option<DeadCodeProgrammaticOutput>,
pub duplication: Option<DuplicationProgrammaticOutput>,
pub health: Option<HealthProgrammaticOutput>,
pub root: PathBuf,
pub elapsed: std::time::Duration,
pub explain: bool,
pub next_steps: Vec<NextStep>,
pub envelope_mode: RootEnvelopeMode,
pub telemetry_analysis_run_id: Option<String>,
}
#[derive(Debug, Clone)]
pub struct DeadCodeProgrammaticOutput {
pub output: DeadCodeOutput,
pub root: PathBuf,
pub config_fixable: bool,
pub envelope_mode: RootEnvelopeMode,
pub telemetry_analysis_run_id: Option<String>,
}
impl DeadCodeProgrammaticOutput {
#[must_use]
pub fn results(&self) -> &AnalysisResults {
&self.output.results
}
#[must_use]
pub fn root(&self) -> &Path {
&self.root
}
}
#[derive(Debug, Clone)]
pub struct CircularDependenciesProgrammaticOutput {
pub output: CircularDependenciesOutput,
pub root: PathBuf,
pub envelope_mode: RootEnvelopeMode,
pub telemetry_analysis_run_id: Option<String>,
}
impl CircularDependenciesProgrammaticOutput {
#[must_use]
pub fn results(&self) -> &AnalysisResults {
&self.output.results
}
#[must_use]
pub fn circular_dependencies(&self) -> &[CircularDependencyFinding] {
&self.output.results.circular_dependencies
}
}
impl From<DeadCodeProgrammaticOutput> for CircularDependenciesProgrammaticOutput {
fn from(value: DeadCodeProgrammaticOutput) -> Self {
Self {
output: value.output,
root: value.root,
envelope_mode: value.envelope_mode,
telemetry_analysis_run_id: value.telemetry_analysis_run_id,
}
}
}
#[derive(Debug, Clone)]
pub struct BoundaryViolationsProgrammaticOutput {
pub output: BoundaryViolationsOutput,
pub root: PathBuf,
pub envelope_mode: RootEnvelopeMode,
pub telemetry_analysis_run_id: Option<String>,
}
impl BoundaryViolationsProgrammaticOutput {
#[must_use]
pub fn results(&self) -> &AnalysisResults {
&self.output.results
}
#[must_use]
pub fn boundary_violations(&self) -> &[BoundaryViolationFinding] {
&self.output.results.boundary_violations
}
#[must_use]
pub fn boundary_coverage_violations(&self) -> &[BoundaryCoverageViolationFinding] {
&self.output.results.boundary_coverage_violations
}
#[must_use]
pub fn boundary_call_violations(&self) -> &[BoundaryCallViolationFinding] {
&self.output.results.boundary_call_violations
}
}
impl From<DeadCodeProgrammaticOutput> for BoundaryViolationsProgrammaticOutput {
fn from(value: DeadCodeProgrammaticOutput) -> Self {
Self {
output: value.output,
root: value.root,
envelope_mode: value.envelope_mode,
telemetry_analysis_run_id: value.telemetry_analysis_run_id,
}
}
}
#[derive(Debug, Clone)]
pub struct DuplicationProgrammaticOutput {
pub output: DuplicationOutput,
pub root: PathBuf,
pub threshold: f64,
pub envelope_mode: RootEnvelopeMode,
pub telemetry_analysis_run_id: Option<String>,
}
impl DuplicationProgrammaticOutput {
#[must_use]
pub const fn report(&self) -> &DupesReportPayload {
&self.output.report
}
#[must_use]
pub fn clone_groups(&self) -> &[CloneGroupFinding] {
&self.output.report.clone_groups
}
#[must_use]
pub fn clone_families(&self) -> &[CloneFamilyFinding] {
&self.output.report.clone_families
}
#[must_use]
pub fn groups(&self) -> Option<&[DuplicationGroup]> {
self.output.groups.as_deref()
}
}
#[derive(Debug, Clone)]
pub struct FeatureFlagsProgrammaticOutput {
pub output: FeatureFlagsOutput,
pub envelope_mode: RootEnvelopeMode,
pub telemetry_analysis_run_id: Option<String>,
}
impl FeatureFlagsProgrammaticOutput {
#[must_use]
pub fn feature_flags(&self) -> &[FeatureFlagFinding] {
&self.output.feature_flags
}
#[must_use]
pub const fn total_flags(&self) -> usize {
self.output.total_flags
}
}
#[derive(Debug)]
pub struct TraceExportProgrammaticOutput {
pub output: TraceExportOutput,
}
impl TraceExportProgrammaticOutput {
#[must_use]
pub const fn trace(&self) -> &TraceExportOutput {
&self.output
}
}
#[derive(Debug)]
pub struct TraceFileProgrammaticOutput {
pub output: TraceFileOutput,
}
impl TraceFileProgrammaticOutput {
#[must_use]
pub const fn trace(&self) -> &TraceFileOutput {
&self.output
}
}
#[derive(Debug)]
pub struct TraceDependencyProgrammaticOutput {
pub output: TraceDependencyOutput,
}
impl TraceDependencyProgrammaticOutput {
#[must_use]
pub const fn trace(&self) -> &TraceDependencyOutput {
&self.output
}
}
#[derive(Debug)]
pub struct TraceCloneProgrammaticOutput {
pub output: TraceCloneOutput,
}
impl TraceCloneProgrammaticOutput {
#[must_use]
pub const fn trace(&self) -> &TraceCloneOutput {
&self.output
}
}
#[derive(Debug, Clone)]
pub struct HealthProgrammaticOutput {
pub report: HealthReport,
pub grouping: Option<HealthGrouping>,
pub root: PathBuf,
pub elapsed: std::time::Duration,
pub explain: bool,
pub workspace_diagnostics: Vec<WorkspaceDiagnostic>,
pub next_steps: Vec<NextStep>,
pub envelope_mode: RootEnvelopeMode,
pub telemetry_analysis_run_id: Option<String>,
}
#[derive(Debug, Clone)]
pub struct AuditProgrammaticOutput {
pub verdict: AuditVerdict,
pub summary: AuditSummary,
pub attribution: AuditAttribution,
pub changed_files_count: usize,
pub base_ref: String,
pub base_description: Option<String>,
pub head_sha: Option<String>,
pub elapsed: std::time::Duration,
pub base_snapshot_skipped: Option<bool>,
pub base_snapshot: Option<AuditProgrammaticKeySnapshot>,
pub dead_code: Option<DeadCodeProgrammaticOutput>,
pub duplication: Option<DuplicationProgrammaticOutput>,
pub complexity: Option<HealthProgrammaticOutput>,
pub next_steps: Vec<NextStep>,
pub envelope_mode: RootEnvelopeMode,
pub telemetry_analysis_run_id: Option<String>,
}
#[derive(Debug, Clone, Default)]
pub struct AuditProgrammaticKeySnapshot {
pub dead_code: FxHashSet<String>,
pub health: FxHashSet<String>,
pub dupes: FxHashSet<String>,
}
#[derive(Debug, Clone)]
pub struct DecisionSurfaceProgrammaticOutput {
pub surface: fallow_output::DecisionSurface,
pub elapsed: std::time::Duration,
pub envelope_mode: RootEnvelopeMode,
pub telemetry_analysis_run_id: Option<String>,
}
pub fn serialize_health_report_json(
input: HealthJsonReportInput<'_>,
) -> Result<serde_json::Value, serde_json::Error> {
let root_prefix = format!("{}/", input.root.display());
fallow_output::serialize_health_json_output(HealthJsonOutputInput {
output: HealthOutputInput {
schema_version: HEALTH_SCHEMA_VERSION,
version: env!("CARGO_PKG_VERSION").to_string(),
elapsed: input.elapsed,
report: input.report,
grouped_by: input.grouped_by,
groups: input.groups,
meta: input.explain.then(health_meta),
workspace_diagnostics: input.workspace_diagnostics,
next_steps: input.next_steps,
},
root_prefix: Some(&root_prefix),
envelope_mode: input.envelope_mode,
analysis_run_id: input.telemetry_analysis_run_id,
})
}