#![cfg_attr(feature="unstable", feature(unboxed_closures, core, zero_one))]
extern crate tylar;
#[doc(no_inline)]
pub use tylar::{NumType,Zero,P1};
#[doc(no_inline,hidden)]
pub use tylar::Sub as TSub;
#[doc(no_inline,hidden)]
pub use tylar::Add as TAdd;
#[doc(no_inline,hidden)]
pub use tylar::Halve as THalve;
use std::fmt::{Formatter,Result};
pub trait UnitFormat { fn fmt(&mut Formatter) -> Result; }
pub trait UnitZero {}
pub trait UnitAdd<RHS> { type Out; }
pub trait UnitSub<RHS> { type Out; }
pub trait UnitMul<RHS> { type Out; }
pub trait UnitDiv<RHS> { type Out; }
pub trait UnitSqrt { type Out; }
#[macro_export]
macro_rules! units {( $name:ident { $( $dim:ident[$unit:ident]),+ } ) => {
use $crate::{UnitZero,UnitAdd,UnitSub,UnitMul,UnitDiv,UnitSqrt,UnitFormat};
use std::marker::PhantomData;
use std::fmt::{Debug,Formatter,Result};
use std::ops::{Add,Sub,Mul,Div,Deref};
use $crate::{NumType,Zero,P1,TAdd,TSub,THalve};
#[derive(Copy,Clone,PartialEq,PartialOrd,Eq,Ord)]
pub struct $name<D,N=f64> {
amount: N,
phantom: PhantomData<D>
}
impl<D,N> $name<D,N> {
fn new(v: N) -> Self {
$name { amount: v, phantom: PhantomData }
}
}
impl<D:UnitFormat,N> Debug for $name<D,N> where N:Debug {
fn fmt(&self, formatter: &mut Formatter) -> Result {
try!(self.amount.fmt(formatter));
D::fmt(formatter)
}
}
impl<D1,D2,N1,N2> Add<$name<D2,N2>> for $name<D1,N1> where D1:UnitAdd<D2>, N1:Add<N2> {
type Output = $name<D1::Out, N1::Output>;
fn add(self, rhs: $name<D2,N2>) -> Self::Output {
$name::new(self.amount + rhs.amount)
}
}
impl<D1,D2,N1,N2> Sub<$name<D2,N2>> for $name<D1,N1> where D1:UnitSub<D2>, N1:Sub<N2> {
type Output = $name<D1::Out, N1::Output>;
fn sub(self, rhs: $name<D2,N2>) -> Self::Output {
$name::new(self.amount - rhs.amount)
}
}
impl<D1,D2,N1,N2> Mul<$name<D2,N2>> for $name<D1,N1> where D1:UnitMul<D2>, N1:Mul<N2> {
type Output = $name<D1::Out, N1::Output>;
fn mul(self, rhs: $name<D2,N2>) -> Self::Output {
$name::new(self.amount * rhs.amount)
}
}
impl<D1,D2,N1,N2> Div<$name<D2,N2>> for $name<D1,N1> where D1:UnitDiv<D2>, N1: Div<N2> {
type Output = $name<D1::Out, N1::Output>;
fn div(self, rhs: $name<D2,N2>) -> Self::Output {
$name::new(self.amount / rhs.amount)
}
}
#[allow(dead_code)]
impl<D1> $name<D1,f64> where D1:UnitSqrt {
pub fn sqrt(self) -> $name<D1::Out,f64> {
$name::new(self.amount.sqrt())
}
}
#[allow(dead_code)]
impl<D1> $name<D1,f32> where D1:UnitSqrt {
pub fn sqrt(self) -> $name<D1::Out,f32> {
$name::new(self.amount.sqrt())
}
}
impl<D:UnitZero,N> Deref for $name<D,N> {
type Target = N;
fn deref(&self) -> &Self::Target { &self.amount }
}
#[cfg(feature = "unstable")]
impl <D,N> ::std::num::Zero for $name<D,N> where N: ::std::num::Zero {
fn zero() -> Self {
$name::new(N::zero())
}
}
__dim_fn_call_helper!($name);
#[cfg(not(feature = "unstable"))]
impl<D> Mul<$name<D,f64>> for f64 {
type Output = $name<D,f64>;
fn mul(self, rhs: $name<D,f64>) -> Self::Output {
$name::new(self * rhs.amount)
}
}
#[cfg(not(feature = "unstable"))]
impl<D> Mul<$name<D,f32>> for f32 {
type Output = $name<D,f32>;
fn mul(self, rhs: $name<D,f32>) -> Self::Output {
$name::new(self * rhs.amount)
}
}
#[derive(Copy,Clone,PartialEq,PartialOrd,Eq,Ord)]
#[allow(non_snake_case)]
pub struct Unit<$($dim:NumType=Zero),+> {
$($dim: PhantomData<$dim>),+
}
impl<$($dim:NumType),+> UnitFormat for Unit<$($dim),+> {
fn fmt(formatter: &mut Formatter) -> Result {
let mut exp: i32;
$(
exp = $dim::new().into();
match exp {
0 => (),
1 => try!(write!(formatter, " {}", stringify!($unit))),
_ => try!(write!(formatter, " {}^{:?}", stringify!($unit), exp))
}
)+
Ok(())
}
}
impl<$($dim:NumType),+> UnitAdd<Unit<$($dim),+>> for Unit<$($dim),+> { type Out = Unit<$($dim),+>; }
impl<$($dim:NumType),+> UnitSub<Unit<$($dim),+>> for Unit<$($dim),+> { type Out = Unit<$($dim),+>; }
#[allow(non_camel_case_types)]
impl<$($dim:NumType),+ , $($unit:NumType),+> UnitMul<Unit<$($unit),+>> for Unit<$($dim),+>
where $($dim:TAdd<$unit>),+ { type Out = Unit<$(<$dim as TAdd<$unit>>::Out),+>; }
#[allow(non_camel_case_types)]
impl<$($dim:NumType),+ , $($unit:NumType),+> UnitDiv<Unit<$($unit),+>> for Unit<$($dim),+>
where $($dim:TSub<$unit>),+ { type Out = Unit<$(<$dim as TSub<$unit>>::Out),+>; }
impl<$($dim:NumType),+> UnitSqrt for Unit<$($dim),+>
where $($dim:THalve),+ { type Out = Unit<$(<$dim as THalve>::Out),+>; }
pub type One = Unit;
impl UnitZero for One {}
__dim_type_alias_helper! { $($dim),+ -> P1 }
pub mod f64 {
use std::marker::PhantomData;
use super::{$name,One,$($dim),+};
#[allow(non_upper_case_globals, dead_code)]
pub const one: $name<One, f64> = $name {
amount: 1.0,
phantom: PhantomData
};
$(
#[allow(non_upper_case_globals, dead_code)]
pub const $unit: $name<$dim, f64> = $name {
amount: 1.0,
phantom: PhantomData
};
)+
}
pub mod f32 {
use std::marker::PhantomData;
use super::{$name,One,$($dim),+};
#[allow(non_upper_case_globals, dead_code)]
pub const one: $name<One, f32> = $name {
amount: 1.0,
phantom: PhantomData
};
$(
#[allow(non_upper_case_globals, dead_code)]
pub const $unit: $name<$dim, f32> = $name {
amount: 1.0,
phantom: PhantomData
};
)+
}
}}
#[macro_export]
#[doc(hidden)]
macro_rules! __dim_type_alias_helper {
( $dim:ident -> $($types:ty),+ ) => (
pub type $dim = Unit<$($types),+>;
);
( $dim:ident, $($dims:ident),* -> $($types:ty),+ ) => (
pub type $dim = Unit<$($types),+>; __dim_type_alias_helper!( $($dims),* -> Zero, $($types),+);
)
}
#[macro_export]
#[doc(hidden)]
#[cfg(feature = "unstable")]
macro_rules! __dim_fn_call_helper {
($name:ident) => (
impl<D,N> FnOnce<(N,)> for $name<D,N> where N:Mul<N,Output=N> {
type Output = $name<D,N>;
extern "rust-call" fn call_once(self, args: (N,)) -> Self::Output {
$name::new(self.amount * args.0)
}
}
);
}
#[macro_export]
#[doc(hidden)]
#[cfg(not(feature = "unstable"))]
macro_rules! __dim_fn_call_helper {
($name:ident) => ()
}
mod smoke_test {
units! {
Units {
Meter[m],
Second[s]
}
}
}