use std::collections::BTreeMap;
use serde::{Deserialize, Serialize};
use super::{AlreadyPresent, AlreadyPresentSkipped, DepPinDivergence, PinBump, Unanalyzed};
use crate::model::names::{CommitSha, ModuleName, ProjectName, RecordName, RelativePath};
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct Diagnostics {
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub untracked_calls: BTreeMap<ModuleName, usize>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub untracked_records: BTreeMap<RecordName, usize>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub context_refs_missing: BTreeMap<ModuleName, usize>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub unattributed_paths: BTreeMap<String, usize>,
#[serde(default, skip_serializing_if = "Unanalyzed::is_empty")]
pub unanalyzed: Unanalyzed,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub suggested_suites: Vec<String>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub missing_test_modules: BTreeMap<RelativePath, BTreeMap<ModuleName, usize>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub already_present: Option<AlreadyPresent>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub already_present_skipped: Option<AlreadyPresentSkipped>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub dep_pin_divergence: Vec<DepPinDivergence>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub pin_bumps: Vec<PinBump>,
#[serde(default, skip_serializing_if = "ShapeCheckTally::is_empty")]
pub qualified_call_shape_checks: ShapeCheckTally,
#[serde(default, skip_serializing_if = "ShapeCheckTally::is_empty")]
pub local_call_shape_checks: ShapeCheckTally,
#[serde(default, skip_serializing_if = "MacroValueTally::is_empty")]
pub macro_value_checks: MacroValueTally,
#[serde(default, skip_serializing_if = "IndirectCallTally::is_empty")]
pub indirect_call_checks: IndirectCallTally,
}
impl Diagnostics {
pub fn is_empty(&self) -> bool {
let Self {
untracked_calls,
untracked_records,
context_refs_missing,
unattributed_paths,
unanalyzed,
suggested_suites,
missing_test_modules,
already_present,
already_present_skipped,
dep_pin_divergence,
pin_bumps,
qualified_call_shape_checks,
local_call_shape_checks,
macro_value_checks,
indirect_call_checks,
} = self;
untracked_calls.is_empty()
&& untracked_records.is_empty()
&& context_refs_missing.is_empty()
&& unattributed_paths.is_empty()
&& unanalyzed.is_empty()
&& suggested_suites.is_empty()
&& missing_test_modules.is_empty()
&& already_present.is_none()
&& already_present_skipped.is_none()
&& dep_pin_divergence.is_empty()
&& pin_bumps.is_empty()
&& qualified_call_shape_checks.is_empty()
&& local_call_shape_checks.is_empty()
&& macro_value_checks.is_empty()
&& indirect_call_checks.is_empty()
}
pub fn record_missing_test_module(&mut self, suite: RelativePath, helper: ModuleName) {
let inner = self.missing_test_modules.entry(suite).or_default();
*inner.entry(helper).or_insert(0) += 1;
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
#[serde(rename_all = "snake_case")]
pub enum TargetMatchKind {
TrailerIntersection,
TrailerAncestry,
PatchId,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct TargetMatch {
pub commit: CommitSha,
pub via: TargetMatchKind,
pub subject: String,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct ShapeCheckTally {
#[serde(default, skip_serializing_if = "is_zero")]
pub compared: usize,
#[serde(default, skip_serializing_if = "is_zero")]
pub withheld_no_spec: usize,
#[serde(default, skip_serializing_if = "is_zero")]
pub withheld_unknown_type: usize,
#[serde(default, skip_serializing_if = "is_zero")]
pub withheld_no_source: usize,
#[serde(default, skip_serializing_if = "is_zero")]
pub withheld_imported: usize,
}
#[allow(clippy::trivially_copy_pass_by_ref)]
fn is_zero(n: &usize) -> bool {
*n == 0
}
impl ShapeCheckTally {
pub fn is_empty(&self) -> bool {
*self == Self::default()
}
pub fn merge(&mut self, other: Self) {
self.compared += other.compared;
self.withheld_no_spec += other.withheld_no_spec;
self.withheld_unknown_type += other.withheld_unknown_type;
self.withheld_no_source += other.withheld_no_source;
self.withheld_imported += other.withheld_imported;
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct MacroValueTally {
#[serde(default, skip_serializing_if = "is_zero")]
pub compared: usize,
#[serde(default, skip_serializing_if = "is_zero")]
pub withheld_definition_elsewhere: usize,
#[serde(default, skip_serializing_if = "is_zero")]
pub withheld_multiple_defines: usize,
#[serde(default, skip_serializing_if = "is_zero")]
pub withheld_no_source: usize,
}
impl MacroValueTally {
pub fn is_empty(&self) -> bool {
*self == Self::default()
}
pub fn merge(&mut self, other: Self) {
self.compared += other.compared;
self.withheld_definition_elsewhere += other.withheld_definition_elsewhere;
self.withheld_multiple_defines += other.withheld_multiple_defines;
self.withheld_no_source += other.withheld_no_source;
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct IndirectCallTally {
#[serde(default, skip_serializing_if = "is_zero")]
pub checked: usize,
#[serde(default, skip_serializing_if = "is_zero")]
pub withheld_dynamic: usize,
}
impl IndirectCallTally {
pub fn is_empty(&self) -> bool {
*self == Self::default()
}
pub fn merge(&mut self, other: Self) {
self.checked += other.checked;
self.withheld_dynamic += other.withheld_dynamic;
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct HunkTally {
pub applied: usize,
pub considered: usize,
pub ambiguous: usize,
pub low_confidence: usize,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub struct ContentPresence {
pub pin: ProjectName,
pub hunks_already_applied: usize,
pub hunks_considered: usize,
pub hunks_ambiguous: usize,
pub hunks_low_confidence: usize,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub per_file: BTreeMap<RelativePath, HunkTally>,
}