uom/lib.rs
1//! Units of measurement is a crate that does automatic type-safe zero-cost
2//! [dimensional analysis][analysis]. You can create your own systems or use the pre-built
3//! [International System of Units][si] (SI) which is based on the
4//! [International System of Quantities][isq] (ISQ) and includes numerous [quantities][quantity]
5//! (length, mass, time, ...) with conversion factors for even more numerous
6//! [measurement units][measurement] (meter, kilometer, foot, mile, ...). No more crashing your
7//! [climate orbiter][orbiter]!
8//!
9//! [analysis]: https://en.wikipedia.org/wiki/Dimensional_analysis
10//! [si]: https://jcgm.bipm.org/vim/en/1.16.html
11//! [isq]: https://jcgm.bipm.org/vim/en/1.6.html
12//! [quantity]: https://jcgm.bipm.org/vim/en/1.1.html
13//! [measurement]: https://jcgm.bipm.org/vim/en/1.9.html
14//! [orbiter]: https://en.wikipedia.org/wiki/Mars_Climate_Orbiter
15//!
16//! ## Usage
17//! `uom` requires `rustc` 1.65.0 or later. Add this to your `Cargo.toml`:
18//!
19//! ```toml
20//! [dependencies]
21//! uom = "0.37.0"
22//! ```
23//!
24//! and this to your crate root:
25//!
26//! ```rust
27//! extern crate uom;
28//! ```
29//!
30//! The simple example below shows how to use quantities and units as well as how `uom` stops
31//! invalid operations:
32//!
33#![cfg_attr(all(feature = "si", feature = "f32"), doc = " ```rust")]
34#![cfg_attr(not(all(feature = "si", feature = "f32")), doc = " ```rust,ignore")]
35//! extern crate uom;
36//!
37//! use uom::si::f32::*;
38//! use uom::si::length::kilometer;
39//! use uom::si::time::second;
40//!
41//! fn main() {
42//! let length = Length::new::<kilometer>(5.0);
43//! let time = Time::new::<second>(15.0);
44//! let velocity/*: Velocity*/ = length / time;
45//! let _acceleration = calc_acceleration(velocity, time);
46//! //let error = length + time; // error[E0308]: mismatched types
47//!
48//! // Get a quantity value in a specific unit.
49//! let time_in_nano_seconds = time.get::<uom::si::time::nanosecond>();
50//! }
51//!
52//! fn calc_acceleration(velocity: Velocity, time: Time) -> Acceleration {
53//! velocity / time
54//! }
55//! ```
56//!
57//! See examples provided with the source for more advanced usage including how to create `Quantity`
58//! type aliases for a different set of base units and how to create an entirely new system of
59//! quantities.
60//!
61//! ## Features
62//! `uom` has multiple `Cargo` features for controlling available underlying storage types, the
63//! inclusion of the pre-built [International System of Units][si] (SI), support for
64//! [Serde][serde], and `no_std` functionality. The features are described below. `f32`, `f64`,
65//! `std`, and `si` are enabled by default. Features can be cherry-picked by using the
66//! `--no-default-features` and `--features "..."` flags when compiling `uom` or specifying
67//! features in Cargo.toml:
68//!
69//! ```toml
70//! [dependencies]
71//! uom = {
72//! version = "0.37.0",
73//! default-features = false,
74//! features = [
75//! "autoconvert", # automatic base unit conversion.
76//! "usize", "u8", "u16", "u32", "u64", "u128", # Unsigned integer storage types.
77//! "isize", "i8", "i16", "i32", "i64", "i128", # Signed integer storage types.
78//! "bigint", "biguint", # Arbitrary width integer storage types.
79//! "rational", "rational32", "rational64", "bigrational", # Integer ratio storage types.
80//! "complex32", "complex64", # Complex floating point storage types.
81//! "f32", "f64", # Floating point storage types.
82//! "si", "std", # Built-in SI system and std library support.
83//! "serde", # Serde support.
84//! ]
85//! }
86//! ```
87//!
88//! * `autoconvert` -- Feature to enable automatic conversion between base units in binary
89//! operators. Disabling the feature only allows for quantities with the same base units to
90//! directly interact. The feature exists to account for compiler limitations where zero-cost
91//! code is not generated for non-floating point underlying storage types.
92//! * `usize`, `u8`, `u16`, `u32`, `u64`, `u128`, `isize`, `i8`, `i16`, `i32`, `i64`, `i128`,
93//! `bigint`, `biguint`, `rational`, `rational32`, `rational64`, `bigrational`, `complex32`,
94//! `complex64`, `f32`, `f64` -- Features to enable underlying storage types. At least one of
95//! these features must be enabled. `f32` and `f64` are enabled by default. See the
96//! [Design](#design) section for implications of choosing different underlying storage types.
97//! * `si` -- Feature to include the pre-built [International System of Units][si] (SI). Enabled by
98//! default.
99//! * `std` -- Feature to compile with standard library support. Disabling this feature compiles
100//! `uom` with `no_std`. Enabled by default.
101//! * `serde` -- Feature to enable support for serialization and deserialization of quantities with
102//! the [Serde][serde] crate. Disabled by default.
103//!
104//! [si]: https://jcgm.bipm.org/vim/en/1.16.html
105//! [serde]: https://serde.rs/
106//!
107//! ## Design
108//! Rather than working with [measurement units](https://jcgm.bipm.org/vim/en/1.9.html) (meter,
109//! kilometer, foot, mile, ...) `uom` works with [quantities](https://jcgm.bipm.org/vim/en/1.1.html)
110//! (length, mass, time, ...). This simplifies usage because units are only involved at interface
111//! boundaries: the rest of your code only needs to be concerned about the quantities involved.
112//! This also makes operations on quantities (+, -, \*, /, ...) have zero runtime cost over using
113//! the raw storage type (e.g. `f32`).
114//!
115//! `uom` normalizes values to the [base unit](https://jcgm.bipm.org/vim/en/1.10.html) for the
116//! quantity. Alternative base units can be used by executing the macro defined for the system of
117//! quantities (`ISQ!` for the SI). `uom` supports `usize`, `u8`, `u16`, `u32`, `u64`, `u128`,
118//! `isize`, `i8`, `i16`, `i32`, `i64`, `i128`, `bigint`, `biguint`, `rational`, `rational32`,
119//! `rational64`, `bigrational`, `complex32`, `complex64`, `f32`, and `f64` as the underlying
120//! storage type.
121//!
122//! A consequence of normalizing values to the base unit is that some values may not be able to be
123//! represented or can't be precisely represented for floating point and rational underlying
124//! storage types. For example if the base unit of `length` is `meter` and the underlying storage
125//! type is `i32` then values like `1 centimeter` or `1.1 meter` cannot be represented. `1
126//! centimeter` is normalized to `0.01 meter` which can't be stored in an `i32`. `uom` only allows
127//! units to be used safely. Users of this library will still need to be aware of implementation
128//! details of the underlying storage type including limits and precision.
129//!
130//! ## Contributing
131//! Contributions are welcome from everyone. Submit a pull request, an issue, or just add comments
132//! to an existing item. The [International Bureau of Weights and Measures][BIPM] is an
133//! international standards organization that publishes the [SI Brochure][brochure]. This document
134//! defines the [SI] and can be used as a comprehensive reference for changes to `uom`. Conversion
135//! factors for non-SI units can be found in NIST [Special Publication 811][nist811].
136//!
137//! Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in
138//! the work by you, as defined in the Apache-2.0 license, shall be dual licensed as below, without
139//! any additional terms or conditions.
140//!
141//! ### License
142//! Licensed under either of
143//!
144//! * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
145//! <https://www.apache.org/licenses/LICENSE-2.0>)
146//! * MIT license ([LICENSE-MIT](LICENSE-MIT) or <https://opensource.org/licenses/MIT>)
147//!
148//! at your option.
149//!
150//! [BIPM]: https://www.bipm.org/en/about-us/
151//! [brochure]: https://www.bipm.org/en/publications/si-brochure/
152//! [si]: https://jcgm.bipm.org/vim/en/1.16.html
153//! [nist811]: https://www.nist.gov/pml/special-publication-811/nist-guide-si-appendix-b-conversion-factors/nist-guide-si-appendix-b9
154
155// Compile with `no_std` when the `std` feature is not specified.
156#![cfg_attr(not(feature = "std"), no_std)]
157// Rustc lints.
158#![forbid(unsafe_code)]
159#![warn(
160 bare_trait_objects,
161 missing_copy_implementations,
162 missing_debug_implementations,
163 missing_docs,
164 trivial_casts,
165 trivial_numeric_casts,
166 unused_extern_crates,
167 unused_import_braces,
168 unused_qualifications,
169 unused_results
170)]
171// Clippy lints.
172#![cfg_attr(
173 clippy,
174 warn(
175 clippy::must_use_candidate,
176 clippy::return_self_not_must_use,
177 ),
178 allow(
179 clippy::deprecated_cfg_attr,
180 clippy::excessive_precision,
181 clippy::inconsistent_digit_grouping, // https://github.com/rust-lang/rust-clippy/issues/6096
182 clippy::inline_always,
183 )
184)]
185// Lints allowed in tests because they are unavoidable in the generic code when a type may or may
186// not need to be dereferenced or cloned.
187#![cfg_attr(all(clippy, test), allow(clippy::op_ref, clippy::clone_on_copy, clippy::float_cmp))]
188
189// Fail to compile if no underlying storage type features are specified.
190#[rustfmt::skip]
191#[cfg(not(any(
192 feature = "usize", feature = "u8", feature = "u16", feature = "u32", feature = "u64",
193 feature = "u128",
194 feature = "isize", feature = "i8", feature = "i16", feature = "i32", feature = "i64",
195 feature = "i128",
196 feature = "bigint", feature = "biguint",
197 feature = "rational", feature = "rational32", feature = "rational64", feature = "bigrational",
198 feature = "complex32", feature = "complex64",
199 feature = "f32", feature = "f64", )))]
200compile_error!("A least one underlying storage type must be enabled. See the features section of \
201 uom documentation for available underlying storage type options.");
202
203#[doc(hidden)]
204pub extern crate num_traits;
205
206#[doc(hidden)]
207#[cfg(feature = "bigint-support")]
208pub extern crate num_bigint;
209
210#[doc(hidden)]
211#[cfg(any(feature = "rational-support", feature = "bigint-support"))]
212pub extern crate num_rational;
213
214#[doc(hidden)]
215#[cfg(feature = "complex-support")]
216pub extern crate num_complex;
217
218#[doc(hidden)]
219#[cfg(feature = "serde")]
220pub extern crate serde;
221
222#[doc(hidden)]
223pub extern crate typenum;
224
225#[cfg(all(
226 test,
227 any(feature = "f32", feature = "f64", feature = "complex32", feature = "complex64")
228))]
229#[macro_use]
230extern crate approx;
231#[cfg(test)]
232#[macro_use]
233extern crate quickcheck;
234#[cfg(test)]
235#[macro_use]
236extern crate static_assertions;
237
238// Conditionally import `core` or `std` based on feature selection.
239#[doc(hidden)]
240pub mod lib {
241 #[cfg(not(feature = "std"))]
242 pub use core::*;
243 #[cfg(feature = "std")]
244 pub use std::*;
245
246 // Re-export `ops` module along with `typenum::ops` to provide all types in a single mod. This
247 // allows the `system!` macro to reference all operations by the absolute path. Macro paths and
248 // idents can't easily be combined without a `use` statement that pollutes the macro execution
249 // location's namespace.
250 pub mod ops {
251 #[cfg(not(feature = "std"))]
252 pub use core::ops::*;
253 #[cfg(feature = "std")]
254 pub use std::ops::*;
255 pub use typenum::type_operators::*;
256 }
257
258 // Export `panic` module when the `std` feature is not enabled. `RefUnwindSafe` and `UnwindSafe`
259 // traits do not exist in `core` but are conditionally needed in traits defined by `uom` when
260 // `std` is enabled. These definitions work around conditional requirements.
261 #[cfg(not(feature = "std"))]
262 pub mod panic {
263 pub trait RefUnwindSafe {}
264 pub trait UnwindSafe {}
265 }
266}
267
268// Conditionally import num sub-crate types based on feature selection.
269#[doc(hidden)]
270pub mod num {
271 #[cfg(feature = "std")]
272 pub use num_traits::float::Float;
273 #[cfg(not(feature = "std"))]
274 pub use num_traits::float::FloatCore as Float;
275
276 pub use num_traits::{pow, FromPrimitive, Num, One, Saturating, Signed, ToPrimitive, Zero};
277
278 #[cfg(feature = "bigint-support")]
279 pub use num_bigint::{BigInt, BigUint};
280
281 #[cfg(feature = "rational-support")]
282 pub type Rational = num_rational::Ratio<isize>;
283
284 #[cfg(feature = "bigint-support")]
285 pub use num_rational::BigRational;
286
287 #[cfg(any(feature = "rational-support", feature = "bigint-support"))]
288 pub mod rational {
289 pub use num_rational::*;
290 }
291
292 #[cfg(feature = "complex-support")]
293 pub mod complex {
294 pub use num_complex::*;
295 }
296}
297
298/// Primitive traits and types representing basic properties of types.
299pub mod marker {
300 /// Trait to denote that a quantity is able to be added with a quantity of the same dimensions.
301 /// When a specific quantity's kind inherits this trait `ops::Add` is implemented
302 /// automatically.
303 pub trait Add {}
304
305 /// Trait to denote that a quantity is able to be added with a quantity of the same dimensions.
306 /// When a specific quantity's kind inherits this trait `ops::AddAssign` is implemented
307 /// automatically.
308 pub trait AddAssign {}
309
310 /// Trait to denote that a quantity is able to be subtracted with a quantity of the same
311 /// dimensions. When a specific quantity's kind inherits this trait `ops::Sub` is implemented
312 /// automatically.
313 pub trait Sub {}
314
315 /// Trait to denote that a quantity is able to be subtracted with a quantity of the same
316 /// dimensions. When a specific quantity's kind inherits this trait `ops::SubAssign` is
317 /// implemented automatically.
318 pub trait SubAssign {}
319
320 /// Trait to denote that a quantity is able to be multiplied with a quantity of the same
321 /// dimensions. When a specific quantity's kind inherits this trait `ops::Mul` is implemented
322 /// automatically.
323 pub trait Mul {}
324
325 /// Trait to denote that a quantity is able to be multiplied with a quantity of the same
326 /// dimensions. When a specific quantity's kind inherits this trait `ops::MulAssign` is
327 /// implemented automatically.
328 pub trait MulAssign {}
329
330 /// Trait to denote that a quantity is able to be divided with a quantity of the same
331 /// dimensions. When a specific quantity's kind inherits this trait `ops::Div` is implemented
332 /// automatically.
333 pub trait Div {}
334
335 /// Trait to denote that a quantity is able to be divided with a quantity of the same
336 /// dimensions. When a specific quantity's kind inherits this trait `ops::DivAssign` is
337 /// implemented automatically.
338 pub trait DivAssign {}
339
340 /// Trait to denote that a quantity is able to be negated. When a specific quantity's kind
341 /// inherits this trait `ops::Neg` is implemented automatically.
342 pub trait Neg {}
343
344 /// Trait to denote that a quantity is able to calculate a remainder with a quantity of the
345 /// same dimensions. When a specific quantity's kind inherits this trait `ops::Rem` is
346 /// implemented automatically.
347 pub trait Rem {}
348
349 /// Trait to denote that a quantity is able to calculate a remainder with a quantity of the
350 /// same dimensions. When a specific quantity's kind inherits this trait `ops::RemAssign` is
351 /// implemented automatically.
352 pub trait RemAssign {}
353
354 /// Trait to denote that a quantity is able to perform saturating additions and subtractions
355 /// with a quantity of the same dimensions. When a specific quantity's kind inherits this trait
356 /// `ops::Saturating` is implemented automatically.
357 pub trait Saturating {}
358}
359
360#[macro_use]
361mod features;
362
363#[macro_use]
364mod storage_types;
365
366#[macro_use]
367mod system;
368
369#[macro_use]
370mod quantity;
371
372#[macro_use]
373mod unit;
374
375#[cfg(feature = "si")]
376#[macro_use]
377pub mod si;
378
379#[cfg(test)]
380mod tests;
381
382/// Operations performed on the constant portion of the [conversion factor][factor]. Used to help
383/// guide optimizations when floating point underlying storage types are used.
384///
385/// For a value, `v: Float`, adding `-0.0` is a no-op while adding `0.0` will change the sign if
386/// `v` is `-0.0`. The opposite is true for subtraction.
387///
388/// ```ignore
389/// v
390/// 0.0 + -0.0 = 0.0
391/// -0.0 + 0.0 = 0.0 // v + 0.0 != v
392/// -0.0 + -0.0 = -0.0
393/// 0.0 - -0.0 = 0.0
394/// -0.0 - 0.0 = 0.0
395/// -0.0 - -0.0 = 0.0 // v - -0.0 != v
396/// ```
397///
398/// [factor]: https://jcgm.bipm.org/vim/en/1.24.html
399#[derive(Clone, Copy, Debug)]
400pub enum ConstantOp {
401 /// Hint that the constant is being added to a value.
402 Add,
403
404 /// Hint that the constant is being subtracted from a value.
405 Sub,
406}
407
408/// Trait to identify [units][units] which have a [conversion factor][factor].
409///
410/// ## Generic Parameters
411/// * `V`: Underlying storage type trait is implemented for.
412///
413/// [units]: https://jcgm.bipm.org/vim/en/1.13.html
414/// [factor]: https://jcgm.bipm.org/vim/en/1.24.html
415pub trait Conversion<V> {
416 /// Conversion factor type specific to the underlying storage type.
417 type T: ConversionFactor<V>;
418
419 /// Coefficient portion of [conversion factor](https://jcgm.bipm.org/vim/en/1.24.html) for
420 /// converting the given unit. To convert to the base unit for the quantity use `(value +
421 /// constant()) * coefficient()`. To convert from the base unit, `(value / coefficient()) -
422 /// constant()` is used. Implementation should return the multiplicative identity
423 /// (`Self::T::one()`) if no coefficient exists.
424 #[must_use = "method returns a new number and does not mutate the original value"]
425 #[inline(always)]
426 fn coefficient() -> Self::T {
427 <Self::T as num::One>::one()
428 }
429
430 /// Constant portion of [conversion factor](https://jcgm.bipm.org/vim/en/1.24.html) for
431 /// converting the given unit. To convert to the base unit for the quantity use `(value +
432 /// constant()) * coefficient()`. To convert from the base unit, `(value / coefficient()) -
433 /// constant()` is used. Implementation should return the additive identity (`Self::T::zero()`)
434 /// if no constant exists. See [`ConstantOp`] documentation for details about parameter use to
435 /// ensure the method optimizes correctly.
436 #[must_use = "method returns a new number and does not mutate the original value"]
437 #[inline(always)]
438 #[allow(unused_variables)]
439 fn constant(op: ConstantOp) -> Self::T {
440 <Self::T as num::Zero>::zero()
441 }
442
443 /// Instance [conversion factor](https://jcgm.bipm.org/vim/en/1.24.html).
444 ///
445 /// Default implementation returns the coefficient: `Self::coefficient()`.
446 #[must_use = "method returns a new number and does not mutate the original value"]
447 #[inline(always)]
448 fn conversion(&self) -> Self::T
449 where
450 Self: Sized,
451 {
452 Self::coefficient()
453 }
454}
455
456/// Trait representing a [conversion factor][factor].
457///
458/// ## Generic Parameters
459/// * `V`: Underlying storage type trait is implemented for.
460///
461/// [factor]: https://jcgm.bipm.org/vim/en/1.24.html
462#[allow(unused_qualifications)] // lib:cmp::PartialOrder false positive.
463pub trait ConversionFactor<V>:
464 lib::cmp::PartialOrd
465 + lib::ops::Add<Self, Output = Self>
466 + lib::ops::Sub<Self, Output = Self>
467 + lib::ops::Mul<Self, Output = Self>
468 + lib::ops::Div<Self, Output = Self>
469 + crate::num::Zero
470 + crate::num::One
471{
472 /// Raises a `ConversionFactor<V>` to an integer power.
473 #[must_use = "method returns a new number and does not mutate the original value"]
474 fn powi(self, e: i32) -> Self;
475
476 /// Converts a `ConversionFactor<V>` into its underlying storage type.
477 #[must_use = "method returns a new number and does not mutate the original value"]
478 fn value(self) -> V;
479}
480
481/// Helper trait to identify the zero value of a type at compile time.
482///
483#[cfg_attr(all(feature = "si", feature = "f32"), doc = " ```rust")]
484#[cfg_attr(not(all(feature = "si", feature = "f32")), doc = " ```rust,ignore")]
485/// # use uom::si::f32::Length;
486/// use uom::ConstZero;
487///
488/// const ORIGIN: (Length, Length, Length) = (Length::ZERO, Length::ZERO, Length::ZERO);
489/// ```
490pub trait ConstZero {
491 /// Constant representing the zero value.
492 const ZERO: Self;
493}
494
495/// Default [kind][kind] of quantities to allow addition, subtraction, multiplication, division,
496/// remainder, negation, and saturating addition/subtraction.
497///
498/// [kind]: https://jcgm.bipm.org/vim/en/1.2.html
499pub trait Kind:
500 marker::Add
501 + marker::AddAssign
502 + marker::Sub
503 + marker::SubAssign
504 + marker::Mul
505 + marker::MulAssign
506 + marker::Div
507 + marker::DivAssign
508 + marker::Rem
509 + marker::RemAssign
510 + marker::Neg
511 + marker::Saturating
512{
513}
514
515storage_types! {
516 types: Float;
517
518 impl crate::Conversion<Self> for V {
519 type T = Self;
520
521 #[inline(always)]
522 fn constant(op: crate::ConstantOp) -> Self::T {
523 match op {
524 crate::ConstantOp::Add => -<Self::T as crate::num::Zero>::zero(),
525 crate::ConstantOp::Sub => <Self::T as crate::num::Zero>::zero(),
526 }
527 }
528
529 #[inline(always)]
530 fn conversion(&self) -> Self::T {
531 *self
532 }
533 }
534
535 impl crate::ConversionFactor<Self> for V {
536 #[inline(always)]
537 fn powi(self, e: i32) -> Self {
538 <Self as crate::num::Float>::powi(self, e)
539 }
540
541 #[inline(always)]
542 fn value(self) -> Self {
543 self
544 }
545 }
546
547 impl crate::ConstZero for V {
548 const ZERO: Self = 0.0;
549 }
550}
551
552storage_types! {
553 types: PrimInt;
554
555 impl crate::Conversion<V> for V {
556 type T = crate::num::rational::Ratio<V>;
557
558 #[inline(always)]
559 fn conversion(&self) -> Self::T {
560 (*self).into()
561 }
562 }
563
564 impl crate::ConversionFactor<V> for crate::num::rational::Ratio<V> {
565 #[inline(always)]
566 fn powi(self, e: i32) -> Self {
567 self.pow(e)
568 }
569
570 #[inline(always)]
571 fn value(self) -> V {
572 self.to_integer()
573 }
574 }
575
576 impl crate::ConstZero for V {
577 const ZERO: Self = 0;
578 }
579}
580
581storage_types! {
582 types: BigInt, BigUint;
583
584 impl crate::Conversion<V> for V {
585 type T = crate::num::rational::Ratio<V>;
586
587 #[inline(always)]
588 fn conversion(&self) -> Self::T {
589 self.clone().into()
590 }
591 }
592
593 impl crate::ConversionFactor<V> for crate::num::rational::Ratio<V> {
594 #[inline(always)]
595 fn powi(self, e: i32) -> Self {
596 match e.cmp(&0) {
597 crate::lib::cmp::Ordering::Equal => <Self as crate::num::One>::one(),
598 crate::lib::cmp::Ordering::Less => crate::num::pow::pow(self.recip(), (-e) as usize),
599 crate::lib::cmp::Ordering::Greater => crate::num::pow::pow(self, e as usize),
600 }
601 }
602
603 #[inline(always)]
604 fn value(self) -> V {
605 self.to_integer()
606 }
607 }
608}
609
610storage_types! {
611 types: Rational, Rational32, Rational64;
612
613 impl crate::Conversion<V> for V {
614 type T = V;
615
616 #[inline(always)]
617 fn conversion(&self) -> Self::T {
618 *self
619 }
620 }
621
622 impl crate::ConversionFactor<V> for V {
623 #[inline(always)]
624 fn powi(self, e: i32) -> Self {
625 self.pow(e)
626 }
627
628 #[inline(always)]
629 fn value(self) -> V {
630 self
631 }
632 }
633}
634
635storage_types! {
636 types: BigRational;
637
638 impl crate::Conversion<V> for V {
639 type T = V;
640
641 #[inline(always)]
642 fn conversion(&self) -> Self::T {
643 self.clone()
644 }
645 }
646
647 impl crate::ConversionFactor<V> for V {
648 #[inline(always)]
649 fn powi(self, e: i32) -> Self {
650 match e.cmp(&0) {
651 crate::lib::cmp::Ordering::Equal => <Self as crate::num::One>::one(),
652 crate::lib::cmp::Ordering::Less => crate::num::pow::pow(self.recip(), (-e) as usize),
653 crate::lib::cmp::Ordering::Greater => crate::num::pow::pow(self, e as usize),
654 }
655 }
656
657 #[inline(always)]
658 fn value(self) -> V {
659 self
660 }
661 }
662}
663
664storage_types! {
665 types: Complex;
666 impl crate::Conversion<V> for V {
667 type T = VV;
668
669 #[inline(always)]
670 fn constant(op: crate::ConstantOp) -> Self::T {
671 match op {
672 crate::ConstantOp::Add => -<Self::T as crate::num::Zero>::zero(),
673 crate::ConstantOp::Sub => <Self::T as crate::num::Zero>::zero(),
674 }
675 }
676
677 #[inline(always)]
678 fn conversion(&self) -> Self::T {
679 // Conversion factor is the norm of the number. Scaling with length again yields the
680 // same number.
681 self.norm()
682 }
683 }
684
685 impl crate::ConversionFactor<V> for VV {
686 #[inline(always)]
687 fn powi(self, e: i32) -> Self {
688 self.powi(e)
689 }
690
691 #[inline(always)]
692 fn value(self) -> V {
693 // Conversion by scaling (multiplication with only real number). Scaling a normalized
694 // number yields the original number again.
695 V::new(self, 0.0)
696 }
697 }
698}
699
700/// Utilities for formatting and printing quantities.
701pub mod fmt {
702 /// An enum to specify the display style to use.
703 #[derive(Clone, Copy, Debug)]
704 pub enum DisplayStyle {
705 /// Display the value and a unit abbreviation, e.g. "1.0 m", "327 s".
706 Abbreviation,
707
708 /// Display the value and full unit name (pluralized as appropriate),
709 /// e.g. "1 kilogram", "756 feet".
710 Description,
711 }
712}
713
714/// Unicode string slice manipulation for quantities.
715pub mod str {
716 use crate::lib::fmt::{self, Display, Formatter};
717
718 /// Represents an error encountered while parsing a string into a `Quantity`.
719 #[allow(missing_copy_implementations)]
720 #[derive(Clone, Debug, Eq, PartialEq)]
721 pub enum ParseQuantityError {
722 /// No separators (spaces) were encountered.
723 NoSeparator,
724
725 /// An error occurred while parsing the value (first) portion of the string.
726 ///
727 /// Due to exhaustiveness and type system limitations, this variant does not encode
728 /// the underlying parse error.
729 ValueParseError,
730
731 /// The unit used wasn't found for this quantity.
732 ///
733 /// ### Notes
734 /// For now, only abbreviations are supported, so this error may be encountered even if the
735 /// unit name (description) is correct.
736 UnknownUnit,
737 }
738
739 impl Display for ParseQuantityError {
740 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
741 use ParseQuantityError::{NoSeparator, UnknownUnit, ValueParseError};
742
743 match *self {
744 NoSeparator => write!(f, "no space between quantity and units"),
745 ValueParseError => write!(f, "error parsing unit quantity"),
746 UnknownUnit => write!(f, "unrecognized unit of measure"),
747 }
748 }
749 }
750
751 #[cfg(feature = "std")]
752 impl crate::lib::error::Error for ParseQuantityError {}
753}