dry4rust/cli/checking/ceiling.rs
1// Copyright (c) 2026 Matjaz Domen Pecan
2// Copyright 2026 Umberto Gotti <umberto.gotti@umbertogotti.dev>
3// Licensed under the MIT License
4// SPDX-License-Identifier: MIT
5
6use crate::threshold::Threshold;
7
8/// One limit `check` measures a result against, and the sentence it produces
9/// when the result exceeds it.
10///
11/// `check` had four of these written out in full, differing only in what they
12/// counted and how the number reads -- a group count as an integer, a
13/// duplicated-line share to one decimal place. Four copies of one shape is how
14/// a function acquires branches it does not need.
15pub struct Ceiling {
16 limit: Option<f64>,
17 actual: f64,
18 subject: &'static str,
19 percentage: bool,
20}
21
22impl Ceiling {
23 /// A ceiling on a number of groups.
24 #[must_use]
25 pub fn count(limit: Option<usize>, actual: usize, subject: &'static str) -> Self {
26 Self {
27 limit: limit.map(|value| value as f64),
28 actual: actual as f64,
29 subject,
30 percentage: false,
31 }
32 }
33
34 /// A ceiling on a share of duplicated lines.
35 #[must_use]
36 pub fn percent(limit: Option<Threshold>, actual: f64, subject: &'static str) -> Self {
37 Self {
38 limit: limit.map(Threshold::as_percent),
39 actual,
40 subject,
41 percentage: true,
42 }
43 }
44
45 /// What to say when the ceiling is exceeded, and `None` when it is not.
46 ///
47 /// An unset limit is not a ceiling of zero. It means the caller did not
48 /// ask, so nothing can breach it.
49 #[must_use]
50 pub fn breach(&self) -> Option<String> {
51 let limit = self.limit?;
52 if self.actual <= limit {
53 return None;
54 }
55 Some(if self.percentage {
56 format!("{:.1}% {} (max: {:.1}%)", self.actual, self.subject, limit)
57 } else {
58 format!(
59 "{} {} (max: {})",
60 self.actual as usize, self.subject, limit as usize
61 )
62 })
63 }
64}