const_unit_poc 1.0.0

Proof of Concept: Physical units through const generics
Documentation
//! # Proof of Concept: Physical units through const generics
//!
//! Like a tiny [uom](https://crates.io/crates/uom) with better error messages!
//!
//! Correct usage:
//!
//! ```
//! #![feature(const_generics, const_evaluatable_checked)]
//! use const_unit_poc::values::{m, kg, s, N};
//!
//! let distance = 1.0 * m;
//! let mass = 18.0 * kg;
//! let force = distance * mass / (1.8 * s * 2.0 * s);
//! assert_eq!(force, 5.0 * N);
//!
//! let mut mutable_distance = 3.2 * m;
//! mutable_distance -= 0.2 * m;
//! mutable_distance += 2.0 * m;
//!
//! assert_eq!(mutable_distance, 5.0 * m);
//! ```
//!
//! Wrong usage:
//!
//! ```compile_fail
//! #![feature(const_generics, const_evaluatable_checked)]
//! use const_unit_poc::values::{kg, m};
//!
//! let mut distance = 15.0 * m;
//! distance = 2.0 * kg;
//! ```
//!
//! ```plain
//! error[E0308]: mismatched types
//!  --> src/some_module.rs:8:16
//!   |
//! 8 |     distance = 2.0 * kg;
//!   |                ^^^^^^^^ expected `1_i8`, found `0_i8`
//!   |
//!   = note: expected struct `Quantity<SiUnit { m: 1_i8, kg: 0_i8, s: 0_i8, A: 0_i8, K: 0_i8, mol: 0_i8, cd: 0_i8 }>`
//!              found struct `Quantity<SiUnit { m: 0_i8, kg: 1_i8, s: 0_i8, A: 0_i8, K: 0_i8, mol: 0_i8, cd: 0_i8 }>`
//! ```
#![feature(const_generics, const_evaluatable_checked, doc_cfg)]
#![allow(incomplete_features)]
#![cfg_attr(feature = "non_ascii", feature(non_ascii_idents))]

use std::ops;

pub mod units;
pub mod values;

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[allow(non_snake_case)]
pub struct SiUnit {
    m: i8,
    kg: i8,
    s: i8,
    A: i8,
    K: i8,
    mol: i8,
    cd: i8,
}

// Can't call trait methods in const context, so these are inherent methods
impl SiUnit {
    const fn neg(self) -> Self {
        Self {
            m: -self.m,
            kg: -self.kg,
            s: -self.s,
            A: -self.A,
            K: -self.K,
            mol: -self.mol,
            cd: -self.cd,
        }
    }

    const fn unit_mul(self, rhs: Self) -> Self {
        Self {
            m: self.m + rhs.m,
            kg: self.kg + rhs.kg,
            s: self.s + rhs.s,
            A: self.A + rhs.A,
            K: self.K + rhs.K,
            mol: self.mol + rhs.mol,
            cd: self.cd + rhs.cd,
        }
    }

    const fn unit_div(self, rhs: Self) -> Self {
        Self {
            m: self.m - rhs.m,
            kg: self.kg - rhs.kg,
            s: self.s - rhs.s,
            A: self.A - rhs.A,
            K: self.K - rhs.K,
            mol: self.mol - rhs.mol,
            cd: self.cd - rhs.cd,
        }
    }
}

#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
#[repr(transparent)]
pub struct Quantity<const U: SiUnit> {
    pub raw_value: f64,
}

impl<const U: SiUnit> ops::Add for Quantity<U> {
    type Output = Self;

    fn add(self, rhs: Self) -> Self {
        Self { raw_value: self.raw_value + rhs.raw_value }
    }
}

impl<const U: SiUnit> ops::AddAssign for Quantity<U> {
    fn add_assign(&mut self, rhs: Self) {
        self.raw_value += rhs.raw_value;
    }
}

impl<const U: SiUnit> ops::Sub for Quantity<U> {
    type Output = Self;

    fn sub(self, rhs: Self) -> Self {
        Self { raw_value: self.raw_value - rhs.raw_value }
    }
}

impl<const U: SiUnit> ops::SubAssign for Quantity<U> {
    fn sub_assign(&mut self, rhs: Self) {
        self.raw_value -= rhs.raw_value;
    }
}

impl<const U: SiUnit> ops::Mul<f64> for Quantity<U> {
    type Output = Quantity<U>;

    fn mul(self, rhs: f64) -> Self::Output {
        Quantity { raw_value: self.raw_value * rhs }
    }
}

impl<const U: SiUnit> ops::Mul<Quantity<U>> for f64 {
    type Output = Quantity<U>;

    fn mul(self, rhs: Quantity<U>) -> Self::Output {
        Quantity { raw_value: self * rhs.raw_value }
    }
}

impl<const UL: SiUnit, const UR: SiUnit> ops::Mul<Quantity<UR>> for Quantity<UL>
where
    Quantity<{ UL.unit_mul(UR) }>: ,
{
    type Output = Quantity<{ UL.unit_mul(UR) }>;

    fn mul(self, rhs: Quantity<UR>) -> Self::Output {
        Quantity { raw_value: self.raw_value * rhs.raw_value }
    }
}

impl<const U: SiUnit> ops::MulAssign<f64> for Quantity<U> {
    fn mul_assign(&mut self, rhs: f64) {
        self.raw_value *= rhs;
    }
}

impl<const U: SiUnit> ops::Div<f64> for Quantity<U> {
    type Output = Quantity<U>;

    fn div(self, rhs: f64) -> Self::Output {
        Quantity { raw_value: self.raw_value / rhs }
    }
}

impl<const U: SiUnit> ops::Div<Quantity<U>> for f64
where
    Quantity<{ U.neg() }>: ,
{
    type Output = Quantity<{ U.neg() }>;

    fn div(self, rhs: Quantity<U>) -> Self::Output {
        Quantity { raw_value: self / rhs.raw_value }
    }
}

impl<const UL: SiUnit, const UR: SiUnit> ops::Div<Quantity<UR>> for Quantity<UL>
where
    Quantity<{ UL.unit_div(UR) }>: ,
{
    type Output = Quantity<{ UL.unit_div(UR) }>;

    fn div(self, rhs: Quantity<UR>) -> Self::Output {
        Quantity { raw_value: self.raw_value / rhs.raw_value }
    }
}

impl<const U: SiUnit> ops::DivAssign<f64> for Quantity<U> {
    fn div_assign(&mut self, rhs: f64) {
        self.raw_value /= rhs;
    }
}