1use core::ops::{Deref, DerefMut};
6use irox_tools::{cfg_feature_std, ToF64};
7
8#[allow(unused_imports)]
9use irox_tools::f64::FloatExt;
10
11#[derive(Debug, Copy, Clone, Eq, PartialEq)]
12pub enum Units {
13 Gram,
14 Meter,
15 SquareMeter,
16 CubicMeter,
17 MeterPerSecond,
18 MeterPerSecondPerSecond,
19 Second,
20 Mole,
21 Ampere,
22 Kelvin,
23 Candela,
24 Newton,
25 Joule,
26 Katal,
27 Coulomb,
28 Celsius,
29 Lux,
30 Lumen,
31 Farad,
32 Weber,
33 Watt,
34 Pascal,
35 Gray,
36 Becquerel,
37 Henry,
38 Volt,
39 Ohm,
40 Steradian,
41 Radian,
42 Siemens,
43 Tesla,
44 Hertz,
45 Sievert,
46 Other {
47 name: &'static str,
48 symbol: &'static str,
49 },
50}
51
52impl Units {
53 pub fn name(&self) -> &'static str {
54 match self {
55 Units::Gram => "Gram",
56 Units::Meter => "Meter",
57 Units::SquareMeter => "SquareMeter",
58 Units::CubicMeter => "CubicMeter",
59 Units::MeterPerSecond => "MeterPerSecond",
60 Units::MeterPerSecondPerSecond => "MeterPerSecondPerSecond",
61 Units::Second => "Second",
62 Units::Mole => "Mole",
63 Units::Ampere => "Ampere",
64 Units::Kelvin => "Kelvin",
65 Units::Candela => "Candela",
66 Units::Newton => "Newton",
67 Units::Joule => "Joule",
68 Units::Katal => "Katal",
69 Units::Coulomb => "Coulomb",
70 Units::Celsius => "Celsius",
71 Units::Lux => "Lux",
72 Units::Lumen => "Lumen",
73 Units::Farad => "Farad",
74 Units::Weber => "Weber",
75 Units::Watt => "Watt",
76 Units::Pascal => "Pascal",
77 Units::Gray => "Gray",
78 Units::Becquerel => "Becquerel",
79 Units::Henry => "Henry",
80 Units::Volt => "Volt",
81 Units::Ohm => "Ohm",
82 Units::Steradian => "Steradian",
83 Units::Radian => "Radian",
84 Units::Siemens => "Siemens",
85 Units::Tesla => "Tesla",
86 Units::Hertz => "Hertz",
87 Units::Sievert => "Sievert",
88 Units::Other { name, symbol: _ } => name,
89 }
90 }
91 pub fn symbol(&self) -> &'static str {
92 match self {
93 Units::Gram => "g",
94 Units::Meter => "m",
95 Units::SquareMeter => "m\u{00B2}",
96 Units::CubicMeter => "m\u{00B3}",
97 Units::MeterPerSecond => "m/s",
98 Units::MeterPerSecondPerSecond => "m/s\u{00B2}",
99 Units::Second => "s",
100 Units::Mole => "mol",
101 Units::Ampere => "A",
102 Units::Kelvin => "K",
103 Units::Candela => "cd",
104 Units::Newton => "N",
105 Units::Joule => "J",
106 Units::Katal => "kat",
107 Units::Coulomb => "C",
108 Units::Celsius => "\u{00B0}C",
109 Units::Lux => "lx",
110 Units::Lumen => "lm",
111 Units::Farad => "F",
112 Units::Weber => "Wb",
113 Units::Watt => "W",
114 Units::Pascal => "Pa",
115 Units::Gray => "Gy",
116 Units::Becquerel => "Bq",
117 Units::Henry => "H",
118 Units::Volt => "V",
119 Units::Ohm => "\u{03A9}",
120 Units::Steradian => "sr",
121 Units::Radian => "rad",
122 Units::Siemens => "S",
123 Units::Tesla => "T",
124 Units::Hertz => "Hz",
125 Units::Sievert => "Sv",
126 Units::Other { name: _, symbol } => symbol,
127 }
128 }
129
130 cfg_feature_std! {
131 pub fn format<T: ToF64>(&self, v: &T) -> String {
132 let value = v.to_f64();
133 if let Some(prefix) = crate::prefixes::PrefixSet::Common.best_prefix_for(&value) {
134 let scale = value / prefix.scale_factor();
135 format!("{scale:.3}{}{}", prefix.symbol(), self.symbol())
136 } else {
137 format!("{:.3}{}", value, self.symbol() )
138 }
139 }
140 }
141}
142
143#[derive(Debug, Copy, Clone)]
144pub struct Quantity<T: ToF64> {
145 value: T,
146 unit: Units,
147}
148impl<T: ToF64> Quantity<T> {
149 #[must_use]
150 pub const fn new(value: T, unit: Units) -> Self {
151 Self { value, unit }
152 }
153 #[must_use]
154 pub const fn unit(&self) -> &Units {
155 &self.unit
156 }
157 #[must_use]
158 pub fn value(&self) -> &T {
159 &self.value
160 }
161}
162impl<T: ToF64> Deref for Quantity<T> {
163 type Target = T;
164 fn deref(&self) -> &Self::Target {
165 &self.value
166 }
167}
168impl<T: ToF64> DerefMut for Quantity<T> {
169 fn deref_mut(&mut self) -> &mut Self::Target {
170 &mut self.value
171 }
172}
173
174cfg_feature_std! {
175 use core::fmt::{Display, Formatter};
176 impl<T: ToF64> Display for Quantity<T> {
177 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
178 f.write_str(&self.unit.format(self.value()))
179 }
180 }
181}
182
183pub const ONE_HYPERFINE_SECOND: Quantity<u64> = Quantity::new(9_192_631_770, Units::Hertz);
184pub const SPEED_OF_LIGHT_VACUUM: Quantity<u64> = Quantity::new(299_792_458, Units::MeterPerSecond);
185pub const ELEMENTARY_CHARGE: Quantity<f64> = Quantity::new(1.602176634e-19, Units::Coulomb);
186
187#[cfg(all(test, feature = "alloc"))]
188mod test {
189 use crate::quantities::{Quantity, Units};
190
191 #[test]
192 pub fn test() {
193 assert_eq!("1.025mV", Units::Volt.format(&1.025e-3));
194 assert_eq!("10.250nV", Units::Volt.format(&1.025e-8));
195
196 let mut q = Quantity::new(1.0256e-3, Units::Volt);
197 assert_eq!("1.026mV", q.to_string());
198 assert_eq!("1.026mV", format!("{q}"));
199 *q = 1.025e-8;
200 assert_eq!("10.250nV", q.to_string());
201 assert_eq!("10.250nV", format!("{q}"));
202 *q = 1.025e4;
203 assert_eq!("10.250kV", q.to_string());
204 assert_eq!("10.250kV", format!("{q}"));
205
206 let q = Quantity::new(1.0256e-8, Units::Ohm);
207 assert_eq!("10.256n\u{03A9}", q.to_string());
208
209 let q = Quantity::new(1.0256e-8, Units::Celsius);
210 assert_eq!("10.256n\u{00B0}C", q.to_string());
211
212 let q = Quantity::new(1.0256e-8, Units::SquareMeter);
213 assert_eq!("10.256nm\u{00B2}", q.to_string());
214 let q = Quantity::new(1.0256e-8, Units::CubicMeter);
215 assert_eq!("10.256nm\u{00B3}", q.to_string());
216 let q = Quantity::new(1.0256e-8, Units::MeterPerSecondPerSecond);
217 assert_eq!("10.256nm/s\u{00B2}", q.to_string());
218 }
219}