1pub mod length;
2pub mod temperature;
3
4use std::fmt;
5
6pub trait UnitType: Copy + PartialEq + std::fmt::Debug + 'static {
8 fn canonical_string(&self) -> &'static str;
10
11 fn parse(s: &str) -> Result<Self, UnitError>;
13
14 fn dimension_name() -> &'static str;
16}
17
18pub trait UnitConversion<U: UnitType> {
20 fn to_base_value(unit: U, value: f64) -> f64;
22
23 fn from_base_value(base_value: f64, unit: U) -> f64;
25
26 fn base_unit() -> U;
28
29 fn convert_direct(_from: U, _to: U, _value: f64) -> Option<f64> {
31 None }
33}
34
35pub struct Dimension<U: UnitType> {
37 value: f64,
38 unit: U,
39}
40
41impl<U: UnitType> Dimension<U>
42where
43 Self: UnitConversion<U>
44{
45 pub fn new(value: f64, unit: U) -> Self {
47 Self { value, unit }
48 }
49
50 pub fn from_unit(unit_str: &str, value: f64) -> Result<Self, UnitError> {
52 let unit = U::parse(unit_str)?;
53 Ok(Self::new(value, unit))
54 }
55
56 pub fn convert_to(&self, target: U) -> Self {
58 let new_value = if self.unit == target {
59 self.value
60 } else if let Some(direct_value) = Self::convert_direct(self.unit, target, self.value) {
61 direct_value
63 } else {
64 let base_value = Self::to_base_value(self.unit, self.value);
66 Self::from_base_value(base_value, target)
67 };
68
69 Self::new(new_value, target)
70 }
71
72 pub fn value(&self) -> f64 {
74 self.value
75 }
76
77 pub fn unit(&self) -> U {
79 self.unit
80 }
81
82 pub fn parse_unit(unit_str: &str) -> Result<U, UnitError> {
84 U::parse(unit_str)
85 }
86
87 pub fn convert_value(from_unit: U, to_unit: U, value: f64) -> f64 {
89 if from_unit == to_unit {
90 value
91 } else if let Some(direct_value) = Self::convert_direct(from_unit, to_unit, value) {
92 direct_value
93 } else {
94 let base_value = Self::to_base_value(from_unit, value);
95 Self::from_base_value(base_value, to_unit)
96 }
97 }
98}
99
100impl<U: UnitType> fmt::Display for Dimension<U> {
101 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102 write!(f, "{}{}", self.value, self.unit.canonical_string())
103 }
104}
105
106
107#[derive(Debug, Clone, PartialEq)]
108pub enum UnitError {
109 UnknownUnit(String),
110}
111
112impl fmt::Display for UnitError {
113 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
114 match self {
115 UnitError::UnknownUnit(unit) => write!(f, "Unknown unit: '{}'", unit),
116 }
117 }
118}
119
120impl std::error::Error for UnitError {}