#[macro_export]
macro_rules! unit {
(
system: $system:path;
quantity: $quantity:path;
$($(#[$unit_attr:meta])* @$unit:ident: $($conversion:expr),+;
$abbreviation:expr, $singular:expr, $plural:expr;)+
) => {
use $system as __system;
use $quantity as __quantity;
use __quantity::{Conversion, Unit};
unit!(@units $($(#[$unit_attr])* @$unit: $($conversion),+;
$abbreviation, $singular, $plural;)+);
};
(
@units $($(#[$unit_attr:meta])* @$unit:ident: $($conversion:expr),+;
$abbreviation:expr, $singular:expr, $plural:expr;)+
) => {
$(unit!(@unit $(#[$unit_attr])* @$unit $plural);
impl __system::Unit for $unit {
#[inline(always)]
fn abbreviation() -> &'static str {
$abbreviation
}
#[inline(always)]
fn singular() -> &'static str {
$singular
}
#[inline(always)]
fn plural() -> &'static str {
$plural
}
}
impl Unit for $unit {})+
storage_types! {
types: Float;
$(impl $crate::Conversion<V> for super::$unit {
type T = V;
#[inline(always)]
#[allow(clippy::inconsistent_digit_grouping)]
fn coefficient() -> Self::T {
unit!(@coefficient $($conversion),+)
}
#[inline(always)]
#[allow(unused_variables)]
#[allow(clippy::inconsistent_digit_grouping)]
fn constant(op: $crate::ConstantOp) -> Self::T {
unit!(@constant op $($conversion),+)
}
}
impl super::Conversion<V> for super::$unit {
#[cfg(test)]
#[inline(always)]
fn is_valid() -> bool {
use $crate::num::ToPrimitive;
let r = Some(unit!(@coefficient $($conversion),+));
let c = <Self as $crate::Conversion<V>>::coefficient().to_f64();
r == c
}
})+
}
storage_types! {
types: PrimInt, BigInt;
pub type T = $crate::num::rational::Ratio<V>;
#[inline(always)]
fn from_f64(value: f64) -> T {
<T as $crate::num::FromPrimitive>::from_f64(value).unwrap()
}
$(impl $crate::Conversion<V> for super::$unit {
type T = T;
#[inline(always)]
fn coefficient() -> Self::T {
from_f64(unit!(@coefficient $($conversion),+))
}
#[inline(always)]
#[allow(unused_variables)]
fn constant(op: $crate::ConstantOp) -> Self::T {
from_f64(unit!(@constant op $($conversion),+))
}
}
impl super::Conversion<V> for super::$unit {
#[cfg(test)]
#[inline(always)]
fn is_valid() -> bool {
use $crate::num::{FromPrimitive, ToPrimitive};
if let Some(conversion) = Self::T::from_f64(unit!(@coefficient $($conversion),+)) {
if conversion.numer() >= conversion.denom() {
if let Some(numer) = conversion.numer().to_f64() {
if let Some(denom) = conversion.denom().to_f64() {
let r = { unit!(@coefficient $($conversion),+) };
let c = numer / denom;
return r == c
}
}
}
}
false
}
})+
}
storage_types! {
types: BigUint;
pub type T = $crate::num::rational::Ratio<V>;
#[inline(always)]
fn from_f64(value: f64) -> T {
use $crate::num::FromPrimitive;
let c = $crate::num::rational::Ratio::<$crate::num::BigInt>::from_f64(value)
.unwrap();
T::new(c.numer().to_biguint().unwrap(), c.denom().to_biguint().unwrap())
}
$(impl $crate::Conversion<V> for super::$unit {
type T = T;
#[inline(always)]
fn coefficient() -> Self::T {
from_f64(unit!(@coefficient $($conversion),+))
}
#[inline(always)]
#[allow(unused_variables)]
fn constant(op: $crate::ConstantOp) -> Self::T {
from_f64(unit!(@constant op $($conversion),+))
}
}
impl super::Conversion<V> for super::$unit {
#[cfg(test)]
#[inline(always)]
fn is_valid() -> bool {
use $crate::num::{FromPrimitive, ToPrimitive};
if let Some(conversion) = $crate::num::rational::Ratio::<$crate::num::BigInt>::from_f64(unit!(@coefficient $($conversion),+)) {
if conversion.numer() >= conversion.denom() {
if let Some(numer) = conversion.numer().to_f64() {
if let Some(denom) = conversion.denom().to_f64() {
let r = { unit!(@coefficient $($conversion),+) };
let c = numer / denom;
return r == c
}
}
}
}
false
}
})+
}
storage_types! {
types: Ratio;
#[inline(always)]
fn from_f64(value: f64) -> V {
<V as $crate::num::FromPrimitive>::from_f64(value).unwrap()
}
$(impl $crate::Conversion<V> for super::$unit {
type T = V;
#[inline(always)]
fn coefficient() -> Self::T {
from_f64(unit!(@coefficient $($conversion),+))
}
#[inline(always)]
#[allow(unused_variables)]
fn constant(op: $crate::ConstantOp) -> Self::T {
from_f64(unit!(@constant op $($conversion),+))
}
}
impl super::Conversion<V> for super::$unit {
#[cfg(test)]
#[inline(always)]
fn is_valid() -> bool {
use $crate::num::{FromPrimitive, ToPrimitive};
if let Some(conversion) = Self::T::from_f64(unit!(@coefficient $($conversion),+)) {
if conversion.numer() >= conversion.denom() {
if let Some(numer) = conversion.numer().to_f64() {
if let Some(denom) = conversion.denom().to_f64() {
let r = { unit!(@coefficient $($conversion),+) };
let c = numer / denom;
return r == c
}
}
}
}
false
}
})+
}
storage_types! {
types: Complex;
$(impl $crate::Conversion<V> for super::$unit {
type T = VV;
#[inline(always)]
#[allow(clippy::inconsistent_digit_grouping)]
fn coefficient() -> Self::T {
unit!(@coefficient $($conversion),+)
}
#[inline(always)]
#[allow(unused_variables)]
#[allow(clippy::inconsistent_digit_grouping)]
fn constant(op: $crate::ConstantOp) -> Self::T {
unit!(@constant op $($conversion),+)
}
}
impl super::Conversion<V> for super::$unit {
#[cfg(test)]
#[inline(always)]
fn is_valid() -> bool {
use $crate::num::ToPrimitive;
let r = Some(unit!(@coefficient $($conversion),+));
let c = <Self as $crate::Conversion<V>>::coefficient().to_f64();
r == c
}
})+
}
};
(@unit $(#[$unit_attr:meta])+ @$unit:ident $plural:expr) => {
$(#[$unit_attr])*
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, Debug, Hash)]
pub struct $unit;
};
(@unit @$unit:ident $plural:expr) => {
#[doc = $plural]
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, Debug, Hash)]
pub struct $unit;
};
(@coefficient $factor:expr, $const:expr) => { #[allow(clippy::eq_op)] {$factor} };
(@coefficient $factor:expr) => { #[allow(clippy::eq_op)] {$factor} };
(@constant $op:ident $factor:expr, $const:expr) => { $const };
(@constant $op:ident $factor:expr) => {
match $op {
$crate::ConstantOp::Add => -0.0,
$crate::ConstantOp::Sub => 0.0,
}
};
}