use crate::adapters::report::dry_dedup::dedup_by_locations;
use crate::domain::findings::{
DryFinding, DryFindingDetails, DryFindingKind, DuplicateParticipant, FragmentParticipant,
RepeatedMatchParticipant,
};
pub(crate) struct ParticipantRow {
pub function_name: String,
pub file: String,
pub line: usize,
}
pub(crate) struct DryGroupRow {
pub kind_label: String,
pub participants: Vec<ParticipantRow>,
}
pub(crate) struct DeadCodeRow {
pub qualified_name: String,
pub kind_tag: &'static str,
pub file: String,
pub line: usize,
pub suggestion: String,
}
pub(crate) struct BoilerplateRow {
pub pattern_id: String,
pub struct_name: String,
pub file: String,
pub line: usize,
pub message: String,
pub suggestion: String,
}
pub(crate) struct WildcardRow {
pub module_path: String,
pub file: String,
pub line: usize,
}
pub(crate) struct DryBuckets {
pub duplicate_groups: Vec<DryGroupRow>,
pub fragment_groups: Vec<DryGroupRow>,
pub repeated_match_groups: Vec<DryGroupRow>,
pub dead_code: Vec<DeadCodeRow>,
pub boilerplate: Vec<BoilerplateRow>,
pub wildcards: Vec<WildcardRow>,
}
pub(crate) fn split_dry_findings(findings: &[DryFinding]) -> DryBuckets {
DryBuckets {
duplicate_groups: build_duplicate_groups(findings),
fragment_groups: build_fragment_groups(findings),
repeated_match_groups: build_repeated_match_groups(findings),
dead_code: build_dead_code(findings),
boilerplate: build_boilerplate(findings),
wildcards: build_wildcards(findings),
}
}
fn dup_participants(p: &[DuplicateParticipant]) -> Vec<ParticipantRow> {
p.iter()
.map(|p| ParticipantRow {
function_name: p.function_name.clone(),
file: p.file.clone(),
line: p.line,
})
.collect()
}
fn frag_participants(p: &[FragmentParticipant]) -> Vec<ParticipantRow> {
p.iter()
.map(|p| ParticipantRow {
function_name: p.function_name.clone(),
file: p.file.clone(),
line: p.line,
})
.collect()
}
fn rep_participants(p: &[RepeatedMatchParticipant]) -> Vec<ParticipantRow> {
p.iter()
.map(|p| ParticipantRow {
function_name: p.function_name.clone(),
file: p.file.clone(),
line: p.line,
})
.collect()
}
fn build_duplicate_groups(findings: &[DryFinding]) -> Vec<DryGroupRow> {
dedup_by_locations(findings, |f| match (&f.kind, &f.details) {
(
DryFindingKind::DuplicateExact | DryFindingKind::DuplicateSimilar,
DryFindingDetails::Duplicate { participants, .. },
) => {
let key: Vec<(String, usize)> = participants
.iter()
.map(|p| (p.file.clone(), p.line))
.collect();
Some((
DryGroupRow {
kind_label: f.kind.meta().html_kind_label.to_string(),
participants: dup_participants(participants),
},
key,
))
}
_ => None,
})
}
fn build_fragment_groups(findings: &[DryFinding]) -> Vec<DryGroupRow> {
dedup_by_locations(findings, |f| match &f.details {
DryFindingDetails::Fragment {
participants,
statement_count,
} => {
let key: Vec<(String, usize)> = participants
.iter()
.map(|p| (p.file.clone(), p.line))
.collect();
Some((
DryGroupRow {
kind_label: format!("{statement_count} stmts"),
participants: frag_participants(participants),
},
key,
))
}
_ => None,
})
}
fn build_repeated_match_groups(findings: &[DryFinding]) -> Vec<DryGroupRow> {
dedup_by_locations(findings, |f| match &f.details {
DryFindingDetails::RepeatedMatch {
enum_name,
participants,
} => {
let key: Vec<(String, usize)> = participants
.iter()
.map(|p| (p.file.clone(), p.line))
.collect();
Some((
DryGroupRow {
kind_label: enum_name.clone(),
participants: rep_participants(participants),
},
key,
))
}
_ => None,
})
}
fn build_dead_code(findings: &[DryFinding]) -> Vec<DeadCodeRow> {
findings
.iter()
.filter(|f| !f.common.suppressed)
.filter_map(|f| match (&f.kind, &f.details) {
(
DryFindingKind::DeadCodeUncalled | DryFindingKind::DeadCodeTestOnly,
DryFindingDetails::DeadCode {
qualified_name,
suggestion,
},
) => Some(DeadCodeRow {
qualified_name: qualified_name.clone(),
kind_tag: f.kind.meta().html_dead_code_tag,
file: f.common.file.clone(),
line: f.common.line,
suggestion: suggestion.clone().unwrap_or_default(),
}),
_ => None,
})
.collect()
}
fn build_boilerplate(findings: &[DryFinding]) -> Vec<BoilerplateRow> {
findings
.iter()
.filter(|f| !f.common.suppressed)
.filter_map(|f| match &f.details {
DryFindingDetails::Boilerplate {
pattern_id,
struct_name,
suggestion,
} => Some(BoilerplateRow {
pattern_id: pattern_id.clone(),
struct_name: struct_name.clone().unwrap_or_default(),
file: f.common.file.clone(),
line: f.common.line,
message: f.common.message.clone(),
suggestion: suggestion.clone(),
}),
_ => None,
})
.collect()
}
fn build_wildcards(findings: &[DryFinding]) -> Vec<WildcardRow> {
findings
.iter()
.filter(|f| !f.common.suppressed)
.filter_map(|f| match &f.details {
DryFindingDetails::Wildcard { module_path } => Some(WildcardRow {
module_path: module_path.clone(),
file: f.common.file.clone(),
line: f.common.line,
}),
_ => None,
})
.collect()
}