1#![doc = include_str ! ("../README.md")]
11#![cfg_attr(not(feature = "std"), no_std)]
12#![deny(non_ascii_idents)]
14#![deny(unsafe_code)]
15#![warn(missing_debug_implementations)]
16#![warn(missing_docs)]
17#![warn(trivial_casts)]
18#![warn(unused)]
19#![allow(dead_code)]
20#![warn(clippy::cast_possible_truncation)]
22#![warn(clippy::cast_possible_wrap)]
23#![warn(clippy::cast_precision_loss)]
24#![warn(clippy::cast_sign_loss)]
25#![warn(clippy::cognitive_complexity)]
26#![warn(clippy::decimal_literal_representation)]
27#![warn(clippy::enum_glob_use)]
28#![warn(clippy::equatable_if_let)]
29#![warn(clippy::fallible_impl_from)]
30#![warn(clippy::if_not_else)]
31#![warn(clippy::if_then_some_else_none)]
32#![warn(clippy::implicit_clone)]
33#![warn(clippy::integer_division)]
34#![warn(clippy::manual_assert)]
35#![warn(clippy::match_same_arms)]
36#![warn(clippy::mismatching_type_param_order)]
37#![warn(clippy::missing_const_for_fn)]
38#![warn(clippy::missing_errors_doc)]
39#![warn(clippy::missing_panics_doc)]
40#![warn(clippy::multiple_crate_versions)]
41#![warn(clippy::multiple_inherent_impl)]
42#![warn(clippy::must_use_candidate)]
43#![warn(clippy::needless_pass_by_value)]
44#![warn(clippy::print_stderr)]
45#![warn(clippy::print_stdout)]
46#![warn(clippy::semicolon_if_nothing_returned)]
47#![warn(clippy::undocumented_unsafe_blocks)]
48#![warn(clippy::unicode_not_nfc)]
49#![warn(clippy::unimplemented)]
50#![warn(clippy::unseparated_literal_suffix)]
51#![warn(clippy::unused_self)]
52#![warn(clippy::unwrap_in_result)]
53#![warn(clippy::use_self)]
54#![warn(clippy::used_underscore_binding)]
55#![warn(clippy::wildcard_imports)]
56
57extern crate alloc;
58
59use alloc::{borrow::ToOwned, format, string::String};
60use core::{
61 cmp::Ordering,
62 fmt,
63 ops::{Add, Div, Mul, Sub},
64};
65
66#[cfg(feature = "fpdec")]
67pub use amnt_dec::{AmountT, Dec, Decimal, AMNT_ONE, AMNT_ZERO};
68#[cfg(all(
69 not(feature = "fpdec"),
70 any(
71 all(feature = "f32", not(feature = "f64")),
72 all(feature = "f32", feature = "f64", target_pointer_width = "32"),
73 all(
74 not(feature = "f32"),
75 not(feature = "f64"),
76 target_pointer_width = "32"
77 )
78 )
79))]
80pub use amnt_f32::{AmountT, AMNT_ONE, AMNT_ZERO};
81#[cfg(all(
82 not(feature = "fpdec"),
83 any(
84 all(not(feature = "f32"), feature = "f64"),
85 all(feature = "f32", feature = "f64", target_pointer_width = "64"),
86 all(
87 not(feature = "f32"),
88 not(feature = "f64"),
89 target_pointer_width = "64"
90 )
91 )
92))]
93pub use amnt_f64::{AmountT, AMNT_ONE, AMNT_ZERO};
94pub use converter::{ConversionTable, Converter};
95pub use rate::Rate;
96pub use si_prefixes::SIPrefix;
97
98mod converter;
99pub mod prelude;
100mod rate;
101mod si_prefixes;
102
103#[cfg(feature = "fpdec")]
104#[doc(hidden)]
105pub mod amnt_dec;
106#[cfg(all(
107 not(feature = "fpdec"),
108 any(
109 all(feature = "f32", not(feature = "f64")),
110 all(feature = "f32", feature = "f64", target_pointer_width = "32"),
111 all(
112 not(feature = "f32"),
113 not(feature = "f64"),
114 target_pointer_width = "32"
115 )
116 )
117))]
118#[doc(hidden)]
119pub mod amnt_f32;
120#[cfg(all(
121 not(feature = "fpdec"),
122 any(
123 all(not(feature = "f32"), feature = "f64"),
124 all(feature = "f32", feature = "f64", target_pointer_width = "64"),
125 all(
126 not(feature = "f32"),
127 not(feature = "f64"),
128 target_pointer_width = "64"
129 )
130 )
131))]
132#[doc(hidden)]
133pub mod amnt_f64;
134
135#[cfg(feature = "acceleration")]
136pub mod acceleration;
137#[cfg(feature = "area")]
138pub mod area;
139#[cfg(feature = "datathroughput")]
140pub mod datathroughput;
141#[cfg(feature = "datavolume")]
142pub mod datavolume;
143#[cfg(feature = "duration")]
144pub mod duration;
145#[cfg(feature = "energy")]
146pub mod energy;
147#[cfg(feature = "force")]
148pub mod force;
149#[cfg(feature = "frequency")]
150pub mod frequency;
151#[cfg(feature = "length")]
152pub mod length;
153#[cfg(feature = "mass")]
154pub mod mass;
155#[cfg(feature = "power")]
156pub mod power;
157#[cfg(feature = "speed")]
158pub mod speed;
159#[cfg(feature = "temperature")]
160pub mod temperature;
161#[cfg(feature = "volume")]
162pub mod volume;
163
164pub trait Unit:
166 Copy + Eq + PartialEq + Sized + Mul<AmountT> + fmt::Display
167{
168 type QuantityType: Quantity<UnitType = Self>;
170
171 fn iter() -> impl Iterator<Item = Self>;
173
174 #[must_use]
177 fn from_symbol(symbol: &str) -> Option<Self> {
178 Self::iter().find(|&unit| unit.symbol() == symbol)
179 }
180
181 fn name(&self) -> String;
183
184 fn symbol(&self) -> String;
186
187 fn si_prefix(&self) -> Option<SIPrefix>;
189
190 fn as_qty(&self) -> Self::QuantityType {
192 Self::QuantityType::new(AMNT_ONE, *self)
193 }
194
195 fn fmt(&self, form: &mut fmt::Formatter<'_>) -> fmt::Result {
202 fmt::Display::fmt(&self.symbol(), form)
203 }
204}
205
206pub trait LinearScaledUnit: Unit {
208 const REF_UNIT: Self;
210
211 #[must_use]
214 fn from_scale(amnt: AmountT) -> Option<Self> {
215 Self::iter().find(|&unit| unit.scale() == amnt)
216 }
217
218 #[inline(always)]
220 fn is_ref_unit(&self) -> bool {
221 *self == Self::REF_UNIT
222 }
223
224 fn scale(&self) -> AmountT;
226
227 #[inline(always)]
229 fn ratio(&self, other: &Self) -> AmountT {
230 self.scale() / other.scale()
231 }
232}
233
234pub trait Quantity: Copy + Sized + Mul<AmountT> {
236 type UnitType: Unit<QuantityType = Self>;
238
239 #[must_use]
241 fn iter_units() -> impl Iterator<Item = Self::UnitType> {
242 Self::UnitType::iter()
243 }
244
245 #[must_use]
248 fn unit_from_symbol(symbol: &str) -> Option<Self::UnitType> {
249 Self::iter_units().find(|&unit| unit.symbol() == symbol)
250 }
251
252 fn new(amount: AmountT, unit: Self::UnitType) -> Self;
254
255 fn amount(&self) -> AmountT;
257
258 fn unit(&self) -> Self::UnitType;
260
261 #[inline(always)]
264 fn eq(&self, other: &Self) -> bool {
265 self.unit() == other.unit() && self.amount() == other.amount()
266 }
267
268 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
271 if self.unit() == other.unit() {
272 PartialOrd::partial_cmp(&self.amount(), &other.amount())
273 } else {
274 None
275 }
276 }
277
278 fn add(self, rhs: Self) -> Self {
284 if self.unit() == rhs.unit() {
285 return Self::new(self.amount() + rhs.amount(), self.unit());
286 }
287 panic!(
288 "Can't add '{}' and '{}'.",
289 self.unit().symbol(),
290 rhs.unit().symbol()
291 );
292 }
293
294 fn sub(self, rhs: Self) -> Self {
301 if self.unit() == rhs.unit() {
302 return Self::new(self.amount() - rhs.amount(), self.unit());
303 }
304 panic!(
305 "Can't subtract '{}' and '{}'.",
306 self.unit().symbol(),
307 rhs.unit().symbol(),
308 );
309 }
310
311 fn div(self, rhs: Self) -> AmountT {
317 if self.unit() == rhs.unit() {
318 return self.amount() / rhs.amount();
319 }
320 panic!(
321 "Can't divide '{}' and '{}'.",
322 self.unit().symbol(),
323 rhs.unit().symbol()
324 );
325 }
326
327 fn fmt(&self, form: &mut fmt::Formatter<'_>) -> fmt::Result {
334 if self.unit().symbol().is_empty() {
335 fmt::Display::fmt(&self.amount(), form)
336 } else {
337 let tmp: String;
338 let amnt_non_neg = self.amount() >= AMNT_ZERO;
339 #[cfg(feature = "fpdec")]
340 let abs_amnt = self.amount().abs();
341 #[cfg(not(feature = "fpdec"))]
342 let abs_amnt = if amnt_non_neg {
343 self.amount()
344 } else {
345 -self.amount()
346 };
347 if let Some(prec) = form.precision() {
348 tmp = format!("{:.*} {}", prec, abs_amnt, self.unit());
349 } else {
350 tmp = format!("{} {}", abs_amnt, self.unit());
351 }
352 form.pad_integral(amnt_non_neg, "", &tmp)
353 }
354 }
355}
356
357pub trait HasRefUnit: Quantity + Add<Self> + Sub<Self> + Div<Self>
359where
360 <Self as Quantity>::UnitType: LinearScaledUnit,
361{
362 const REF_UNIT: <Self as Quantity>::UnitType;
364
365 #[must_use]
368 fn unit_from_scale(amnt: AmountT) -> Option<Self::UnitType> {
369 Self::iter_units().find(|&unit| unit.scale() == amnt)
370 }
371
372 #[inline(always)]
374 fn equiv_amount(&self, unit: Self::UnitType) -> AmountT {
375 if self.unit() == unit {
376 self.amount()
377 } else {
378 self.unit().ratio(&unit) * self.amount()
379 }
380 }
381
382 fn convert(&self, to_unit: Self::UnitType) -> Self {
384 Self::new(self.equiv_amount(to_unit), to_unit)
385 }
386
387 #[inline(always)]
390 fn eq(&self, other: &Self) -> bool {
391 self.amount() == other.equiv_amount(self.unit())
392 }
393
394 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
397 if self.unit() == other.unit() {
398 PartialOrd::partial_cmp(&self.amount(), &other.amount())
399 } else {
400 PartialOrd::partial_cmp(
401 &self.amount(),
402 &other.equiv_amount(self.unit()),
403 )
404 }
405 }
406
407 #[inline]
409 fn add(self, rhs: Self) -> Self {
410 Self::new(self.amount() + rhs.equiv_amount(self.unit()), self.unit())
411 }
412
413 #[inline]
415 fn sub(self, rhs: Self) -> Self {
416 Self::new(self.amount() - rhs.equiv_amount(self.unit()), self.unit())
417 }
418
419 #[inline]
421 fn div(self, rhs: Self) -> AmountT {
422 self.amount() / rhs.equiv_amount(self.unit())
423 }
424
425 #[doc(hidden)]
426 #[must_use]
433 fn _fit(amount: AmountT) -> Self {
434 let take_all = Self::REF_UNIT.si_prefix().is_none();
435 let mut it = Self::iter_units()
436 .filter(|u| take_all || u.si_prefix().is_some());
437 let first = it.next().unwrap();
439 let last = it
440 .filter(|u| u.scale() > first.scale() && u.scale() <= amount)
441 .last();
442 match last {
443 Some(unit) => Self::new(amount / unit.scale(), unit),
444 None => Self::new(amount / first.scale(), first),
445 }
446 }
447}
448
449#[derive(Copy, Clone, Debug, Eq, PartialEq)]
451pub enum One {
452 One,
454}
455
456impl One {
457 const VARIANTS: [Self; 1] = [ONE];
458}
459
460pub const ONE: One = One::One;
462
463impl Unit for One {
464 type QuantityType = AmountT;
465 fn iter() -> impl Iterator<Item = Self> {
466 Self::VARIANTS.iter().cloned()
467 }
468 fn name(&self) -> String {
469 "One".to_owned()
470 }
471 fn symbol(&self) -> String {
472 "".to_owned()
473 }
474 fn si_prefix(&self) -> Option<SIPrefix> {
475 None
476 }
477}
478
479impl fmt::Display for One {
480 #[inline(always)]
481 fn fmt(&self, form: &mut fmt::Formatter<'_>) -> fmt::Result {
482 <Self as Unit>::fmt(self, form)
483 }
484}
485
486impl LinearScaledUnit for One {
487 const REF_UNIT: Self = ONE;
488 fn scale(&self) -> AmountT {
489 AMNT_ONE
490 }
491}
492
493impl Mul<One> for AmountT {
494 type Output = Self;
495 #[inline(always)]
496 fn mul(self, _rhs: One) -> Self::Output {
497 self
498 }
499}
500
501impl Mul<AmountT> for One {
502 type Output = AmountT;
503 #[inline(always)]
504 fn mul(self, rhs: AmountT) -> Self::Output {
505 rhs
506 }
507}
508
509impl Quantity for AmountT {
510 type UnitType = One;
511
512 #[inline(always)]
513 fn new(amount: AmountT, _unit: Self::UnitType) -> Self {
514 amount
515 }
516
517 #[inline(always)]
518 fn amount(&self) -> AmountT {
519 *self
520 }
521
522 #[inline(always)]
523 fn unit(&self) -> Self::UnitType {
524 ONE
525 }
526}
527
528impl HasRefUnit for AmountT {
529 const REF_UNIT: One = ONE;
530
531 #[inline(always)]
532 fn _fit(amount: AmountT) -> Self {
533 amount
534 }
535}