cuke_dedup/reporters/
context.rs1use crate::analysis::AnalysisCensus;
2use crate::model::{AnalysisResult, DuplicationThreshold, Severity};
3use serde::Serialize;
4use std::collections::BTreeMap;
5use std::path::Path;
6
7#[non_exhaustive]
9#[derive(Debug, Clone, PartialEq, Serialize)]
10#[serde(rename_all = "camelCase")]
11pub struct ExecutionMetrics {
12 pub definition_files: usize,
14 pub feature_files: usize,
16 pub files_discovered: usize,
18 pub discovery_ms: f64,
20 pub parsing_ms: f64,
22 pub analysis_ms: f64,
24}
25
26impl ExecutionMetrics {
27 pub fn new(
29 definition_files: usize,
30 feature_files: usize,
31 discovery_ms: f64,
32 parsing_ms: f64,
33 analysis_ms: f64,
34 ) -> Self {
35 Self {
36 definition_files,
37 feature_files,
38 files_discovered: definition_files.saturating_add(feature_files),
39 discovery_ms,
40 parsing_ms,
41 analysis_ms,
42 }
43 }
44}
45
46#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
48#[serde(rename_all = "camelCase")]
49pub(crate) struct CorpusCensus {
50 pub(crate) definition_files: usize,
52 pub(crate) definition_files_with_definitions: usize,
54 pub(crate) definitions_extracted: usize,
56 pub(crate) feature_files: usize,
58 pub(crate) feature_files_parsed: usize,
60 pub(crate) feature_files_without_steps: usize,
62 pub(crate) incomplete: bool,
69}
70
71pub(crate) struct CliReportMetadata<'a> {
72 pub(crate) corpus: &'a CorpusCensus,
73 pub(crate) analysis: &'a AnalysisCensus,
74 pub(crate) execution_successful: bool,
75}
76
77#[derive(Debug, Clone, Serialize)]
78#[serde(rename_all = "camelCase")]
79#[non_exhaustive]
81pub struct Summary {
82 pub definitions_analyzed: usize,
84 pub feature_steps_analyzed: usize,
86 pub findings: usize,
88 pub errors: usize,
90 pub warnings: usize,
92 pub suppressed: usize,
94 pub by_rule: BTreeMap<String, usize>,
96 pub duplication: DuplicationThreshold,
98}
99
100impl Summary {
101 pub fn from_result(result: &AnalysisResult) -> Self {
103 Self::from_result_with_threshold(result, 0.0)
104 }
105
106 pub fn from_result_with_threshold(result: &AnalysisResult, threshold: f64) -> Self {
108 let active: Vec<_> = result
109 .findings
110 .iter()
111 .filter(|finding| finding.is_active())
112 .collect();
113 let mut by_rule = BTreeMap::new();
114 for finding in &active {
115 *by_rule.entry(finding.rule.to_string()).or_insert(0) += 1;
116 }
117 Self {
118 definitions_analyzed: result.definitions.len(),
119 feature_steps_analyzed: result.feature_steps.len(),
120 findings: active.len(),
121 errors: active
122 .iter()
123 .filter(|finding| finding.severity == Severity::Error)
124 .count(),
125 warnings: active
126 .iter()
127 .filter(|finding| finding.severity == Severity::Warning)
128 .count(),
129 suppressed: result
130 .findings
131 .iter()
132 .filter(|finding| finding.suppression.is_some())
133 .count(),
134 by_rule,
135 duplication: DuplicationThreshold::from_result(result, threshold),
136 }
137 }
138}
139
140#[non_exhaustive]
142pub struct ReportContext<'a> {
143 pub result: &'a AnalysisResult,
145 pub root: &'a Path,
147 pub threshold: f64,
149 pub summary: Summary,
151 pub tool_version: &'static str,
153 pub metrics: Option<&'a ExecutionMetrics>,
155}
156
157impl<'a> ReportContext<'a> {
158 pub fn new(result: &'a AnalysisResult, root: &'a Path, threshold: f64) -> Self {
160 Self {
161 result,
162 root,
163 threshold,
164 summary: Summary::from_result_with_threshold(result, threshold),
165 tool_version: env!("CARGO_PKG_VERSION"),
166 metrics: None,
167 }
168 }
169
170 pub fn with_metrics(
172 result: &'a AnalysisResult,
173 root: &'a Path,
174 threshold: f64,
175 metrics: &'a ExecutionMetrics,
176 ) -> Self {
177 Self {
178 metrics: Some(metrics),
179 ..Self::new(result, root, threshold)
180 }
181 }
182}