#[macro_export]
macro_rules! impl_unit_from_conversions {
($unit:ty) => {};
($first:ty, $($rest:ty),+ $(,)?) => {
$(
impl<S: $crate::scalar::Real> From<$crate::Quantity<$first, S>> for $crate::Quantity<$rest, S> {
fn from(value: $crate::Quantity<$first, S>) -> Self {
value.to::<$rest>()
}
}
impl<S: $crate::scalar::Real> From<$crate::Quantity<$rest, S>> for $crate::Quantity<$first, S> {
fn from(value: $crate::Quantity<$rest, S>) -> Self {
value.to::<$first>()
}
}
)+
$crate::impl_unit_from_conversions!($($rest),+);
};
}
#[macro_export]
macro_rules! impl_unit_cross_unit_ops {
($unit:ty) => {};
($first:ty, $($rest:ty),+ $(,)?) => {
$(
impl<S: $crate::scalar::Real> PartialEq<$crate::Quantity<$rest, S>> for $crate::Quantity<$first, S> {
#[inline]
fn eq(&self, other: &$crate::Quantity<$rest, S>) -> bool {
const R_FIRST: f64 = <$first as $crate::Unit>::RATIO;
const R_REST: f64 = <$rest as $crate::Unit>::RATIO;
if R_FIRST >= R_REST {
self.value() == other.value() * S::from_f64(R_REST / R_FIRST)
} else {
self.value() * S::from_f64(R_FIRST / R_REST) == other.value()
}
}
}
impl<S: $crate::scalar::Real> PartialEq<$crate::Quantity<$first, S>> for $crate::Quantity<$rest, S> {
#[inline]
fn eq(&self, other: &$crate::Quantity<$first, S>) -> bool {
const R_FIRST: f64 = <$first as $crate::Unit>::RATIO;
const R_REST: f64 = <$rest as $crate::Unit>::RATIO;
if R_REST >= R_FIRST {
self.value() == other.value() * S::from_f64(R_FIRST / R_REST)
} else {
self.value() * S::from_f64(R_REST / R_FIRST) == other.value()
}
}
}
impl<S: $crate::scalar::Real> PartialOrd<$crate::Quantity<$rest, S>> for $crate::Quantity<$first, S> {
#[inline]
fn partial_cmp(&self, other: &$crate::Quantity<$rest, S>) -> Option<core::cmp::Ordering> {
const R_FIRST: f64 = <$first as $crate::Unit>::RATIO;
const R_REST: f64 = <$rest as $crate::Unit>::RATIO;
if R_FIRST >= R_REST {
self.value().partial_cmp(&(other.value() * S::from_f64(R_REST / R_FIRST)))
} else {
(self.value() * S::from_f64(R_FIRST / R_REST)).partial_cmp(&other.value())
}
}
}
impl<S: $crate::scalar::Real> PartialOrd<$crate::Quantity<$first, S>> for $crate::Quantity<$rest, S> {
#[inline]
fn partial_cmp(&self, other: &$crate::Quantity<$first, S>) -> Option<core::cmp::Ordering> {
const R_FIRST: f64 = <$first as $crate::Unit>::RATIO;
const R_REST: f64 = <$rest as $crate::Unit>::RATIO;
if R_REST >= R_FIRST {
self.value().partial_cmp(&(other.value() * S::from_f64(R_FIRST / R_REST)))
} else {
(self.value() * S::from_f64(R_REST / R_FIRST)).partial_cmp(&other.value())
}
}
}
)+
$crate::impl_unit_cross_unit_ops!($($rest),+);
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! assert_units_are_builtin {
($($unit:ty),+ $(,)?) => {
const _: () = {
trait _AssertBuiltin: $crate::unit_arithmetic::BuiltinUnit {}
$(impl _AssertBuiltin for $unit {})+
};
};
}
#[macro_export]
macro_rules! impl_unit_conversions {
($($unit:ty),+ $(,)?) => {
$crate::impl_unit_from_conversions!($($unit),+);
$crate::impl_unit_cross_unit_ops!($($unit),+);
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __impl_from_one_to_many {
($one:ty; $($base:ty),+ $(,)?) => {
$(
impl<S: $crate::scalar::Real> From<$crate::Quantity<$one, S>> for $crate::Quantity<$base, S> {
fn from(value: $crate::Quantity<$one, S>) -> Self {
value.to::<$base>()
}
}
impl<S: $crate::scalar::Real> From<$crate::Quantity<$base, S>> for $crate::Quantity<$one, S> {
fn from(value: $crate::Quantity<$base, S>) -> Self {
value.to::<$one>()
}
}
)+
};
}
#[macro_export]
macro_rules! impl_unit_from_conversions_between {
($($base:ty),+; $($extra:ty),+ $(,)?) => {
$crate::__impl_from_each_extra_to_bases!({$($base),+} $($extra),+);
$crate::impl_unit_from_conversions!($($extra),+);
};
($($base:ty),+; $extra:ty $(,)?) => {
$crate::__impl_from_one_to_many!($extra; $($base),+);
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __impl_from_each_extra_to_bases {
({$($base:ty),+} $extra:ty) => {
$crate::__impl_from_one_to_many!($extra; $($base),+);
};
({$($base:ty),+} $first:ty, $($rest:ty),+) => {
$crate::__impl_from_one_to_many!($first; $($base),+);
$crate::__impl_from_each_extra_to_bases!({$($base),+} $($rest),+);
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __impl_cross_ops_one_to_many {
($one:ty; $($base:ty),+ $(,)?) => {
$(
impl<S: $crate::scalar::Real> PartialEq<$crate::Quantity<$base, S>> for $crate::Quantity<$one, S> {
#[inline]
fn eq(&self, other: &$crate::Quantity<$base, S>) -> bool {
const R_ONE: f64 = <$one as $crate::Unit>::RATIO;
const R_BASE: f64 = <$base as $crate::Unit>::RATIO;
if R_ONE >= R_BASE {
self.value() == other.value() * S::from_f64(R_BASE / R_ONE)
} else {
self.value() * S::from_f64(R_ONE / R_BASE) == other.value()
}
}
}
impl<S: $crate::scalar::Real> PartialEq<$crate::Quantity<$one, S>> for $crate::Quantity<$base, S> {
#[inline]
fn eq(&self, other: &$crate::Quantity<$one, S>) -> bool {
const R_ONE: f64 = <$one as $crate::Unit>::RATIO;
const R_BASE: f64 = <$base as $crate::Unit>::RATIO;
if R_BASE >= R_ONE {
self.value() == other.value() * S::from_f64(R_ONE / R_BASE)
} else {
self.value() * S::from_f64(R_BASE / R_ONE) == other.value()
}
}
}
impl<S: $crate::scalar::Real> PartialOrd<$crate::Quantity<$base, S>> for $crate::Quantity<$one, S> {
#[inline]
fn partial_cmp(&self, other: &$crate::Quantity<$base, S>) -> Option<core::cmp::Ordering> {
const R_ONE: f64 = <$one as $crate::Unit>::RATIO;
const R_BASE: f64 = <$base as $crate::Unit>::RATIO;
if R_ONE >= R_BASE {
self.value().partial_cmp(&(other.value() * S::from_f64(R_BASE / R_ONE)))
} else {
(self.value() * S::from_f64(R_ONE / R_BASE)).partial_cmp(&other.value())
}
}
}
impl<S: $crate::scalar::Real> PartialOrd<$crate::Quantity<$one, S>> for $crate::Quantity<$base, S> {
#[inline]
fn partial_cmp(&self, other: &$crate::Quantity<$one, S>) -> Option<core::cmp::Ordering> {
const R_ONE: f64 = <$one as $crate::Unit>::RATIO;
const R_BASE: f64 = <$base as $crate::Unit>::RATIO;
if R_BASE >= R_ONE {
self.value().partial_cmp(&(other.value() * S::from_f64(R_ONE / R_BASE)))
} else {
(self.value() * S::from_f64(R_BASE / R_ONE)).partial_cmp(&other.value())
}
}
}
)+
};
}
#[macro_export]
macro_rules! impl_unit_cross_unit_ops_between {
($($base:ty),+; $($extra:ty),+ $(,)?) => {
$crate::__impl_cross_ops_each_extra_to_bases!({$($base),+} $($extra),+);
$crate::impl_unit_cross_unit_ops!($($extra),+);
};
($($base:ty),+; $extra:ty $(,)?) => {
$crate::__impl_cross_ops_one_to_many!($extra; $($base),+);
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __impl_cross_ops_each_extra_to_bases {
({$($base:ty),+} $extra:ty) => {
$crate::__impl_cross_ops_one_to_many!($extra; $($base),+);
};
({$($base:ty),+} $first:ty, $($rest:ty),+) => {
$crate::__impl_cross_ops_one_to_many!($first; $($base),+);
$crate::__impl_cross_ops_each_extra_to_bases!({$($base),+} $($rest),+);
};
}
#[cfg(test)]
mod tests {
use crate::{Length, Quantity, Unit};
use core::cmp::Ordering;
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
struct LargeTestUnit;
impl Unit for LargeTestUnit {
const RATIO: f64 = 1_000.0;
type Dim = Length;
const SYMBOL: &'static str = "ltu";
}
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
struct SmallTestUnit;
impl Unit for SmallTestUnit {
const RATIO: f64 = 1.0;
type Dim = Length;
const SYMBOL: &'static str = "stu";
}
crate::impl_unit_cross_unit_ops!(LargeTestUnit, SmallTestUnit);
type LargeQuantity = Quantity<LargeTestUnit>;
type SmallQuantity = Quantity<SmallTestUnit>;
#[test]
fn cross_unit_ops_cover_larger_first_branches() {
let large = LargeQuantity::new(1.0);
let equal_small = SmallQuantity::new(1_000.0);
let smaller_small = SmallQuantity::new(500.0);
assert_eq!(large, equal_small);
assert_eq!(equal_small, large);
assert_eq!(large.partial_cmp(&smaller_small), Some(Ordering::Greater));
assert_eq!(smaller_small.partial_cmp(&large), Some(Ordering::Less));
}
}