use scale_factor::ScaleFactor;
use num::Zero;
use num_lib::NumCast;
#[cfg(feature = "plugins")]
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::cmp::Ordering;
use std::ops::{Add, Sub, Mul, Div, Neg};
#[cfg(feature = "unstable")]
use std::ops::{AddAssign, SubAssign};
use std::marker::PhantomData;
#[derive(Copy, RustcDecodable, RustcEncodable, Debug)]
#[cfg_attr(feature = "plugins", derive(HeapSizeOf))]
pub struct Length<Unit, T>(pub T, PhantomData<Unit>);
#[cfg(feature = "plugins")]
impl<Unit,T> Deserialize for Length<Unit,T> where T: Deserialize {
fn deserialize<D>(deserializer: &mut D) -> Result<Length<Unit,T>,D::Error>
where D: Deserializer {
Ok(Length(try!(Deserialize::deserialize(deserializer)), PhantomData))
}
}
#[cfg(feature = "plugins")]
impl<Unit,T> Serialize for Length<Unit,T> where T: Serialize {
fn serialize<S>(&self, serializer: &mut S) -> Result<(),S::Error> where S: Serializer {
self.0.serialize(serializer)
}
}
impl<Unit, T> Length<Unit, T> {
pub fn new(x: T) -> Length<Unit, T> {
Length(x, PhantomData)
}
}
impl<Unit, T: Clone> Length<Unit, T> {
pub fn get(&self) -> T {
self.0.clone()
}
}
impl<U, T: Clone + Add<T, Output=T>> Add for Length<U, T> {
type Output = Length<U, T>;
fn add(self, other: Length<U, T>) -> Length<U, T> {
Length::new(self.get() + other.get())
}
}
#[cfg(feature = "unstable")]
impl<U, T: Clone + AddAssign<T>> AddAssign for Length<U, T> {
fn add_assign(&mut self, other: Length<U, T>) {
self.0 += other.get();
}
}
impl<U, T: Clone + Sub<T, Output=T>> Sub<Length<U, T>> for Length<U, T> {
type Output = Length<U, T>;
fn sub(self, other: Length<U, T>) -> <Self as Sub>::Output {
Length::new(self.get() - other.get())
}
}
#[cfg(feature = "unstable")]
impl<U, T: Clone + SubAssign<T>> SubAssign for Length<U, T> {
fn sub_assign(&mut self, other: Length<U, T>) {
self.0 -= other.get();
}
}
impl<Src, Dst, T: Clone + Div<T, Output=T>> Div<Length<Src, T>> for Length<Dst, T> {
type Output = ScaleFactor<Src, Dst, T>;
#[inline]
fn div(self, other: Length<Src, T>) -> ScaleFactor<Src, Dst, T> {
ScaleFactor::new(self.get() / other.get())
}
}
impl<Src, Dst, T: Clone + Mul<T, Output=T>> Mul<ScaleFactor<Src, Dst, T>> for Length<Src, T> {
type Output = Length<Dst, T>;
#[inline]
fn mul(self, scale: ScaleFactor<Src, Dst, T>) -> Length<Dst, T> {
Length::new(self.get() * scale.get())
}
}
impl<Src, Dst, T: Clone + Div<T, Output=T>> Div<ScaleFactor<Src, Dst, T>> for Length<Dst, T> {
type Output = Length<Src, T>;
#[inline]
fn div(self, scale: ScaleFactor<Src, Dst, T>) -> Length<Src, T> {
Length::new(self.get() / scale.get())
}
}
impl <U, T:Clone + Neg<Output=T>> Neg for Length<U, T> {
type Output = Length<U, T>;
#[inline]
fn neg(self) -> Length<U, T> {
Length::new(-self.get())
}
}
impl<Unit, T0: NumCast + Clone> Length<Unit, T0> {
pub fn cast<T1: NumCast + Clone>(&self) -> Option<Length<Unit, T1>> {
NumCast::from(self.get()).map(Length::new)
}
}
impl<Unit, T: Clone> Clone for Length<Unit, T> {
fn clone(&self) -> Length<Unit, T> {
Length::new(self.get())
}
}
impl<Unit, T: Clone + PartialEq> PartialEq for Length<Unit, T> {
fn eq(&self, other: &Length<Unit, T>) -> bool { self.get().eq(&other.get()) }
}
impl<Unit, T: Clone + PartialOrd> PartialOrd for Length<Unit, T> {
fn partial_cmp(&self, other: &Length<Unit, T>) -> Option<Ordering> {
self.get().partial_cmp(&other.get())
}
}
impl<Unit, T: Clone + Eq> Eq for Length<Unit, T> {}
impl<Unit, T: Clone + Ord> Ord for Length<Unit, T> {
fn cmp(&self, other: &Length<Unit, T>) -> Ordering { self.get().cmp(&other.get()) }
}
impl<Unit, T: Zero> Zero for Length<Unit, T> {
fn zero() -> Length<Unit, T> {
Length::new(Zero::zero())
}
}
#[cfg(test)]
mod tests {
use super::Length;
use scale_factor::ScaleFactor;
#[derive(Debug, Copy, Clone)]
enum Inch {}
#[derive(Debug, Copy, Clone)]
enum Mm {}
#[test]
fn test_length() {
let mm_per_inch: ScaleFactor<Inch, Mm, f32> = ScaleFactor::new(25.4);
let one_foot: Length<Inch, f32> = Length::new(12.0);
let two_feet = one_foot.clone() + one_foot.clone();
let zero_feet = one_foot.clone() - one_foot.clone();
assert_eq!(one_foot.get(), 12.0);
assert_eq!(two_feet.get(), 24.0);
assert_eq!(zero_feet.get(), 0.0);
assert!(one_foot == one_foot);
assert!(two_feet != one_foot);
assert!(zero_feet < one_foot);
assert!(zero_feet <= one_foot);
assert!(two_feet > one_foot);
assert!(two_feet >= one_foot);
assert!( two_feet <= two_feet);
assert!( two_feet >= two_feet);
assert!(!(two_feet > two_feet));
assert!(!(two_feet < two_feet));
let one_foot_in_mm: Length<Mm, f32> = one_foot * mm_per_inch;
assert_eq!(one_foot_in_mm, Length::new(304.8));
assert_eq!(one_foot_in_mm / one_foot, mm_per_inch);
let back_to_inches: Length<Inch, f32> = one_foot_in_mm / mm_per_inch;
assert_eq!(one_foot, back_to_inches);
let int_foot: Length<Inch, isize> = one_foot.cast().unwrap();
assert_eq!(int_foot.get(), 12);
let negative_one_foot = -one_foot;
assert_eq!(negative_one_foot.get(), -12.0);
let negative_two_feet = -two_feet;
assert_eq!(negative_two_feet.get(), -24.0);
let zero_feet: Length<Inch, f32> = Length::new(0.0);
let negative_zero_feet = -zero_feet;
assert_eq!(negative_zero_feet.get(), 0.0);
}
#[cfg(feature = "unstable")]
#[test]
fn test_addassign() {
let one_cm: Length<Mm, f32> = Length::new(10.0);
let mut measurement: Length<Mm, f32> = Length::new(5.0);
measurement += one_cm;
assert_eq!(measurement.get(), 15.0);
}
#[cfg(feature = "unstable")]
#[test]
fn test_subassign() {
let one_cm: Length<Mm, f32> = Length::new(10.0);
let mut measurement: Length<Mm, f32> = Length::new(5.0);
measurement -= one_cm;
assert_eq!(measurement.get(), -5.0);
}
}