use core::fmt;
use core::marker::PhantomData;
use core::ops::{Add, Div, Mul, Sub};
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
pub struct Mass;
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
pub struct Temperature;
pub trait Unit {
const LABEL: &'static str;
const FACTOR: f64;
const ZERO: f64;
type Measure;
fn convert<T>(val: f64) -> f64
where
T: Unit<Measure = Self::Measure>,
{
val * (Self::FACTOR / T::FACTOR)
}
}
#[macro_export]
macro_rules! declare_unit {
($(#[$doc:meta])*
$unit:ident,
$label:expr,
$measure:ident,
$factor:expr,
) => {
$(#[$doc])*
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
pub struct $unit;
impl $crate::quan::Unit for $unit {
type Measure = $measure;
const LABEL: &'static str = $label;
const FACTOR: f64 = $factor;
const ZERO: f64 = 0.0;
}
impl core::ops::Mul<$unit> for f64 {
type Output = $crate::quan::Quantity<$unit>;
fn mul(self, _unit: $unit) -> Self::Output {
Self::Output::new(self)
}
}
impl core::ops::Mul<$unit> for i32 {
type Output = $crate::quan::Quantity<$unit>;
fn mul(self, _unit: $unit) -> Self::Output {
Self::Output::new(self)
}
}
};
($(#[$doc:meta])*
$unit:ident,
$label:expr,
$measure:ident,
$factor:expr,
$zero:expr,
) => {
$(#[$doc])*
#[allow(non_camel_case_types)]
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
pub struct $unit;
impl $crate::quan::Unit for $unit {
type Measure = $measure;
const LABEL: &'static str = $label;
const FACTOR: f64 = $factor;
const ZERO: f64 = $zero;
fn convert<T>(val: f64) -> f64
where
T: $crate::quan::Unit<Measure = Self::Measure>,
{
let v = (val - Self::ZERO) * Self::FACTOR;
v / T::FACTOR + T::ZERO
}
}
impl core::ops::Mul<$unit> for f64 {
type Output = $crate::quan::Quantity<$unit>;
fn mul(self, _unit: $unit) -> Self::Output {
Self::Output::new(self)
}
}
impl core::ops::Mul<$unit> for i32 {
type Output = $crate::quan::Quantity<$unit>;
fn mul(self, _unit: $unit) -> Self::Output {
Self::Output::new(self)
}
}
};
}
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
pub struct Quantity<U>
where
U: Unit,
{
pub value: f64,
unit: PhantomData<U>,
}
impl<U> Quantity<U>
where
U: Unit,
{
pub fn new<V>(value: V) -> Self
where
V: Into<f64>,
{
Self {
value: value.into(),
unit: PhantomData,
}
}
pub fn to<T>(self) -> Quantity<T>
where
T: Unit<Measure = <U>::Measure>,
{
Quantity::new(U::convert::<T>(self.value))
}
}
impl<U> fmt::Display for Quantity<U>
where
U: Unit,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.value.fmt(f)?;
write!(f, " {}", U::LABEL)
}
}
impl<U> Add for Quantity<U>
where
U: Unit,
{
type Output = Self;
fn add(self, other: Self) -> Self::Output {
Self::new(self.value + other.value)
}
}
impl<U> Sub for Quantity<U>
where
U: Unit,
{
type Output = Self;
fn sub(self, other: Self) -> Self::Output {
Self::new(self.value - other.value)
}
}
pub trait MulUnit {}
impl MulUnit for Mass {}
impl<U, M, V> Mul<V> for Quantity<U>
where
U: Unit<Measure = M>,
M: MulUnit,
V: Into<f64>,
{
type Output = Self;
fn mul(self, scalar: V) -> Self::Output {
Self::new(self.value * scalar.into())
}
}
impl<U, M> Mul<Quantity<U>> for f64
where
U: Unit<Measure = M>,
M: MulUnit,
{
type Output = Quantity<U>;
fn mul(self, quan: Self::Output) -> Self::Output {
Self::Output::new(self * quan.value)
}
}
impl<U, M> Div<f64> for Quantity<U>
where
U: Unit<Measure = M>,
M: MulUnit,
{
type Output = Self;
fn div(self, scalar: f64) -> Self::Output {
Self::new(self.value / scalar)
}
}