pub trait IsPlural: PartialEq {
fn is_singular(&self) -> bool;
fn is_plural(&self) -> bool {
!self.is_singular()
}
}
macro_rules ! impl_is_singular {
($($ty:ty: [$pos:literal$(, $neg:literal)?]),*) => {
$(
impl IsPlural for $ty {
fn is_singular(&self) -> bool {
if self == &$pos {
true
}
$(
else if self == &$neg {
true
}
)?
else {
false
}
}
}
)*
}
}
impl_is_singular!(
u8: [1],
u16: [1],
u32: [1],
u64: [1],
u128: [1],
i8: [1, -1],
i16: [1, -1],
i32: [1, -1],
i64: [1, -1],
i128: [1, -1],
f32: [1.0, -1.0],
f64: [1.0, -1.0]
);
macro_rules ! unit {
{
$($name:ident = [$value:literal, $short:expr, $singular:literal, $plural:literal],)*
} => {
#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(u8)]
pub enum Unit {
$($name = $value,)*
Unknown(u8),
}
impl Unit {
fn short_display(&self) -> Option<&str> {
match self {
$(Self::$name => $short,)*
Self::Unknown(_) => None,
}
}
fn plural_display(&self) -> &str {
match self {
$(Self::$name => $plural,)*
Self::Unknown(_) => "Unknown",
}
}
fn singular_display(&self) -> &str {
match self {
$(Self::$name => $singular,)*
Self::Unknown(_) => "Unknown",
}
}
fn display_str(&self, short: bool, plural: bool) -> &str
{
let short_value = if short {
self.short_display()
} else {
None
};
if let Some(short_value) = short_value {
short_value
} else {
if plural {
self.plural_display()
} else {
self.singular_display()
}
}
}
pub fn display<T>(&self, short: bool, value: T) -> String
where T: IsPlural + core::fmt::Display,
{
format!("{:.2} {}", value, self.display_str(short, value.is_plural()))
}
}
impl From<u8> for Unit {
fn from(value: u8) -> Self {
match value {
$($value => Self::$name,)*
_ => Self::Unknown(value),
}
}
}
impl From<Unit> for u8 {
fn from(value: Unit) -> Self {
match value {
$(Unit::$name => $value,)*
Unit::Unknown(v) => v,
}
}
}
}
}
unit! {
Unspecified = [0, None, "Unspecified", "Unspecified"],
DegreesCelcius = [1, Some("°C"), "Degree Celcius", "Degrees Celcius"],
DegreesFahrenheit = [2, Some("°F"), "Degree Fahrenheit", "Degrees Fahrenheit"],
DegreesKelvin = [3, Some("°K"), "Degree Kelvin", "Degrees Kelvin"],
Volt = [4, Some("V"), "Volt", "Volts"],
Amp = [5, Some("A"), "Ampere", "Amperes"],
Watt = [6, Some("W"), "Watt", "Watts"],
Joule = [7, Some("J"), "Joule", "Joules"],
Coulomb = [8, Some("C"), "Coulomb", "Coulombs"],
VoltAmpere = [9, Some("VA"), "Volt-Ampere", "Volt-Amperes"],
Nit = [10, None, "Nit", "Nits"],
Lumen = [11, Some("lm"), "Lumen", "Lumens"],
Lux = [12, Some("lx"), "Lux", "Lux"],
Candela = [13, Some("cd"), "Candela", "Candelas"],
KiloPascal = [14, Some("kPa"), "Kilopascal", "Kilopascal"],
PoundsPerSquareInch = [15, Some("psi"), "Pound per square inch", "Pounds per square inch"],
Newton = [16, Some("N"), "Newton", "Newton"],
CubicFeetPerMinute = [17, Some("cfm"), "Cubic Foot per Minute", "Cubic Feet per minute"],
RevolutionsPerMinute = [18, Some("rpm"), "Revolution per Minute", "Revolutions per Minute"],
Hertz = [19, Some("hz"), "Hertz", "Hertz"],
Microsecond = [20, Some("µs"), "Microsecond", "Microseconds"],
Millisecond = [21, Some("ms"), "Millisecond", "Milliseconds"],
Second = [22, Some("s"), "Second", "Seconds"],
Minute = [23, Some("min"), "Minute", "Minutes"],
Hour = [24, Some("h"), "Hour", "Hours"],
Day = [25, Some("d"), "Day", "Days"],
Week = [26, Some("w"), "Week", "Weeks"],
Mil = [27, Some("mil"), "mil", "mils"],
Inch = [28, Some("in"), "Inch", "Inches"],
Foot = [29, Some("ft"), "Foot", "Feet"],
CubicInch = [30, Some("cu in"), "Cubic Inch", "Cubic Inches"],
CubicFoot = [31, Some("cu ft"), "Cubic Foot", "Cubic Feet"],
Millimeter = [32, Some("mm"), "Millimeter", "Millimeters"],
Centimeter = [33, Some("cm"), "Centimeter", "Centimeters"],
Meter = [34, Some("m"), "Meter", "Meters"],
CubicCentimeter = [35, Some("cu cm"), "Cubic Centimeter", "Cubic Centimeters"],
CubicMeter = [36, Some("cu m"), "Cubic Meter", "Cubic Meters"],
Liter = [37, Some("l"), "Liter", "Liters"],
FluidOunce = [38, Some("fl oz"), "Fluid Ounce", "Fluid Ounces"],
Radian = [39, Some("rad"), "Radian", "Radians"],
Steradian = [40, Some("sr"), "Steradian", "Steradians"],
Revolution = [41, Some("rev"), "Revolution", "Revolutions"],
Cycle = [42, None, "Cycle", "Cycles"],
Gravity = [43, Some("Gs (grav)"), "Gravity", "Gravities"],
Ounce = [44, Some("oz"), "Ounce", "Ounces"],
Pound = [45, Some("lb"), "Pound", "Pounds"],
FootPound = [46, Some("ft lb"), "Foot Pound", "Foot Pounds"],
OunceInch = [47, Some("oz in"), "Ounce Inch", "Ounce Inches"],
Gauss = [48, Some("Gs"), "Gauss", "Gauss"],
Gilbert = [49, Some("Gb"), "Gilbert", "Gilbert"],
Henry = [50, Some("H"), "Heny", "Heny"],
Millihenry = [51, Some("mH"), "Millihenry", "Millihenry"],
Farad = [52, Some("F"), "Farad", "Farad"],
Microfarad = [53, Some("µF"), "Microfarad", "Microfarad"],
Ohm = [54, Some("Ω"), "Ohm", "Ohm"],
Siemens = [55, Some("S"), "Siemens", "Siemens"],
Mole = [56, Some("mol"), "Mol", "Mol"],
Becquerel = [57, Some("Bq"), "Becquerel", "Becquerel"],
PartsPerMillion = [58, Some("ppm"), "Part part per million", "Parts per million"],
Decibel = [60, Some("dB"), "Decibel", "Decibels"],
AWeightedDecibel = [61, Some("dBa"), "A weighted Decibel", "A weighted Decibels"],
CWeightedDecibel = [62, Some("dBc"), "C weighted Decibel", "C weighted Decibels"],
Gray = [63, Some("Gy"), "Gray", "Gray"],
Sievert = [64, Some("Sv"), "Sievert", "Sievert"],
ColorTemperatureDegreesKelvin = [65, Some("°K (Color)"), "Degree Kelvin (Color)", "Degrees Kelvin (Color)"],
Bit = [66, Some("b"), "Bit", "Bits"],
Kilobit = [67, Some("kb"), "Kilobit", "Kilobits"],
Megabit = [68, Some("mb"), "Megabit", "Megabits"],
Gigabit = [69, Some("Gb"), "Gigabit", "Gigabits"],
Byte = [70, Some("B"), "Byte", "Bytes"],
Kilobyte = [71, Some("KB"), "Kilobyte", "Kilobytes"],
Megabyte = [72, Some("MB"), "Megabyte", "Megabytes"],
Gigabyte = [73, Some("GB"), "Gigabyte", "Gigabytes"],
Word = [74, None, "Word", "Words"],
DoubleWord = [75, None, "Double word", "Double words"],
QuadWord = [76, None, "Quad word", "Quad words"],
CacheLine = [77, None, "Cache line", "Cache lines"],
Hit = [78, None, "Hit", "Hits"],
Miss = [79, None, "Miss", "Misses"],
Retry = [80, None, "Retry", "Retries"],
Reset = [81, None, "Reset", "Resets"],
OverrunOrUnderflow = [82, None, "Overrun or Underflow", "Overruns or Underflows"],
Underrun = [83, None, "Underrun", "Underruns"],
Collision = [84, None, "Collision", "Collisions"],
Packet = [85, None, "Packet", "Packets"],
Message = [86, None, "Message", "Messges"],
Character = [87, None, "Character", "Characters"],
Error = [88, None, "Error", "Errors"],
CorrectableError = [89, None, "Correctable Error", "Correctable Errors"],
UncorrectableError = [90, None, "Uncorrectable Error", "Uncorrectable Errors"],
FatalError = [91, None, "Fatal Error", "Fatal Errors"],
Gram = [92, Some("gr"), "Gram", "Grams"],
}
#[test]
fn display_tests() {
use Unit::*;
assert_eq!("32 °C", DegreesCelcius.display(true, 32));
assert_eq!("32 Degrees Celcius", DegreesCelcius.display(false, 32));
assert_eq!("1 Degree Celcius", DegreesCelcius.display(false, 1));
assert_eq!("-1 Nit", Nit.display(true, -1));
assert_eq!("-1 Nit", Nit.display(false, -1));
assert_eq!("-15 Nits", Nit.display(false, -15));
}