use std::path::Path;
use std::time::Duration;
use fallow_output::{
CHECK_SCHEMA_VERSION, CheckGroupedEntry, CheckGroupedOutput, CheckOutput, CheckOutputInput,
DupesOutput, DupesOutputInput, GroupByMode, RootEnvelopeMode,
apply_config_fixable_to_duplicate_exports, build_check_output, build_dupes_output,
harmonize_multi_kind_suppress_line_actions as harmonize_typed_suppress_line_actions,
strip_root_prefix,
};
use fallow_types::duplicates::DuplicationReport;
use fallow_types::envelope::{
BaselineDeltas, BaselineMatch, ElapsedMs, Meta, RegressionResult, SchemaVersion, ToolVersion,
};
use fallow_types::output::NextStep;
use fallow_types::results::AnalysisResults;
use fallow_types::workspace::WorkspaceDiagnostic;
use crate::{DupesReportPayload, DuplicationGroup, DuplicationGrouping, ResultGroup};
pub struct CheckJsonOutputInput<'a> {
pub results: &'a AnalysisResults,
pub root: &'a Path,
pub elapsed: Duration,
pub config_fixable: bool,
pub meta: Option<Meta>,
pub extras: CheckJsonExtraOutputs,
pub workspace_diagnostics: Vec<WorkspaceDiagnostic>,
pub next_steps: Vec<NextStep>,
pub envelope_mode: RootEnvelopeMode,
pub telemetry_analysis_run_id: Option<&'a str>,
}
pub struct CheckJsonPayloadInput<'a> {
pub results: &'a AnalysisResults,
pub root: &'a Path,
pub elapsed: Duration,
pub config_fixable: bool,
pub extras: CheckJsonExtraOutputs,
pub workspace_diagnostics: Vec<WorkspaceDiagnostic>,
}
#[derive(Debug, Clone, Default)]
pub struct CheckJsonExtraOutputs {
pub baseline_deltas: Option<BaselineDeltas>,
pub baseline: Option<BaselineMatch>,
pub regression: Option<RegressionResult>,
}
struct CheckJsonEnvelopeInput<'a> {
results: &'a AnalysisResults,
elapsed: Duration,
config_fixable: bool,
meta: Option<Meta>,
extras: CheckJsonExtraOutputs,
workspace_diagnostics: Vec<WorkspaceDiagnostic>,
next_steps: Vec<NextStep>,
}
pub struct GroupedCheckJsonOutputInput<'a> {
pub groups: &'a [ResultGroup],
pub original: &'a AnalysisResults,
pub root: &'a Path,
pub elapsed: Duration,
pub grouped_by: GroupByMode,
pub config_fixable: bool,
pub meta: Option<Meta>,
pub next_steps: Vec<NextStep>,
pub envelope_mode: RootEnvelopeMode,
pub telemetry_analysis_run_id: Option<&'a str>,
}
pub struct DuplicationJsonOutputInput<'a> {
pub report: &'a DuplicationReport,
pub root: &'a Path,
pub elapsed: Duration,
pub meta: Option<Meta>,
pub workspace_diagnostics: Vec<WorkspaceDiagnostic>,
pub next_steps: Vec<NextStep>,
pub envelope_mode: RootEnvelopeMode,
pub telemetry_analysis_run_id: Option<&'a str>,
}
pub struct GroupedDuplicationJsonOutputInput<'a> {
pub report: &'a DuplicationReport,
pub grouping: &'a DuplicationGrouping,
pub root: &'a Path,
pub elapsed: Duration,
pub meta: Option<Meta>,
pub workspace_diagnostics: Vec<WorkspaceDiagnostic>,
pub next_steps: Vec<NextStep>,
pub envelope_mode: RootEnvelopeMode,
pub telemetry_analysis_run_id: Option<&'a str>,
}
pub fn serialize_check_json(
input: CheckJsonOutputInput<'_>,
) -> Result<serde_json::Value, serde_json::Error> {
let envelope = build_check_json_envelope(CheckJsonEnvelopeInput {
results: input.results,
elapsed: input.elapsed,
config_fixable: input.config_fixable,
meta: input.meta,
extras: input.extras,
workspace_diagnostics: input.workspace_diagnostics,
next_steps: input.next_steps,
});
let mut output = fallow_output::serialize_check_json_output(
envelope,
input.envelope_mode,
input.telemetry_analysis_run_id,
)?;
strip_json_root_prefix(&mut output, input.root);
Ok(output)
}
pub fn serialize_check_json_payload(
input: CheckJsonPayloadInput<'_>,
) -> Result<serde_json::Value, serde_json::Error> {
let envelope = build_check_json_envelope(CheckJsonEnvelopeInput {
results: input.results,
elapsed: input.elapsed,
config_fixable: input.config_fixable,
meta: None,
extras: input.extras,
workspace_diagnostics: input.workspace_diagnostics,
next_steps: Vec::new(),
});
let mut output = serde_json::to_value(envelope)?;
strip_json_root_prefix(&mut output, input.root);
Ok(output)
}
pub fn serialize_grouped_check_json(
input: GroupedCheckJsonOutputInput<'_>,
) -> Result<serde_json::Value, serde_json::Error> {
let entries = input
.groups
.iter()
.map(|group| {
let mut results = group.results.clone();
apply_config_fixable_to_duplicate_exports(&mut results, input.config_fixable);
harmonize_typed_suppress_line_actions(&mut results);
CheckGroupedEntry {
key: group.key.clone(),
owners: group.owners.clone(),
total_issues: results.total_issues(),
results,
}
})
.collect();
let envelope = CheckGroupedOutput {
schema_version: SchemaVersion(CHECK_SCHEMA_VERSION),
version: ToolVersion(env!("CARGO_PKG_VERSION").to_string()),
elapsed_ms: ElapsedMs(input.elapsed.as_millis() as u64),
grouped_by: input.grouped_by,
total_issues: input.original.total_issues(),
groups: entries,
meta: input.meta,
next_steps: input.next_steps,
};
let mut output = fallow_output::serialize_check_grouped_json_output(
envelope,
input.envelope_mode,
input.telemetry_analysis_run_id,
)?;
let root_prefix = format!("{}/", input.root.display());
if let Some(arr) = output
.get_mut("groups")
.and_then(serde_json::Value::as_array_mut)
{
for entry in arr {
strip_root_prefix(entry, &root_prefix);
}
}
Ok(output)
}
pub fn serialize_duplication_json(
input: DuplicationJsonOutputInput<'_>,
) -> Result<serde_json::Value, serde_json::Error> {
let payload = DupesReportPayload::from_report(input.report);
let envelope: DupesOutput<DupesReportPayload, DuplicationGroup> =
build_dupes_output(DupesOutputInput {
schema_version: CHECK_SCHEMA_VERSION,
version: env!("CARGO_PKG_VERSION").to_string(),
elapsed: input.elapsed,
report: payload,
grouped_by: None,
total_issues: None,
groups: None,
meta: input.meta,
workspace_diagnostics: input.workspace_diagnostics,
next_steps: input.next_steps,
});
let mut output = fallow_output::serialize_dupes_json_output(
envelope,
input.envelope_mode,
input.telemetry_analysis_run_id,
)?;
let root_prefix = format!("{}/", input.root.display());
strip_root_prefix(&mut output, &root_prefix);
Ok(output)
}
pub fn serialize_grouped_duplication_json(
input: GroupedDuplicationJsonOutputInput<'_>,
) -> Result<serde_json::Value, serde_json::Error> {
let root_prefix = format!("{}/", input.root.display());
let payload = DupesReportPayload::from_report(input.report);
let envelope: DupesOutput<DupesReportPayload, DuplicationGroup> =
build_dupes_output(DupesOutputInput {
schema_version: CHECK_SCHEMA_VERSION,
version: env!("CARGO_PKG_VERSION").to_string(),
elapsed: input.elapsed,
report: payload,
grouped_by: Some(group_by_mode_from_label(input.grouping.mode)),
total_issues: Some(input.report.clone_groups.len()),
groups: None,
meta: input.meta,
workspace_diagnostics: input.workspace_diagnostics,
next_steps: input.next_steps,
});
let mut output = fallow_output::serialize_dupes_json_output(
envelope,
input.envelope_mode,
input.telemetry_analysis_run_id,
)?;
strip_root_prefix(&mut output, &root_prefix);
let group_values = input
.grouping
.groups
.iter()
.map(|group| {
let mut value = serde_json::to_value(group)?;
strip_root_prefix(&mut value, &root_prefix);
Ok(value)
})
.collect::<Result<Vec<_>, serde_json::Error>>()?;
if let serde_json::Value::Object(ref mut map) = output {
map.insert("groups".to_string(), serde_json::Value::Array(group_values));
}
Ok(output)
}
fn build_check_json_envelope(input: CheckJsonEnvelopeInput<'_>) -> CheckOutput {
let mut output = build_check_output(CheckOutputInput {
schema_version: CHECK_SCHEMA_VERSION,
version: env!("CARGO_PKG_VERSION").to_string(),
elapsed: input.elapsed,
results: input.results.clone(),
config_fixable: input.config_fixable,
meta: input.meta,
workspace_diagnostics: input.workspace_diagnostics,
next_steps: input.next_steps,
});
output.baseline_deltas = input.extras.baseline_deltas;
output.baseline = input.extras.baseline;
output.regression = input.extras.regression;
output
}
fn strip_json_root_prefix(output: &mut serde_json::Value, root: &Path) {
let root_prefix = format!("{}/", root.display());
strip_root_prefix(output, &root_prefix);
}
fn group_by_mode_from_label(label: &str) -> GroupByMode {
match label {
"directory" => GroupByMode::Directory,
"package" => GroupByMode::Package,
"section" => GroupByMode::Section,
_ => GroupByMode::Owner,
}
}