routee-compass-core 0.11.3

The core routing algorithms and data structures of the RouteE-Compass energy-aware routing engine
Documentation
use super::{baseunit, Convert, UnitError, Weight};
use crate::model::unit::AsF64;
use serde::{Deserialize, Serialize};
use std::str::FromStr;

#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Hash)]
#[serde(rename_all = "snake_case")]
pub enum WeightUnit {
    Pounds,
    Tons,
    Kg,
}

impl Convert<Weight> for WeightUnit {
    fn convert(&self, value: &mut std::borrow::Cow<Weight>, to: &Self) -> Result<(), UnitError> {
        use WeightUnit as S;
        let conversion_factor = match (self, to) {
            (S::Pounds, S::Pounds) => None,
            (S::Pounds, S::Tons) => Some(0.0005),
            (S::Pounds, S::Kg) => Some(0.45359291),
            (S::Tons, S::Pounds) => Some(2000.0),
            (S::Tons, S::Tons) => None,
            (S::Tons, S::Kg) => Some(907.185),
            (S::Kg, S::Pounds) => Some(2.20462),
            (S::Kg, S::Tons) => Some(0.00110231),
            (S::Kg, S::Kg) => None,
        };
        if let Some(factor) = conversion_factor {
            let updated = Weight::from(value.as_ref().as_f64() * factor);
            *value.to_mut() = updated;
        }
        Ok(())
    }

    fn convert_to_base(&self, value: &mut std::borrow::Cow<Weight>) -> Result<(), UnitError> {
        self.convert(value, &baseunit::WEIGHT_UNIT)
    }
}

impl std::fmt::Display for WeightUnit {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let s = serde_json::to_string(self)
            .map_err(|_| std::fmt::Error)?
            .replace('\"', "");
        write!(f, "{}", s)
    }
}

impl FromStr for WeightUnit {
    type Err = String;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "pound" | "pounds" | "lb" | "lbs" => Ok(Self::Pounds),
            "ton" | "tons" => Ok(Self::Tons),
            "kilogram" | "kilograms" | "kg" | "kgs" => Ok(Self::Kg),
            _ => Err(format!("unknown weight unit '{}'", s)),
        }
    }
}

impl TryFrom<String> for WeightUnit {
    type Error = String;
    fn try_from(value: String) -> Result<Self, Self::Error> {
        Self::from_str(&value)
    }
}

#[cfg(test)]
mod test {

    use super::WeightUnit as D;
    use crate::model::unit::*;
    use std::borrow::Cow;

    fn assert_approx_eq(a: Weight, b: Weight, error: f64) {
        let result = match (a, b) {
            (c, d) if c < d => (d - c).as_f64() < error,
            (c, d) if c > d => (c - d).as_f64() < error,
            (_, _) => true,
        };
        assert!(
            result,
            "{} ~= {} is not true within an error of {}",
            a, b, error
        )
    }

    #[test]
    fn test_pounds_pounds() {
        let mut value = Cow::Owned(Weight::from(1.0));
        D::Pounds.convert(&mut value, &D::Pounds).unwrap();
        assert_approx_eq(*value, Weight::from(1.0), 0.0001);
    }
    #[test]
    fn test_pounds_tons() {
        let mut value = Cow::Owned(Weight::from(1.0));
        D::Pounds.convert(&mut value, &D::Tons).unwrap();
        assert_approx_eq(*value, Weight::from(0.0005), 0.0001);
    }
    #[test]
    fn test_pounds_kg() {
        let mut value = Cow::Owned(Weight::from(1.0));
        D::Pounds.convert(&mut value, &D::Kg).unwrap();
        assert_approx_eq(*value, Weight::from(0.453592), 0.0001);
    }
    #[test]
    fn test_tons_pounds() {
        let mut value = Cow::Owned(Weight::from(1.0));
        D::Tons.convert(&mut value, &D::Pounds).unwrap();
        assert_approx_eq(*value, Weight::from(2000.0), 0.0001);
    }
    #[test]
    fn test_tons_tons() {
        let mut value = Cow::Owned(Weight::from(1.0));
        D::Tons.convert(&mut value, &D::Tons).unwrap();
        assert_approx_eq(*value, Weight::from(1.0), 0.0001);
    }
    #[test]
    fn test_tons_kg() {
        let mut value = Cow::Owned(Weight::from(1.0));
        D::Tons.convert(&mut value, &D::Kg).unwrap();
        assert_approx_eq(*value, Weight::from(907.185), 0.0001);
    }
    #[test]
    fn test_kg_pounds() {
        let mut value = Cow::Owned(Weight::from(1.0));
        D::Kg.convert(&mut value, &D::Pounds).unwrap();
        assert_approx_eq(*value, Weight::from(2.20462), 0.0001);
    }
    #[test]
    fn test_kg_tons() {
        let mut value = Cow::Owned(Weight::from(1.0));
        D::Kg.convert(&mut value, &D::Tons).unwrap();
        assert_approx_eq(*value, Weight::from(0.00110231), 0.0001);
    }
    #[test]
    fn test_kg_kg() {
        let mut value = Cow::Owned(Weight::from(1.0));
        D::Kg.convert(&mut value, &D::Kg).unwrap();
        assert_approx_eq(*value, Weight::from(1.0), 0.0001);
    }
}