#[derive(Debug, thiserror::Error)]
pub enum MeasureError {
#[error("value must be non-negative, got {value}")]
NegativeValue {
value: f32,
},
#[error("value must be finite, got {value}")]
NonFiniteValue {
value: f32,
},
}
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct AreaKm2(f32);
impl AreaKm2 {
pub fn new(raw: f32) -> Result<Self, MeasureError> {
if !raw.is_finite() {
return Err(MeasureError::NonFiniteValue { value: raw });
}
if raw < 0.0 {
return Err(MeasureError::NegativeValue { value: raw });
}
Ok(Self(raw))
}
pub fn get(self) -> f32 {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct Weight(f32);
impl Weight {
pub fn new(raw: f32) -> Result<Self, MeasureError> {
if !raw.is_finite() {
return Err(MeasureError::NonFiniteValue { value: raw });
}
if raw < 0.0 {
return Err(MeasureError::NegativeValue { value: raw });
}
Ok(Self(raw))
}
pub fn get(self) -> f32 {
self.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn area_km2_accepts_zero() {
let a = AreaKm2::new(0.0).unwrap();
assert_eq!(a.get(), 0.0);
}
#[test]
fn area_km2_accepts_positive() {
let a = AreaKm2::new(123.45).unwrap();
assert_eq!(a.get(), 123.45);
}
#[test]
fn area_km2_rejects_negative() {
assert!(matches!(
AreaKm2::new(-1.0),
Err(MeasureError::NegativeValue { value: _ })
));
}
#[test]
fn area_km2_rejects_nan() {
assert!(matches!(
AreaKm2::new(f32::NAN),
Err(MeasureError::NonFiniteValue { value: _ })
));
}
#[test]
fn area_km2_rejects_inf() {
assert!(matches!(
AreaKm2::new(f32::INFINITY),
Err(MeasureError::NonFiniteValue { value: _ })
));
}
#[test]
fn area_km2_rejects_neg_inf() {
assert!(matches!(
AreaKm2::new(f32::NEG_INFINITY),
Err(MeasureError::NonFiniteValue { value: _ })
));
}
#[test]
fn weight_accepts_zero() {
let w = Weight::new(0.0).unwrap();
assert_eq!(w.get(), 0.0);
}
#[test]
fn weight_accepts_positive() {
let w = Weight::new(0.75).unwrap();
assert_eq!(w.get(), 0.75);
}
#[test]
fn weight_rejects_negative() {
assert!(matches!(
Weight::new(-0.1),
Err(MeasureError::NegativeValue { value: _ })
));
}
#[test]
fn weight_rejects_nan() {
assert!(matches!(
Weight::new(f32::NAN),
Err(MeasureError::NonFiniteValue { value: _ })
));
}
#[test]
fn weight_rejects_inf() {
assert!(matches!(
Weight::new(f32::INFINITY),
Err(MeasureError::NonFiniteValue { value: _ })
));
}
#[test]
fn area_km2_min_positive_succeeds() {
let a = AreaKm2::new(f32::MIN_POSITIVE).unwrap();
assert_eq!(a.get(), f32::MIN_POSITIVE);
}
#[test]
fn weight_neg_infinity_fails_with_non_finite_not_negative() {
assert!(matches!(
Weight::new(f32::NEG_INFINITY),
Err(MeasureError::NonFiniteValue { value: _ })
));
}
}