execution_time/
traits.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/// Trait for rounding floating-point numbers to a specified number of decimal places.
pub trait RoundFloat<T> {
    /// Rounds the floating-point number to the given number of decimal places.
    ///
    /// # Arguments
    ///
    /// * `decimal_places` - The number of decimal places to round to.
    ///
    /// # Returns
    ///
    /// The rounded floating-point number.
    fn round_float(self, decimal_places: T) -> Self
    where
        Self: std::marker::Sized; // This trait is object safe
}

impl<T> RoundFloat<T> for f64
where
    i32: TryFrom<T>,
    <i32 as TryFrom<T>>::Error: std::fmt::Display,
{
    fn round_float(self, decimal_places: T) -> f64 {
        match i32::try_from(decimal_places) {
            Ok(dec) => {
                if dec <= 0 || self == 0.0 {
                    self.round()
                } else {
                    let multiplier: f64 = 10.0_f64.powi(dec);
                    (self * multiplier).round() / multiplier
                }
            }
            Err(why) => {
                let t = std::any::type_name::<T>();
                eprintln!("fn round_float() for f64: {self}");
                eprintln!("Error converting decimal places from type {t} to i32.");
                panic!("Invalid Decimal Places: {why}")
            }
        }
    }
}

/// Trait for formatting floating-point values with their units.
pub trait FormatFloatValue {
    /// Formats a value with its unit (singular or plural).
    ///
    /// # Arguments
    ///
    /// * `decimal` - The number of decimal places to display.
    /// * `singular` - The singular form of the unit.
    /// * `plural` - The plural form of the unit.
    ///
    /// # Returns
    ///
    /// A formatted string.
    fn format_unit(&self, decimal: usize, singular: &str, plural: &str) -> String;
}

// Implementation of the trait for f64
impl FormatFloatValue for f64 {
    fn format_unit(&self, decimal: usize, singular: &str, plural: &str) -> String {
        let unit = if *self >= 2.0 { plural } else { singular };
        format!("{self:.decimal$} {unit}")
    }
}

/// Trait for formatting integer values with their units.
pub trait FormatIntegerValue {
    /// Formats a value with its unit (singular or plural).
    ///
    /// # Arguments
    ///
    /// * `singular` - The singular form of the unit.
    /// * `plural` - The plural form of the unit.
    ///
    /// # Returns
    ///
    /// A formatted string.
    fn format_unit(&self, singular: &str, plural: &str) -> String;
}

// Implementation of the trait for u8
impl FormatIntegerValue for u8 {
    fn format_unit(&self, singular: &str, plural: &str) -> String {
        let unit = if *self >= 2 { plural } else { singular };
        format!("{self} {unit}")
    }
}

// Implementation of the trait for u64
impl FormatIntegerValue for u64 {
    fn format_unit(&self, singular: &str, plural: &str) -> String {
        let unit = if *self >= 2 { plural } else { singular };
        format!("{self} {unit}")
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_integer_formatting() {
        assert_eq!(1u64.format_unit("man", "men"), "1 man");
        assert_eq!(5u64.format_unit("man", "men"), "5 men");
        assert_eq!(1u8.format_unit("apple", "apples"), "1 apple");
        assert_eq!(8u8.format_unit("ox", "oxen"), "8 oxen");
    }

    #[test]
    fn test_floating_point_formatting() {
        assert_eq!(1.0.format_unit(1, "meter", "meters"), "1.0 meter");
        assert_eq!(2.5.format_unit(2, "meter", "meters"), "2.50 meters");
        assert_eq!(7.123501.format_unit(3, "foot", "feet"), "7.124 feet");
    }
}