units-rs/src/lib.rs
2023-02-22 14:35:05 +01:00

716 lines
18 KiB
Rust

#![cfg_attr(not(feature = "std"), no_std)]
mod display;
pub mod types;
#[cfg(not(feature = "std"))]
use core::{
marker::PhantomData,
ops::{
Add, AddAssign, Deref, DerefMut, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub,
SubAssign,
},
};
#[cfg(feature = "std")]
use std::{
marker::PhantomData,
ops::{
Add, AddAssign, Deref, DerefMut, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub,
SubAssign,
},
};
use num_traits::{Num, One, Zero};
use typenum::{int::Z0, op, Integer};
use types::Unit;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
pub struct SiUnit<T, Second, Meter, Kilogram, Ampere, Kelvin, Mole, Candela>
where
Second: Integer,
Meter: Integer,
Kilogram: Integer,
Ampere: Integer,
Kelvin: Integer,
Mole: Integer,
Candela: Integer,
{
value: T,
_s: PhantomData<Second>,
_m: PhantomData<Meter>,
_kg: PhantomData<Kilogram>,
_a: PhantomData<Ampere>,
_k: PhantomData<Kelvin>,
_mol: PhantomData<Mole>,
_cd: PhantomData<Candela>,
}
impl<T, Second, Meter, Kilogram, Ampere, Kelvin, Mole, Candela> Deref
for SiUnit<T, Second, Meter, Kilogram, Ampere, Kelvin, Mole, Candela>
where
Second: Integer,
Meter: Integer,
Kilogram: Integer,
Ampere: Integer,
Kelvin: Integer,
Mole: Integer,
Candela: Integer,
{
type Target = T;
fn deref(&self) -> &Self::Target {
&self.value
}
}
impl<T, Second, Meter, Kilogram, Ampere, Kelvin, Mole, Candela> DerefMut
for SiUnit<T, Second, Meter, Kilogram, Ampere, Kelvin, Mole, Candela>
where
Second: Integer,
Meter: Integer,
Kilogram: Integer,
Ampere: Integer,
Kelvin: Integer,
Mole: Integer,
Candela: Integer,
{
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.value
}
}
impl<T, Second, Meter, Kilogram, Ampere, Kelvin, Mole, Candela> Neg
for SiUnit<T, Second, Meter, Kilogram, Ampere, Kelvin, Mole, Candela>
where
Second: Integer,
Meter: Integer,
Kilogram: Integer,
Ampere: Integer,
Kelvin: Integer,
Mole: Integer,
Candela: Integer,
T: Neg,
{
type Output = SiUnit<T::Output, Second, Meter, Kilogram, Ampere, Kelvin, Mole, Candela>;
fn neg(self) -> Self::Output {
Self::Output::new(-self.value)
}
}
impl<T, Second, Meter, Kilogram, Ampere, Kelvin, Mole, Candela> Zero
for SiUnit<T, Second, Meter, Kilogram, Ampere, Kelvin, Mole, Candela>
where
Second: Integer,
Meter: Integer,
Kilogram: Integer,
Ampere: Integer,
Kelvin: Integer,
Mole: Integer,
Candela: Integer,
T: Zero,
{
fn zero() -> Self {
Self::new(T::zero())
}
fn is_zero(&self) -> bool {
self.value.is_zero()
}
}
impl<T, Second, Meter, Kilogram, Ampere, Kelvin, Mole, Candela> One
for SiUnit<T, Second, Meter, Kilogram, Ampere, Kelvin, Mole, Candela>
where
Second: Integer + Add<Output = Second>,
Meter: Integer + Add<Output = Meter>,
Kilogram: Integer + Add<Output = Kilogram>,
Ampere: Integer + Add<Output = Ampere>,
Kelvin: Integer + Add<Output = Kelvin>,
Mole: Integer + Add<Output = Mole>,
Candela: Integer + Add<Output = Candela>,
T: One,
<Second as Add>::Output: Integer,
<Meter as Add>::Output: Integer,
<Kilogram as Add>::Output: Integer,
<Ampere as Add>::Output: Integer,
<Kelvin as Add>::Output: Integer,
<Mole as Add>::Output: Integer,
<Candela as Add>::Output: Integer,
{
fn one() -> Self {
Self::new(T::one())
}
}
impl<T, Second, Meter, Kilogram, Ampere, Kelvin, Mole, Candela> Num
for SiUnit<T, Second, Meter, Kilogram, Ampere, Kelvin, Mole, Candela>
where
Second: Integer + Add<Output = Second> + Sub<Output = Second> + PartialEq,
Meter: Integer + Add<Output = Meter> + Sub<Output = Meter> + PartialEq,
Kilogram: Integer + Add<Output = Kilogram> + Sub<Output = Kilogram> + PartialEq,
Ampere: Integer + Add<Output = Ampere> + Sub<Output = Ampere> + PartialEq,
Kelvin: Integer + Add<Output = Kelvin> + Sub<Output = Kelvin> + PartialEq,
Mole: Integer + Add<Output = Mole> + Sub<Output = Mole> + PartialEq,
Candela: Integer + Add<Output = Candela> + Sub<Output = Candela> + PartialEq,
T: Num,
{
type FromStrRadixErr = T::FromStrRadixErr;
fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
T::from_str_radix(str, radix).map(Self::new)
}
}
impl<T, Second, Meter, Kilogram, Ampere, Kelvin, Mole, Candela> Add
for SiUnit<T, Second, Meter, Kilogram, Ampere, Kelvin, Mole, Candela>
where
Second: Integer,
Meter: Integer,
Kilogram: Integer,
Ampere: Integer,
Kelvin: Integer,
Mole: Integer,
Candela: Integer,
T: Add,
{
type Output = SiUnit<T::Output, Second, Meter, Kilogram, Ampere, Kelvin, Mole, Candela>;
fn add(self, rhs: Self) -> Self::Output {
Self::Output::new(self.value + rhs.value)
}
}
impl<T, Second, Meter, Kilogram, Ampere, Kelvin, Mole, Candela> AddAssign
for SiUnit<T, Second, Meter, Kilogram, Ampere, Kelvin, Mole, Candela>
where
Second: Integer,
Meter: Integer,
Kilogram: Integer,
Ampere: Integer,
Kelvin: Integer,
Mole: Integer,
Candela: Integer,
T: AddAssign,
{
fn add_assign(&mut self, rhs: Self) {
self.value += rhs.value;
}
}
impl<T, Second, Meter, Kilogram, Ampere, Kelvin, Mole, Candela> Sub
for SiUnit<T, Second, Meter, Kilogram, Ampere, Kelvin, Mole, Candela>
where
Second: Integer,
Meter: Integer,
Kilogram: Integer,
Ampere: Integer,
Kelvin: Integer,
Mole: Integer,
Candela: Integer,
T: Sub,
{
type Output = SiUnit<T::Output, Second, Meter, Kilogram, Ampere, Kelvin, Mole, Candela>;
fn sub(self, rhs: Self) -> Self::Output {
Self::Output::new(self.value - rhs.value)
}
}
impl<T, Second, Meter, Kilogram, Ampere, Kelvin, Mole, Candela> SubAssign
for SiUnit<T, Second, Meter, Kilogram, Ampere, Kelvin, Mole, Candela>
where
Second: Integer,
Meter: Integer,
Kilogram: Integer,
Ampere: Integer,
Kelvin: Integer,
Mole: Integer,
Candela: Integer,
T: SubAssign,
{
fn sub_assign(&mut self, rhs: Self) {
self.value -= rhs.value;
}
}
impl<T, Second, Meter, Kilogram, Ampere, Kelvin, Mole, Candela> Mul<T>
for SiUnit<T, Second, Meter, Kilogram, Ampere, Kelvin, Mole, Candela>
where
Second: Integer,
Meter: Integer,
Kilogram: Integer,
Ampere: Integer,
Kelvin: Integer,
Mole: Integer,
Candela: Integer,
T: Mul,
{
type Output = <Self as Mul<Unit<T>>>::Output;
fn mul(self, rhs: T) -> Self::Output {
self * Unit::new(rhs)
}
}
impl<T, Second, Meter, Kilogram, Ampere, Kelvin, Mole, Candela> MulAssign<T>
for SiUnit<T, Second, Meter, Kilogram, Ampere, Kelvin, Mole, Candela>
where
Second: Integer,
Meter: Integer,
Kilogram: Integer,
Ampere: Integer,
Kelvin: Integer,
Mole: Integer,
Candela: Integer,
T: MulAssign,
{
fn mul_assign(&mut self, rhs: T) {
self.value *= rhs;
}
}
impl<T, Second, Meter, Kilogram, Ampere, Kelvin, Mole, Candela> Div<T>
for SiUnit<T, Second, Meter, Kilogram, Ampere, Kelvin, Mole, Candela>
where
Second: Integer + Sub<Z0>,
Meter: Integer + Sub<Z0>,
Kilogram: Integer + Sub<Z0>,
Ampere: Integer + Sub<Z0>,
Kelvin: Integer + Sub<Z0>,
Mole: Integer + Sub<Z0>,
Candela: Integer + Sub<Z0>,
T: Div,
<Second as Sub<Z0>>::Output: Integer,
<Meter as Sub<Z0>>::Output: Integer,
<Kilogram as Sub<Z0>>::Output: Integer,
<Ampere as Sub<Z0>>::Output: Integer,
<Kelvin as Sub<Z0>>::Output: Integer,
<Mole as Sub<Z0>>::Output: Integer,
<Candela as Sub<Z0>>::Output: Integer,
{
type Output = <Self as Div<Unit<T>>>::Output;
fn div(self, rhs: T) -> Self::Output {
self / Unit::new(rhs)
}
}
impl<T, Second, Meter, Kilogram, Ampere, Kelvin, Mole, Candela> DivAssign<T>
for SiUnit<T, Second, Meter, Kilogram, Ampere, Kelvin, Mole, Candela>
where
Second: Integer,
Meter: Integer,
Kilogram: Integer,
Ampere: Integer,
Kelvin: Integer,
Mole: Integer,
Candela: Integer,
T: DivAssign,
{
fn div_assign(&mut self, rhs: T) {
self.value /= rhs;
}
}
impl<T, Second, Meter, Kilogram, Ampere, Kelvin, Mole, Candela> Rem<T>
for SiUnit<T, Second, Meter, Kilogram, Ampere, Kelvin, Mole, Candela>
where
Second: Integer + Sub<Z0>,
Meter: Integer + Sub<Z0>,
Kilogram: Integer + Sub<Z0>,
Ampere: Integer + Sub<Z0>,
Kelvin: Integer + Sub<Z0>,
Mole: Integer + Sub<Z0>,
Candela: Integer + Sub<Z0>,
T: Rem,
<Second as Sub<Z0>>::Output: Integer,
<Meter as Sub<Z0>>::Output: Integer,
<Kilogram as Sub<Z0>>::Output: Integer,
<Ampere as Sub<Z0>>::Output: Integer,
<Kelvin as Sub<Z0>>::Output: Integer,
<Mole as Sub<Z0>>::Output: Integer,
<Candela as Sub<Z0>>::Output: Integer,
{
type Output = <Self as Rem<Unit<T>>>::Output;
fn rem(self, rhs: T) -> Self::Output {
self % Unit::new(rhs)
}
}
impl<T, Second, Meter, Kilogram, Ampere, Kelvin, Mole, Candela> RemAssign<T>
for SiUnit<T, Second, Meter, Kilogram, Ampere, Kelvin, Mole, Candela>
where
Second: Integer,
Meter: Integer,
Kilogram: Integer,
Ampere: Integer,
Kelvin: Integer,
Mole: Integer,
Candela: Integer,
T: RemAssign,
{
fn rem_assign(&mut self, rhs: T) {
self.value %= rhs;
}
}
impl<T, Second, Meter, Kilogram, Ampere, Kelvin, Mole, Candela>
SiUnit<T, Second, Meter, Kilogram, Ampere, Kelvin, Mole, Candela>
where
Second: Integer,
Meter: Integer,
Kilogram: Integer,
Ampere: Integer,
Kelvin: Integer,
Mole: Integer,
Candela: Integer,
{
pub const fn new(value: T) -> Self {
Self {
value,
_s: PhantomData,
_m: PhantomData,
_kg: PhantomData,
_a: PhantomData,
_k: PhantomData,
_mol: PhantomData,
_cd: PhantomData,
}
}
}
impl<
T,
Second1,
Meter1,
Kilogram1,
Ampere1,
Kelvin1,
Mole1,
Candela1,
Second2,
Meter2,
Kilogram2,
Ampere2,
Kelvin2,
Mole2,
Candela2,
> Mul<SiUnit<T, Second1, Meter1, Kilogram1, Ampere1, Kelvin1, Mole1, Candela1>>
for SiUnit<T, Second2, Meter2, Kilogram2, Ampere2, Kelvin2, Mole2, Candela2>
where
Mole2: Integer,
Candela2: Integer,
Candela1: Integer + Add<Candela2>,
Mole1: Integer + Add<Mole2>,
Kelvin1: Integer + Add<Kelvin2>,
Ampere1: Integer + Add<Ampere2>,
Kilogram1: Integer + Add<Kilogram2>,
Meter1: Integer + Add<Meter2>,
Second1: Integer + Add<Second2>,
Kelvin2: Integer,
Ampere2: Integer,
Kilogram2: Integer,
Meter2: Integer,
Second2: Integer,
T: Mul,
Second1::Output: Integer,
Meter1::Output: Integer,
Kilogram1::Output: Integer,
Ampere1::Output: Integer,
Kelvin1::Output: Integer,
Mole1::Output: Integer,
Candela1::Output: Integer,
{
type Output = SiUnit<
T::Output,
op!(Second1 + Second2),
op!(Meter1 + Meter2),
op!(Kilogram1 + Kilogram2),
op!(Ampere1 + Ampere2),
op!(Kelvin1 + Kelvin2),
op!(Mole1 + Mole2),
op!(Candela1 + Candela2),
>;
fn mul(
self,
rhs: SiUnit<T, Second1, Meter1, Kilogram1, Ampere1, Kelvin1, Mole1, Candela1>,
) -> Self::Output {
Self::Output::new(self.value * rhs.value)
}
}
impl<
T,
Second1,
Meter1,
Kilogram1,
Ampere1,
Kelvin1,
Mole1,
Candela1,
Second2,
Meter2,
Kilogram2,
Ampere2,
Kelvin2,
Mole2,
Candela2,
> Div<SiUnit<T, Second1, Meter1, Kilogram1, Ampere1, Kelvin1, Mole1, Candela1>>
for SiUnit<T, Second2, Meter2, Kilogram2, Ampere2, Kelvin2, Mole2, Candela2>
where
Second1: Integer,
Meter1: Integer,
Kilogram1: Integer,
Ampere1: Integer,
Kelvin1: Integer,
Mole1: Integer,
Candela1: Integer,
Second2: Integer + Sub<Second1>,
Meter2: Integer + Sub<Meter1>,
Kilogram2: Integer + Sub<Kilogram1>,
Ampere2: Integer + Sub<Ampere1>,
Kelvin2: Integer + Sub<Kelvin1>,
Mole2: Integer + Sub<Mole1>,
Candela2: Integer + Sub<Candela1>,
T: Div,
Second2::Output: Integer,
Meter2::Output: Integer,
Kilogram2::Output: Integer,
Ampere2::Output: Integer,
Kelvin2::Output: Integer,
Mole2::Output: Integer,
Candela2::Output: Integer,
{
type Output = SiUnit<
T::Output,
op!(Second2 - Second1),
op!(Meter2 - Meter1),
op!(Kilogram2 - Kilogram1),
op!(Ampere2 - Ampere1),
op!(Kelvin2 - Kelvin1),
op!(Mole2 - Mole1),
op!(Candela2 - Candela1),
>;
fn div(
self,
rhs: SiUnit<T, Second1, Meter1, Kilogram1, Ampere1, Kelvin1, Mole1, Candela1>,
) -> Self::Output {
Self::Output::new(self.value / rhs.value)
}
}
impl<
T,
Second1,
Meter1,
Kilogram1,
Ampere1,
Kelvin1,
Mole1,
Candela1,
Second2,
Meter2,
Kilogram2,
Ampere2,
Kelvin2,
Mole2,
Candela2,
> Rem<SiUnit<T, Second1, Meter1, Kilogram1, Ampere1, Kelvin1, Mole1, Candela1>>
for SiUnit<T, Second2, Meter2, Kilogram2, Ampere2, Kelvin2, Mole2, Candela2>
where
Second1: Integer,
Meter1: Integer,
Kilogram1: Integer,
Ampere1: Integer,
Kelvin1: Integer,
Mole1: Integer,
Candela1: Integer,
Second2: Integer + Sub<Second1>,
Meter2: Integer + Sub<Meter1>,
Kilogram2: Integer + Sub<Kilogram1>,
Ampere2: Integer + Sub<Ampere1>,
Kelvin2: Integer + Sub<Kelvin1>,
Mole2: Integer + Sub<Mole1>,
Candela2: Integer + Sub<Candela1>,
T: Rem,
Second2::Output: Integer,
Meter2::Output: Integer,
Kilogram2::Output: Integer,
Ampere2::Output: Integer,
Kelvin2::Output: Integer,
Mole2::Output: Integer,
Candela2::Output: Integer,
{
type Output = SiUnit<
T::Output,
op!(Second2 - Second1),
op!(Meter2 - Meter1),
op!(Kilogram2 - Kilogram1),
op!(Ampere2 - Ampere1),
op!(Kelvin2 - Kelvin1),
op!(Mole2 - Mole1),
op!(Candela2 - Candela1),
>;
fn rem(
self,
rhs: SiUnit<T, Second1, Meter1, Kilogram1, Ampere1, Kelvin1, Mole1, Candela1>,
) -> Self::Output {
Self::Output::new(self.value % rhs.value)
}
}
#[cfg(test)]
mod test {
use super::types::{
Ampere, Coulomb, CubicMeter, Meter, ReciprocalMeter, Second, SquareMeter, Unit, Volt, Watt,
};
use num_traits::{Num, One, Zero};
#[test]
fn clone() {
let m = Meter::new(2);
assert_eq!(m.clone(), m);
}
#[test]
fn ord() {
let a = Meter::new(2);
let b = Meter::new(3);
assert!(a < b);
assert!(b > a);
assert!(a == a);
assert!(a != b);
assert_eq!(a.max(b), b);
assert_eq!(a.min(b), a);
}
#[test]
fn deref() {
let m = Meter::new(2);
assert_eq!(*m, 2);
}
#[test]
fn deref_mut() {
let mut m = Meter::new(2);
*m = 3;
assert_eq!(*m, 3);
}
#[test]
fn neg() {
let m = Meter::new(2);
assert_eq!(-m, Meter::new(-2));
}
#[test]
fn zero() {
let z = Meter::zero();
assert_eq!(z, Meter::new(0));
assert!(z.is_zero());
}
#[test]
fn one() {
let o = Unit::one();
assert_eq!(o, Unit::new(1));
}
#[test]
fn from_str_radix() {
assert_eq!(Unit::from_str_radix("24", 10), Ok(Unit::new(24)));
}
#[test]
fn add() {
let m = Meter::new(2);
assert_eq!(m + m, Meter::new(4));
assert_eq!(m + m + m, Meter::new(6));
}
#[test]
fn add_assign() {
let mut m = Meter::new(2);
m += m;
assert_eq!(m, Meter::new(4));
}
#[test]
fn sub() {
let m = Meter::new(2);
assert_eq!(m - m, Meter::new(0));
assert_eq!(m - m - m, Meter::new(-2));
}
#[test]
fn sub_assign() {
let mut m = Meter::new(2);
m -= m;
assert_eq!(m, Meter::new(0));
}
#[test]
fn mul() {
let m = Meter::new(2);
assert_eq!(m * m, SquareMeter::new(4));
assert_eq!(m * m * m, CubicMeter::new(8));
let s = Second::new(1);
let a = Ampere::new(2);
assert_eq!(s * a, Coulomb::new(2));
let v = Volt::new(2);
assert_eq!(v * a, Watt::new(4));
assert_eq!(m * 2, Meter::new(4));
}
#[test]
fn mul_assign() {
let mut m = Meter::new(2);
m *= 2;
assert_eq!(m, Meter::new(4));
}
#[test]
fn div() {
let m = Meter::new(2);
assert_eq!(m / m, Unit::new(1));
assert_eq!(m / m / m, ReciprocalMeter::new(0));
let c = Coulomb::new(4);
let a = Ampere::new(2);
assert_eq!(c / a, Second::new(2));
let w = Watt::new(2);
assert_eq!(w / a, Volt::new(1));
assert_eq!(m / 2, Meter::new(1));
}
#[test]
fn div_assign() {
let mut m = Meter::new(2);
m /= 2;
assert_eq!(m, Meter::new(1));
}
#[test]
fn rem() {
let m = Meter::new(2);
assert_eq!(m % m, Unit::new(0));
assert_eq!(m / m % m, ReciprocalMeter::new(1));
let c = Coulomb::new(4);
let a = Ampere::new(2);
assert_eq!(c % a, Second::new(0));
let w = Watt::new(2);
assert_eq!(w % a, Volt::new(0));
assert_eq!(m % 2, Meter::new(0));
}
#[test]
fn rem_assign() {
let mut m = Meter::new(2);
m %= 2;
assert_eq!(m, Meter::new(0));
}
}