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}