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
//! Unit expressions as written by the user (never normalized away).
use core::fmt;
/// Exponent on a unit factor (`ft^2`, `psi^(1/2)`, `psi^0.5`).
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum UnitExponent {
/// Integer exponent (`ft^2`, `psi^-1`).
Int(i32),
/// Rational exponent in parentheses (`psi^(1/2)`).
Ratio {
/// Numerator.
num: i32,
/// Denominator.
den: i32,
},
/// Decimal exponent (`psi^0.5`).
Decimal(String),
}
impl fmt::Debug for UnitExponent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Int(n) => write!(f, "{n}"),
Self::Ratio { num, den } => write!(f, "{num}/{den}"),
Self::Decimal(s) => write!(f, "{s}"),
}
}
}
/// A unit expression preserving user syntax (e.g. `kip·ft`, `lbf/ft^2`).
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum UnitExpr {
/// A single registered unit name.
Named(String),
/// Product of factors (`kip*ft`, `kip·ft`).
Product(Vec<UnitExpr>),
/// Quotient via tight `/` (`lbf/ft`).
Quotient(Box<UnitExpr>, Box<UnitExpr>),
/// Unit raised to a power (`ft^2`, `psi^0.5`).
Pow {
/// Base unit expression.
base: Box<UnitExpr>,
/// Exponent.
exp: UnitExponent,
},
/// Dimensionless (numeric literals, `1`).
Dimensionless,
}
impl UnitExpr {
/// Named unit from a string slice.
pub fn named(name: impl Into<String>) -> Self {
Self::Named(name.into())
}
/// Dimensionless unit marker.
pub fn one() -> Self {
Self::Dimensionless
}
/// Primary display name for this unit expression.
pub fn as_str(&self) -> &str {
match self {
Self::Named(s) => s.as_str(),
Self::Dimensionless => "1",
Self::Product(parts) => parts
.first()
.map(|u| u.as_str())
.unwrap_or("1"),
Self::Quotient(num, _) => num.as_str(),
Self::Pow { base, .. } => base.as_str(),
}
}
}
impl fmt::Debug for UnitExpr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Named(s) => write!(f, "{s}"),
Self::Dimensionless => write!(f, "1"),
Self::Product(parts) => {
for (i, p) in parts.iter().enumerate() {
if i > 0 {
write!(f, "·")?;
}
write!(f, "{p:?}")?;
}
Ok(())
}
Self::Quotient(num, den) => write!(f, "{num:?}/{den:?}"),
Self::Pow { base, exp } => write!(f, "{base:?}^{exp:?}"),
}
}
}
impl fmt::Display for UnitExpr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}