Skip to main content

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}