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