1use crate::dimension::{DimDiv, DimMul, Dimension};
7use crate::scalar::Scalar;
8use crate::Quantity;
9use core::fmt::{Debug, Display, Formatter, LowerExp, Result, UpperExp};
10use core::marker::PhantomData;
11
12pub trait Unit: Copy + PartialEq + Debug + 'static {
42 const RATIO: f64;
44
45 type Dim: Dimension;
47
48 const SYMBOL: &'static str;
50}
51
52#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
60pub struct Per<N: Unit, D: Unit>(PhantomData<(N, D)>);
61
62impl<N: Unit, D: Unit> Unit for Per<N, D>
63where
64 N::Dim: DimDiv<D::Dim>,
65 <N::Dim as DimDiv<D::Dim>>::Output: Dimension,
66{
67 const RATIO: f64 = N::RATIO / D::RATIO;
68 type Dim = <N::Dim as DimDiv<D::Dim>>::Output;
69 const SYMBOL: &'static str = "";
72}
73
74impl<N: Unit, D: Unit, S: Scalar + Display> Display for Quantity<Per<N, D>, S>
75where
76 N::Dim: DimDiv<D::Dim>,
77 <N::Dim as DimDiv<D::Dim>>::Output: Dimension,
78{
79 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
80 Display::fmt(&self.value(), f)?;
81 write!(f, " {}/{}", N::SYMBOL, D::SYMBOL)
82 }
83}
84
85impl<N: Unit, D: Unit, S: Scalar + LowerExp> LowerExp for Quantity<Per<N, D>, S>
86where
87 N::Dim: DimDiv<D::Dim>,
88 <N::Dim as DimDiv<D::Dim>>::Output: Dimension,
89{
90 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
91 LowerExp::fmt(&self.value(), f)?;
92 write!(f, " {}/{}", N::SYMBOL, D::SYMBOL)
93 }
94}
95
96impl<N: Unit, D: Unit, S: Scalar + UpperExp> UpperExp for Quantity<Per<N, D>, S>
97where
98 N::Dim: DimDiv<D::Dim>,
99 <N::Dim as DimDiv<D::Dim>>::Output: Dimension,
100{
101 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
102 UpperExp::fmt(&self.value(), f)?;
103 write!(f, " {}/{}", N::SYMBOL, D::SYMBOL)
104 }
105}
106
107#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
113pub struct Prod<A: Unit, B: Unit>(PhantomData<(A, B)>);
114
115impl<A: Unit, B: Unit> Unit for Prod<A, B>
116where
117 A::Dim: DimMul<B::Dim>,
118 <A::Dim as DimMul<B::Dim>>::Output: Dimension,
119{
120 const RATIO: f64 = A::RATIO * B::RATIO;
121 type Dim = <A::Dim as DimMul<B::Dim>>::Output;
122 const SYMBOL: &'static str = "";
125}
126
127impl<A: Unit, B: Unit, S: Scalar + Display> Display for Quantity<Prod<A, B>, S>
128where
129 A::Dim: DimMul<B::Dim>,
130 <A::Dim as DimMul<B::Dim>>::Output: Dimension,
131{
132 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
133 Display::fmt(&self.value(), f)?;
134 write!(f, " {}·{}", A::SYMBOL, B::SYMBOL)
135 }
136}
137
138impl<A: Unit, B: Unit, S: Scalar + LowerExp> LowerExp for Quantity<Prod<A, B>, S>
139where
140 A::Dim: DimMul<B::Dim>,
141 <A::Dim as DimMul<B::Dim>>::Output: Dimension,
142{
143 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
144 LowerExp::fmt(&self.value(), f)?;
145 write!(f, " {}·{}", A::SYMBOL, B::SYMBOL)
146 }
147}
148
149impl<A: Unit, B: Unit, S: Scalar + UpperExp> UpperExp for Quantity<Prod<A, B>, S>
150where
151 A::Dim: DimMul<B::Dim>,
152 <A::Dim as DimMul<B::Dim>>::Output: Dimension,
153{
154 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
155 UpperExp::fmt(&self.value(), f)?;
156 write!(f, " {}·{}", A::SYMBOL, B::SYMBOL)
157 }
158}
159
160#[cfg(all(test, feature = "std"))]
161mod tests {
162 use super::*;
163 use crate::units::length::{Kilometer, Meter};
164 use crate::units::time::Second;
165 use crate::Quantity;
166
167 #[test]
170 fn per_display_formats_value_and_symbol() {
171 let qty: Quantity<Per<Meter, Second>> = Quantity::new(10.0);
173 let s = format!("{qty}");
174 assert_eq!(s, "10 m/s");
175 }
176
177 #[test]
178 fn per_display_with_precision() {
179 let qty: Quantity<Per<Meter, Second>> = Quantity::new(1.5);
180 let s = format!("{qty:.2}");
181 assert_eq!(s, "1.50 m/s");
182 }
183
184 #[test]
185 fn per_lower_exp_formats_correctly() {
186 let qty: Quantity<Per<Meter, Second>> = Quantity::new(1000.0);
187 let s = format!("{qty:e}");
188 assert!(s.contains("e"), "Expected scientific notation, got: {s}");
189 assert!(s.ends_with("m/s"), "Expected 'm/s' suffix, got: {s}");
190 }
191
192 #[test]
193 fn per_upper_exp_formats_correctly() {
194 let qty: Quantity<Per<Meter, Second>> = Quantity::new(1000.0);
195 let s = format!("{qty:E}");
196 assert!(s.contains("E"), "Expected uppercase-E notation, got: {s}");
197 assert!(s.ends_with("m/s"), "Expected 'm/s' suffix, got: {s}");
198 }
199
200 #[test]
203 fn prod_display_formats_value_and_symbol() {
204 let qty: Quantity<Prod<Meter, Second>> = Quantity::new(3.0);
205 let s = format!("{qty}");
206 assert_eq!(s, "3 m·s");
207 }
208
209 #[test]
210 fn prod_display_with_precision() {
211 let qty: Quantity<Prod<Meter, Second>> = Quantity::new(2.5);
212 let s = format!("{qty:.3}");
213 assert_eq!(s, "2.500 m·s");
214 }
215
216 #[test]
217 fn prod_lower_exp_formats_correctly() {
218 let qty: Quantity<Prod<Kilometer, Second>> = Quantity::new(5000.0);
219 let s = format!("{qty:.2e}");
220 assert!(s.contains("e"), "Expected scientific notation, got: {s}");
221 assert!(s.ends_with("km·s"), "Expected 'km·s' suffix, got: {s}");
222 }
223
224 #[test]
225 fn prod_upper_exp_formats_correctly() {
226 let qty: Quantity<Prod<Kilometer, Second>> = Quantity::new(5000.0);
227 let s = format!("{qty:.2E}");
228 assert!(s.contains("E"), "Expected uppercase-E notation, got: {s}");
229 assert!(s.ends_with("km·s"), "Expected 'km·s' suffix, got: {s}");
230 }
231
232 }