#[macro_export]
macro_rules! quantity {
(
$(#[$quantity_attr:meta])* quantity: $quantity:ident; $description:expr;
$(#[$dim_attr:meta])* dimension: $system:ident<$($dimension:ident),+>;
units {
$($(#[$unit_attr:meta])* @$unit:ident: $($conversion:expr),+;
$abbreviation:expr, $singular:expr, $plural:expr;)+
}
) => {
quantity! {
$(#[$quantity_attr])* quantity: $quantity; $description;
$(#[$dim_attr])* dimension: $system<$($dimension),+>;
kind: dyn $crate::Kind;
units {
$($(#[$unit_attr])* @$unit: $($conversion),+; $abbreviation, $singular, $plural;)+
}
}
};
(
$(#[$quantity_attr:meta])* quantity: $quantity:ident; $description:expr;
$(#[$dim_attr:meta])* dimension: $system:ident<$($dimension:ident),+>;
kind: $kind:ty;
units {
$($(#[$unit_attr:meta])* @$unit:ident: $($conversion:expr),+; $abbreviation:expr,
$singular:expr, $plural:expr;)+
}
) => {
$(#[$dim_attr])*
pub type Dimension = super::$system<$($crate::typenum::$dimension),+, $kind>;
$(#[$quantity_attr])*
pub type $quantity<U, V> = super::Quantity<Dimension, U, V>;
pub trait Unit: super::Unit {}
pub trait Conversion<V>: Unit + $crate::Conversion<V, T = <V as $crate::Conversion<V>>::T>
where
V: $crate::Conversion<V>,
{
}
$(quantity!(@unit $(#[$unit_attr])* @$unit);
impl super::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)]
fn coefficient() -> Self::T {
quantity!(@coefficient $($conversion),+)
}
#[inline(always)]
#[allow(unused_variables)]
fn constant(op: $crate::ConstantOp) -> Self::T {
quantity!(@constant op $($conversion),+)
}
}
impl super::Conversion<V> for super::$unit {})+
}
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(quantity!(@coefficient $($conversion),+))
}
#[inline(always)]
#[allow(unused_variables)]
fn constant(op: $crate::ConstantOp) -> Self::T {
from_f64(quantity!(@constant op $($conversion),+))
}
}
impl super::Conversion<V> for super::$unit {})+
}
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(quantity!(@coefficient $($conversion),+))
}
#[inline(always)]
#[allow(unused_variables)]
fn constant(op: $crate::ConstantOp) -> Self::T {
from_f64(quantity!(@constant op $($conversion),+))
}
}
impl super::Conversion<V> for super::$unit {})+
}
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(quantity!(@coefficient $($conversion),+))
}
#[inline(always)]
#[allow(unused_variables)]
fn constant(op: $crate::ConstantOp) -> Self::T {
from_f64(quantity!(@constant op $($conversion),+))
}
}
impl super::Conversion<V> for super::$unit {})+
}
#[allow(dead_code)]
#[inline(always)]
pub fn description() -> &'static str {
$description
}
impl<U, V> $quantity<U, V>
where
U: super::Units<V> + ?Sized,
V: $crate::num::Num + $crate::Conversion<V>,
{
#[inline(always)]
pub fn new<N>(v: V) -> Self
where
N: Unit + $crate::Conversion<V, T = V::T>,
{
$quantity {
dimension: $crate::lib::marker::PhantomData,
units: $crate::lib::marker::PhantomData,
value: super::to_base::<Dimension, U, V, N>(&v),
}
}
#[inline(always)]
pub fn get<N>(&self) -> V
where
N: Unit + $crate::Conversion<V, T = V::T>,
{
super::from_base::<Dimension, U, V, N>(&self.value)
}
#[inline(always)]
pub fn floor<N>(self) -> Self
where
V: $crate::num::Float,
N: Unit + $crate::Conversion<V, T = V::T>,
{
Self::new::<N>(self.get::<N>().floor())
}
#[inline(always)]
pub fn ceil<N>(self) -> Self
where
V: $crate::num::Float,
N: Unit + $crate::Conversion<V, T = V::T>,
{
Self::new::<N>(self.get::<N>().ceil())
}
#[inline(always)]
pub fn round<N>(self) -> Self
where
V: $crate::num::Float,
N: Unit + $crate::Conversion<V, T = V::T>,
{
Self::new::<N>(self.get::<N>().round())
}
#[inline(always)]
pub fn trunc<N>(self) -> Self
where
V: $crate::num::Float,
N: Unit + $crate::Conversion<V, T = V::T>,
{
Self::new::<N>(self.get::<N>().trunc())
}
#[inline(always)]
pub fn fract<N>(self) -> Self
where
V: $crate::num::Float,
N: Unit + $crate::Conversion<V, T = V::T>,
{
Self::new::<N>(self.get::<N>().fract())
}
#[cfg_attr(all(feature = "si", feature = "f32"), doc = " ```rust")]
#[cfg_attr(not(all(feature = "si", feature = "f32")), doc = " ```rust,ignore")]
pub fn format_args<N>(
unit: N,
style: $crate::fmt::DisplayStyle
) -> super::fmt::Arguments<Dimension, N>
where
N: Unit
{
super::fmt::Arguments {
dimension: $crate::lib::marker::PhantomData,
unit: unit,
style: style,
}
}
#[cfg_attr(all(feature = "si", feature = "f32"), doc = " ```rust")]
#[cfg_attr(not(all(feature = "si", feature = "f32")), doc = " ```rust,ignore")]
pub fn into_format_args<N>(
self,
unit: N,
style: $crate::fmt::DisplayStyle
) -> super::fmt::QuantityArguments<Dimension, U, V, N>
where
N: Unit
{
super::fmt::QuantityArguments {
arguments: super::fmt::Arguments {
dimension: $crate::lib::marker::PhantomData,
unit: unit,
style: style,
},
quantity: self,
}
}
}
impl<N> super::fmt::Arguments<Dimension, N>
where
N: super::Unit + Unit,
{
pub fn with<U, V>(
self,
quantity: $quantity<U, V>
) -> super::fmt::QuantityArguments<Dimension, U, V, N>
where
U: super::Units<V> + ?Sized,
V: $crate::num::Num + $crate::Conversion<V>,
{
super::fmt::QuantityArguments {
arguments: self,
quantity: quantity,
}
}
}
mod str {
storage_types! {
use $crate::lib::str::FromStr;
use $crate::str::ParseQuantityError::*;
impl<U> FromStr for super::super::$quantity<U, V>
where
U: super::super::super::Units<V> + ?Sized,
{
type Err = $crate::str::ParseQuantityError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut parts = s.splitn(2, ' ');
let value = parts.next().unwrap();
let abbr = parts.next().ok_or(NoSeparator)?;
let value = value.parse::<V>().map_err(|_| ValueParseError)?;
match abbr.trim() {
$($abbreviation => Ok(Self::new::<super::super::$unit>(value)),)+
_ => Err(UnknownUnit),
}
}
}
}
}
};
(@unit $(#[$unit_attr:meta])+ @$unit:ident) => {
$(#[$unit_attr])*
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, Debug, Hash)]
pub struct $unit;
};
(@unit @$unit:ident) => {
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, Debug, Hash)]
pub struct $unit;
};
(@coefficient $factor:expr, $const:expr) => { $factor };
(@coefficient $factor:expr) => { $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,
}
};
}