use num_enum::{IntoPrimitive, TryFromPrimitive};
use crate::Ident;
#[derive(Clone, Copy, Debug, PartialEq, IntoPrimitive, TryFromPrimitive)]
#[repr(u32)]
#[non_exhaustive]
pub enum KnownUnit {
None = 0x0,
Centimeter = 0x1_1,
Radian = 0x1_2,
Inch = 0x1_3,
Degree = 0x1_4,
Gram = 0x1_0_1,
Seconds = 0x1_0_0_1,
#[doc(hidden)]
_SecondsSystem2 = 0x1_0_0_2,
#[doc(hidden)]
_SecondsSystem3 = 0x1_0_0_3,
#[doc(hidden)]
_SecondsSystem4 = 0x1_0_0_4,
Hertz = 0xF_0_0_1,
SiVelocity = 0xF_0_1_1,
SiMomentum = 0xF_1_1_1,
SiAcceleration = 0xE_0_1_1,
SiForce = 0xE_1_1_1,
SiEnergy = 0xE_1_2_1,
SiAngularVelocity = 0xF_0_1_2,
DegAngularVelocity = 0xF_0_1_4,
SiAngularAcceleration = 0xE_0_1_2,
DegAngularAcceleration = 0xE_0_1_4,
Kelvin = 0x1_0_0_0_1,
DegreesFahrenheit = 0x1_0_0_0_3,
Current = 0x1_0_0_0_0_1,
AmpSeconds = 0x1_0_1_0_0_1,
Voltage = 0xF_0_D_1_2_1,
SiPower = 0xD_1_2_1,
}
pub type Unit = Ident<KnownUnit, u32>;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct UnitExponent(pub i32);
impl core::ops::Deref for UnitExponent {
type Target = i32;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<i32> for UnitExponent {
fn from(value: i32) -> Self {
UnitExponent(value)
}
}
impl core::ops::DerefMut for UnitExponent {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl UnitExponent {
pub fn raw_value(self) -> i32 {
self.0
}
pub fn remap_as_i4(self) -> i32 {
let upper_bits = (self.0 as u32) & 0xFFFF_FFF0;
if upper_bits == 0 {
self.0 << 28 >> 28
} else {
self.0
}
}
}
#[cfg(feature = "std")]
mod std_impls {
use std::fmt::{self, Display};
use super::*;
impl Display for UnitExponent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let raw = self.raw_value();
let remap = self.remap_as_i4();
if remap == raw {
write!(f, "{raw}")
} else {
write!(f, "{raw} (possibly intended {remap})")
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_i4() {
assert_eq!(UnitExponent(0).remap_as_i4(), 0);
assert_eq!(UnitExponent(1).remap_as_i4(), 1);
assert_eq!(UnitExponent(7).remap_as_i4(), 7);
assert_eq!(UnitExponent(-1).remap_as_i4(), -1);
assert_eq!(UnitExponent(-8).remap_as_i4(), -8);
assert_eq!(UnitExponent(-9).remap_as_i4(), -9);
assert_eq!(UnitExponent(8).remap_as_i4(), -8);
assert_eq!(UnitExponent(15).remap_as_i4(), -1);
assert_eq!(UnitExponent(16).remap_as_i4(), 16);
assert_eq!(UnitExponent(-16).remap_as_i4(), -16);
assert_eq!(UnitExponent(i32::MAX).remap_as_i4(), i32::MAX);
assert_eq!(UnitExponent(i32::MIN).remap_as_i4(), i32::MIN);
}
}