jscpd_rs/report/
threshold.rs1use std::error::Error;
2use std::fmt;
3
4use anyhow::Result;
5
6use crate::cli::Options;
7use crate::detector::DetectionResult;
8
9pub(super) fn write(result: &DetectionResult, options: &Options) -> Result<()> {
10 let Some(threshold) = options.threshold else {
11 return Ok(());
12 };
13 if threshold < result.statistics.total.percentage {
14 return Err(ThresholdExceeded::new(format!(
15 "ERROR: jscpd found too many duplicates ({}%) over threshold ({}%)",
16 result.statistics.total.percentage, threshold,
17 ))
18 .into());
19 }
20 Ok(())
21}
22
23#[derive(Debug)]
24pub struct ThresholdExceeded {
25 message: String,
26}
27
28impl ThresholdExceeded {
29 fn new(message: String) -> Self {
30 Self { message }
31 }
32
33 pub fn message(&self) -> &str {
34 &self.message
35 }
36}
37
38impl fmt::Display for ThresholdExceeded {
39 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
40 formatter.write_str(&self.message)
41 }
42}
43
44impl Error for ThresholdExceeded {}
45
46#[cfg(test)]
47mod tests {
48 use super::*;
49 use crate::report::test_support::make_test_result_with_clone;
50 use crate::report::write_reports;
51
52 #[test]
53 fn threshold_reporter_uses_strictly_greater_percentage_like_upstream() {
54 let mut result = make_test_result_with_clone("src/a.js", "src/b.js");
55 result.statistics.total.percentage = 25.0;
56
57 let equal = Options {
58 threshold: Some(25.0),
59 reporters: vec!["threshold".to_string()],
60 ..Options::default()
61 };
62 assert!(write_reports(&result, &equal).is_ok());
63
64 let below = Options {
65 threshold: Some(24.9),
66 reporters: vec!["threshold".to_string()],
67 ..Options::default()
68 };
69 let error = write_reports(&result, &below).unwrap_err();
70 assert!(error.downcast_ref::<ThresholdExceeded>().is_some());
71 assert_eq!(
72 error.to_string(),
73 "ERROR: jscpd found too many duplicates (25%) over threshold (24.9%)"
74 );
75 }
76}