Skip to main content

use_hardness/
lib.rs

1#![forbid(unsafe_code)]
2//! Primitive hardness helpers.
3//!
4//! Hardness values are only compared when they share the same scale. No
5//! approximate cross-scale conversions are provided in this first pass.
6//!
7//! # Examples
8//!
9//! ```rust
10//! use use_hardness::{Hardness, HardnessScale, is_harder_than, same_scale};
11//!
12//! let first = Hardness::new(200.0, HardnessScale::Vickers).unwrap();
13//! let second = Hardness::new(180.0, HardnessScale::Vickers).unwrap();
14//!
15//! assert!(same_scale(first, second));
16//! assert!(is_harder_than(first, second).unwrap());
17//! ```
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum HardnessScale {
21    Brinell,
22    Vickers,
23    RockwellA,
24    RockwellB,
25    RockwellC,
26    Mohs,
27}
28
29#[derive(Debug, Clone, Copy, PartialEq)]
30pub struct Hardness {
31    value: f64,
32    scale: HardnessScale,
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub enum HardnessError {
37    InvalidValue,
38    ScaleMismatch,
39}
40
41impl Hardness {
42    pub fn new(value: f64, scale: HardnessScale) -> Result<Self, HardnessError> {
43        if !value.is_finite() || value <= 0.0 {
44            return Err(HardnessError::InvalidValue);
45        }
46
47        Ok(Self { value, scale })
48    }
49
50    #[must_use]
51    pub fn value(&self) -> f64 {
52        self.value
53    }
54
55    #[must_use]
56    pub fn scale(&self) -> HardnessScale {
57        self.scale
58    }
59}
60
61#[must_use]
62pub fn same_scale(a: Hardness, b: Hardness) -> bool {
63    a.scale == b.scale
64}
65
66pub fn is_harder_than(a: Hardness, b: Hardness) -> Result<bool, HardnessError> {
67    if !same_scale(a, b) {
68        return Err(HardnessError::ScaleMismatch);
69    }
70
71    Ok(a.value > b.value)
72}
73
74#[cfg(test)]
75mod tests {
76    use super::{Hardness, HardnessError, HardnessScale, is_harder_than, same_scale};
77
78    #[test]
79    fn compares_hardness_values_on_the_same_scale() {
80        let first = Hardness::new(200.0, HardnessScale::Vickers).unwrap();
81        let second = Hardness::new(180.0, HardnessScale::Vickers).unwrap();
82
83        assert!(same_scale(first, second));
84        assert!(is_harder_than(first, second).unwrap());
85        assert!(!is_harder_than(second, first).unwrap());
86    }
87
88    #[test]
89    fn rejects_cross_scale_comparisons() {
90        let first = Hardness::new(60.0, HardnessScale::RockwellC).unwrap();
91        let second = Hardness::new(8.0, HardnessScale::Mohs).unwrap();
92
93        assert!(!same_scale(first, second));
94        assert_eq!(
95            is_harder_than(first, second),
96            Err(HardnessError::ScaleMismatch)
97        );
98    }
99
100    #[test]
101    fn rejects_invalid_hardness_values() {
102        assert_eq!(
103            Hardness::new(0.0, HardnessScale::Brinell),
104            Err(HardnessError::InvalidValue)
105        );
106        assert_eq!(
107            Hardness::new(f64::NAN, HardnessScale::Mohs),
108            Err(HardnessError::InvalidValue)
109        );
110    }
111}