#[macro_export]
macro_rules! unit {
(
system: $system:path;
quantity: $quantity:path;
$($(#[$unit_attr:meta])* @$unit:ident: $coefficient:expr $(, $constant:expr)?;
$abbreviation:expr, $singular:expr, $plural:expr;)+
) => {
use $system as __system;
use $quantity as __quantity;
use __quantity::{Conversion, Unit};
unit_units!($($(#[$unit_attr])* @$unit: $coefficient $(, $constant)?;
$abbreviation, $singular, $plural;)+);
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! unit_units {
(
$($(#[$unit_attr:meta])* @$unit:ident: $coefficient:expr $(, $constant: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::eq_op)]
#[allow(clippy::approx_constant)]
fn coefficient() -> Self::T {
$coefficient
}
#[inline(always)]
#[allow(unused_variables)]
fn constant(op: $crate::ConstantOp) -> Self::T {
unit_constant!(op $($constant)?)
}
}
impl super::Conversion<V> for super::$unit {
test! {
#[inline(always)]
#[allow(clippy::eq_op)]
#[allow(clippy::approx_constant)]
fn is_valid() -> bool {
use $crate::num::ToPrimitive;
let r = Some($coefficient);
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)]
#[allow(clippy::eq_op)]
#[allow(clippy::approx_constant)]
fn coefficient() -> Self::T {
from_f64($coefficient)
}
#[inline(always)]
#[allow(unused_variables)]
fn constant(op: $crate::ConstantOp) -> Self::T {
from_f64(unit_constant!(op $($constant)?))
}
}
impl super::Conversion<V> for super::$unit {
test! {
#[inline(always)]
#[allow(clippy::eq_op)]
#[allow(clippy::approx_constant)]
fn is_valid() -> bool {
use $crate::num::{FromPrimitive, ToPrimitive};
if let Some(conversion) = Self::T::from_f64($coefficient) {
if conversion.numer() >= conversion.denom() {
if let Some(numer) = conversion.numer().to_f64() {
if let Some(denom) = conversion.denom().to_f64() {
let r = $coefficient;
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)]
#[allow(clippy::eq_op)]
#[allow(clippy::approx_constant)]
fn coefficient() -> Self::T {
from_f64($coefficient)
}
#[inline(always)]
#[allow(unused_variables)]
fn constant(op: $crate::ConstantOp) -> Self::T {
from_f64(unit_constant!(op $($constant)?))
}
}
impl super::Conversion<V> for super::$unit {
test! {
#[inline(always)]
#[allow(clippy::eq_op)]
#[allow(clippy::approx_constant)]
fn is_valid() -> bool {
use $crate::num::{FromPrimitive, ToPrimitive};
if let Some(conversion) = $crate::num::rational::Ratio::<$crate::num::BigInt>::from_f64($coefficient) {
if conversion.numer() >= conversion.denom() {
if let Some(numer) = conversion.numer().to_f64() {
if let Some(denom) = conversion.denom().to_f64() {
let r = $coefficient;
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)]
#[allow(clippy::eq_op)]
#[allow(clippy::approx_constant)]
fn coefficient() -> Self::T {
from_f64($coefficient)
}
#[inline(always)]
#[allow(unused_variables)]
fn constant(op: $crate::ConstantOp) -> Self::T {
from_f64(unit_constant!(op $($constant)?))
}
}
impl super::Conversion<V> for super::$unit {
test! {
#[inline(always)]
#[allow(clippy::eq_op)]
#[allow(clippy::approx_constant)]
fn is_valid() -> bool {
use $crate::num::{FromPrimitive, ToPrimitive};
if let Some(conversion) = Self::T::from_f64($coefficient) {
if conversion.numer() >= conversion.denom() {
if let Some(numer) = conversion.numer().to_f64() {
if let Some(denom) = conversion.denom().to_f64() {
let r = $coefficient;
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::eq_op)]
#[allow(clippy::approx_constant)]
fn coefficient() -> Self::T {
$coefficient
}
#[inline(always)]
#[allow(unused_variables)]
fn constant(op: $crate::ConstantOp) -> Self::T {
unit_constant!(op $($constant)?)
}
}
impl super::Conversion<V> for super::$unit {
test! {
#[inline(always)]
#[allow(clippy::eq_op)]
#[allow(clippy::approx_constant)]
fn is_valid() -> bool {
use $crate::num::ToPrimitive;
let r = Some($coefficient);
let c = <Self as $crate::Conversion<V>>::coefficient().to_f64();
r == c
}}
})+
}
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! unit_unit {
($(#[$unit_attr:meta])+ @$unit:ident $plural:expr) => {
$(#[$unit_attr])*
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, Debug, Hash)]
pub struct $unit;
};
(@$unit:ident $plural:expr) => {
#[doc = $plural]
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, Debug, Hash)]
pub struct $unit;
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! unit_constant {
($op:ident $const:expr) => {
$const
};
($op:ident) => {
match $op {
$crate::ConstantOp::Add => -0.0,
$crate::ConstantOp::Sub => 0.0,
}
};
}