#![doc = include_str ! ("../README.md")]
#![cfg_attr(not(feature = "std"), no_std)]
#![deny(non_ascii_idents)]
#![deny(unsafe_code)]
#![warn(missing_debug_implementations)]
#![warn(missing_docs)]
#![warn(trivial_casts)]
#![warn(unused)]
#![allow(dead_code)]
#![warn(clippy::cast_possible_truncation)]
#![warn(clippy::cast_possible_wrap)]
#![warn(clippy::cast_precision_loss)]
#![warn(clippy::cast_sign_loss)]
#![warn(clippy::cognitive_complexity)]
#![warn(clippy::decimal_literal_representation)]
#![warn(clippy::enum_glob_use)]
#![warn(clippy::equatable_if_let)]
#![warn(clippy::fallible_impl_from)]
#![warn(clippy::if_not_else)]
#![warn(clippy::if_then_some_else_none)]
#![warn(clippy::implicit_clone)]
#![warn(clippy::integer_division)]
#![warn(clippy::manual_assert)]
#![warn(clippy::match_same_arms)]
#![warn(clippy::missing_const_for_fn)]
#![warn(clippy::missing_errors_doc)]
#![warn(clippy::missing_panics_doc)]
#![warn(clippy::multiple_crate_versions)]
#![warn(clippy::multiple_inherent_impl)]
#![warn(clippy::must_use_candidate)]
#![warn(clippy::needless_pass_by_value)]
#![warn(clippy::print_stderr)]
#![warn(clippy::print_stdout)]
#![warn(clippy::semicolon_if_nothing_returned)]
#![warn(clippy::str_to_string)]
#![warn(clippy::string_to_string)]
#![warn(clippy::undocumented_unsafe_blocks)]
#![warn(clippy::unicode_not_nfc)]
#![warn(clippy::unimplemented)]
#![warn(clippy::unseparated_literal_suffix)]
#![warn(clippy::unused_self)]
#![warn(clippy::unwrap_in_result)]
#![warn(clippy::use_self)]
#![warn(clippy::used_underscore_binding)]
#![warn(clippy::wildcard_imports)]
extern crate alloc;
use alloc::{format, string::String};
use core::{
cmp::Ordering,
fmt,
ops::{Add, Div, Mul, Sub},
};
#[cfg(feature = "fpdec")]
pub use amnt_dec::{AmountT, Dec, Decimal, AMNT_ONE, AMNT_ZERO};
#[cfg(all(not(feature = "fpdec"), target_pointer_width = "32"))]
pub use amnt_f32::{AmountT, AMNT_ONE, AMNT_ZERO};
#[cfg(all(not(feature = "fpdec"), target_pointer_width = "64"))]
pub use amnt_f64::{AmountT, AMNT_ONE, AMNT_ZERO};
pub use converter::{ConversionTable, Converter};
pub use rate::Rate;
pub use si_prefixes::SIPrefix;
mod converter;
pub mod prelude;
mod rate;
mod si_prefixes;
#[cfg(feature = "fpdec")]
#[doc(hidden)]
pub mod amnt_dec;
#[cfg(all(not(feature = "fpdec"), target_pointer_width = "32"))]
#[doc(hidden)]
pub mod amnt_f32;
#[cfg(all(not(feature = "fpdec"), target_pointer_width = "64"))]
#[doc(hidden)]
pub mod amnt_f64;
#[cfg(feature = "acceleration")]
pub mod acceleration;
#[cfg(feature = "area")]
pub mod area;
#[cfg(feature = "datathroughput")]
pub mod datathroughput;
#[cfg(feature = "datavolume")]
pub mod datavolume;
#[cfg(feature = "duration")]
pub mod duration;
#[cfg(feature = "energy")]
pub mod energy;
#[cfg(feature = "force")]
pub mod force;
#[cfg(feature = "frequency")]
pub mod frequency;
#[cfg(feature = "length")]
pub mod length;
#[cfg(feature = "mass")]
pub mod mass;
#[cfg(feature = "power")]
pub mod power;
#[cfg(feature = "speed")]
pub mod speed;
#[cfg(feature = "temperature")]
pub mod temperature;
#[cfg(feature = "volume")]
pub mod volume;
pub trait Unit:
Copy + Eq + PartialEq + Sized + Mul<AmountT> + fmt::Display
{
type QuantityType: Quantity<UnitType = Self>;
fn iter<'a>() -> core::slice::Iter<'a, Self>;
#[must_use]
fn from_symbol(symbol: &str) -> Option<Self> {
for unit in Self::iter() {
if unit.symbol() == symbol {
return Some(*unit);
}
}
None
}
fn name(&self) -> &'static str;
fn symbol(&self) -> &'static str;
fn si_prefix(&self) -> Option<SIPrefix>;
fn as_qty(&self) -> Self::QuantityType {
Self::QuantityType::new(AMNT_ONE, *self)
}
fn fmt(&self, form: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self.symbol(), form)
}
}
pub trait LinearScaledUnit: Unit {
const REF_UNIT: Self;
#[must_use]
fn from_scale(amnt: AmountT) -> Option<Self> {
for unit in Self::iter() {
if unit.scale() == amnt {
return Some(*unit);
}
}
None
}
#[inline(always)]
fn is_ref_unit(&self) -> bool {
*self == Self::REF_UNIT
}
fn scale(&self) -> AmountT;
#[inline(always)]
fn ratio(&self, other: &Self) -> AmountT {
self.scale() / other.scale()
}
}
pub trait Quantity: Copy + Sized + Mul<AmountT> {
type UnitType: Unit<QuantityType = Self>;
fn iter_units<'a>() -> core::slice::Iter<'a, Self::UnitType> {
Self::UnitType::iter()
}
#[must_use]
fn unit_from_symbol(symbol: &str) -> Option<Self::UnitType> {
for unit in Self::iter_units() {
if unit.symbol() == symbol {
return Some(*unit);
}
}
None
}
fn new(amount: AmountT, unit: Self::UnitType) -> Self;
fn amount(&self) -> AmountT;
fn unit(&self) -> Self::UnitType;
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
self.unit() == other.unit() && self.amount() == other.amount()
}
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
if self.unit() == other.unit() {
PartialOrd::partial_cmp(&self.amount(), &other.amount())
} else {
None
}
}
fn add(self, rhs: Self) -> Self {
if self.unit() == rhs.unit() {
return Self::new(self.amount() + rhs.amount(), self.unit());
}
panic!(
"Can't add '{}' and '{}'.",
self.unit().symbol(),
rhs.unit().symbol()
);
}
fn sub(self, rhs: Self) -> Self {
if self.unit() == rhs.unit() {
return Self::new(self.amount() - rhs.amount(), self.unit());
}
panic!(
"Can't subtract '{}' and '{}'.",
self.unit().symbol(),
rhs.unit().symbol(),
);
}
fn div(self, rhs: Self) -> AmountT {
if self.unit() == rhs.unit() {
return self.amount() / rhs.amount();
}
panic!(
"Can't divide '{}' and '{}'.",
self.unit().symbol(),
rhs.unit().symbol()
);
}
fn fmt(&self, form: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.unit().symbol() {
"" => fmt::Display::fmt(&self.amount(), form),
_ => {
let tmp: String;
let amnt_non_neg = self.amount() >= AMNT_ZERO;
#[cfg(feature = "fpdec")]
let abs_amnt = self.amount().abs();
#[cfg(not(feature = "fpdec"))]
let abs_amnt = if amnt_non_neg {
self.amount()
} else {
-self.amount()
};
if let Some(prec) = form.precision() {
tmp = format!("{:.*} {}", prec, abs_amnt, self.unit());
} else {
tmp = format!("{} {}", abs_amnt, self.unit());
}
form.pad_integral(amnt_non_neg, "", &tmp)
}
}
}
}
pub trait HasRefUnit: Quantity + Add<Self> + Sub<Self> + Div<Self>
where
<Self as Quantity>::UnitType: LinearScaledUnit,
{
const REF_UNIT: <Self as Quantity>::UnitType;
#[must_use]
fn unit_from_scale(amnt: AmountT) -> Option<Self::UnitType> {
for unit in Self::iter_units() {
if unit.scale() == amnt {
return Some(*unit);
}
}
None
}
#[inline(always)]
fn equiv_amount(&self, unit: Self::UnitType) -> AmountT {
if self.unit() == unit {
self.amount()
} else {
self.unit().ratio(&unit) * self.amount()
}
}
fn convert(&self, to_unit: Self::UnitType) -> Self {
Self::new(self.equiv_amount(to_unit), to_unit)
}
#[inline(always)]
fn eq(&self, other: &Self) -> bool {
self.amount() == other.equiv_amount(self.unit())
}
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
if self.unit() == other.unit() {
PartialOrd::partial_cmp(&self.amount(), &other.amount())
} else {
PartialOrd::partial_cmp(
&self.amount(),
&other.equiv_amount(self.unit()),
)
}
}
#[inline]
fn add(self, rhs: Self) -> Self {
Self::new(self.amount() + rhs.equiv_amount(self.unit()), self.unit())
}
#[inline]
fn sub(self, rhs: Self) -> Self {
Self::new(self.amount() - rhs.equiv_amount(self.unit()), self.unit())
}
#[inline]
fn div(self, rhs: Self) -> AmountT {
self.amount() / rhs.equiv_amount(self.unit())
}
#[doc(hidden)]
#[must_use]
fn _fit(amount: AmountT) -> Self {
let take_all = Self::REF_UNIT.si_prefix().is_none();
let mut it =
Self::iter_units().filter(|u| take_all || u.si_prefix().is_some());
let first = it.next().unwrap();
let last = it
.filter(|u| u.scale() > first.scale() && u.scale() <= amount)
.last();
match last {
Some(unit) => Self::new(amount / unit.scale(), *unit),
None => Self::new(amount / first.scale(), *first),
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum One {
One,
}
impl One {
const VARIANTS: [Self; 1] = [ONE];
}
pub const ONE: One = One::One;
impl Unit for One {
type QuantityType = AmountT;
fn iter<'a>() -> core::slice::Iter<'a, Self> {
Self::VARIANTS.iter()
}
fn name(&self) -> &'static str {
"One"
}
fn symbol(&self) -> &'static str {
""
}
fn si_prefix(&self) -> Option<SIPrefix> {
None
}
}
impl fmt::Display for One {
#[inline(always)]
fn fmt(&self, form: &mut fmt::Formatter<'_>) -> fmt::Result {
<Self as Unit>::fmt(self, form)
}
}
impl LinearScaledUnit for One {
const REF_UNIT: Self = ONE;
fn scale(&self) -> AmountT {
AMNT_ONE
}
}
impl Mul<One> for AmountT {
type Output = Self;
#[inline(always)]
fn mul(self, _rhs: One) -> Self::Output {
self
}
}
impl Mul<AmountT> for One {
type Output = AmountT;
#[inline(always)]
fn mul(self, rhs: AmountT) -> Self::Output {
rhs
}
}
impl Quantity for AmountT {
type UnitType = One;
#[inline(always)]
fn new(amount: AmountT, _unit: Self::UnitType) -> Self {
amount
}
#[inline(always)]
fn amount(&self) -> AmountT {
*self
}
#[inline(always)]
fn unit(&self) -> Self::UnitType {
ONE
}
}
impl HasRefUnit for AmountT {
const REF_UNIT: One = ONE;
#[inline(always)]
fn _fit(amount: AmountT) -> Self {
amount
}
}