use anyhow::anyhow;
use std::{
fmt::{self, Debug, Display},
str::FromStr,
};
#[derive(PartialEq, Eq, Clone, Debug)]
pub enum Unit {
Unity,
Second,
Watt,
Joule,
Volt,
Ampere,
Hertz,
DegreeCelsius,
DegreeFahrenheit,
WattHour,
Byte,
Custom {
unique_name: String,
display_name: String,
},
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PrefixedUnit {
pub base_unit: Unit,
pub prefix: UnitPrefix,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum UnitPrefix {
Nano,
Micro,
Milli,
Plain,
Kilo,
Mega,
Giga,
}
impl Unit {
pub fn unique_name(&self) -> &str {
match self {
Unit::Unity => "1",
Unit::Second => "s",
Unit::Watt => "W",
Unit::Joule => "J",
Unit::Volt => "V",
Unit::Ampere => "A",
Unit::Hertz => "Hz",
Unit::DegreeCelsius => "Cel",
Unit::DegreeFahrenheit => "[degF]",
Unit::WattHour => "W.h",
Unit::Byte => "By",
Unit::Custom {
unique_name,
display_name: _,
} => unique_name,
}
}
fn display_name(&self) -> &str {
match self {
Unit::Unity => "",
Unit::Second => "s",
Unit::Watt => "W",
Unit::Joule => "J",
Unit::Volt => "V",
Unit::Ampere => "A",
Unit::Hertz => "Hz",
Unit::DegreeCelsius => "°C",
Unit::DegreeFahrenheit => "°F",
Unit::WattHour => "Wh",
Unit::Byte => "B",
Unit::Custom {
unique_name: _,
display_name,
} => display_name,
}
}
fn with_prefix(self, scale: UnitPrefix) -> PrefixedUnit {
PrefixedUnit {
base_unit: self,
prefix: scale,
}
}
}
impl Display for Unit {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.display_name())
}
}
impl FromStr for Unit {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let res = match s {
"1" => Unit::Unity,
"s" => Unit::Second,
"W" => Unit::Watt,
"J" => Unit::Joule,
"V" => Unit::Volt,
"A" => Unit::Ampere,
"Hz" => Unit::Hertz,
"Cel" => Unit::DegreeCelsius,
"[degF]" => Unit::DegreeFahrenheit,
"W.h" => Unit::WattHour,
"By" => Unit::Byte,
_ => return Err(anyhow!("Unknown or non standard Unit {s}")),
};
Ok(res)
}
}
impl PrefixedUnit {
pub fn milli(unit: Unit) -> PrefixedUnit {
unit.with_prefix(UnitPrefix::Milli)
}
pub fn micro(unit: Unit) -> PrefixedUnit {
unit.with_prefix(UnitPrefix::Micro)
}
pub fn nano(unit: Unit) -> PrefixedUnit {
unit.with_prefix(UnitPrefix::Nano)
}
pub fn kilo(unit: Unit) -> PrefixedUnit {
unit.with_prefix(UnitPrefix::Kilo)
}
pub fn mega(unit: Unit) -> PrefixedUnit {
unit.with_prefix(UnitPrefix::Mega)
}
pub fn giga(unit: Unit) -> PrefixedUnit {
unit.with_prefix(UnitPrefix::Giga)
}
pub fn unique_name(&self) -> String {
let prefix = match self.prefix {
UnitPrefix::Nano => "nano",
UnitPrefix::Micro => "micro",
UnitPrefix::Milli => "milli",
UnitPrefix::Plain => "",
UnitPrefix::Kilo => "kilo",
UnitPrefix::Mega => "mega",
UnitPrefix::Giga => "giga",
};
format!("{prefix}{}", self.base_unit.unique_name())
}
pub fn display_name(&self) -> String {
format!("{self}")
}
}
impl From<Unit> for PrefixedUnit {
fn from(value: Unit) -> Self {
value.with_prefix(UnitPrefix::Plain)
}
}
impl Display for PrefixedUnit {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}{}", self.prefix, self.base_unit)
}
}
impl UnitPrefix {
pub fn unique_name(&self) -> &str {
match self {
UnitPrefix::Nano => "nano",
UnitPrefix::Micro => "micro",
UnitPrefix::Milli => "milli",
UnitPrefix::Plain => "",
UnitPrefix::Kilo => "kilo",
UnitPrefix::Mega => "mega",
UnitPrefix::Giga => "giga",
}
}
pub fn display_name(&self) -> &str {
match self {
UnitPrefix::Nano => "n",
UnitPrefix::Micro => "μ",
UnitPrefix::Milli => "m",
UnitPrefix::Plain => "",
UnitPrefix::Kilo => "k",
UnitPrefix::Mega => "M",
UnitPrefix::Giga => "G",
}
}
}
impl Display for UnitPrefix {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.display_name())
}
}
impl FromStr for UnitPrefix {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let res = match s {
"nano" | "n" => UnitPrefix::Nano,
"micro" | "μ" => UnitPrefix::Micro,
"milli" | "m" => UnitPrefix::Milli,
"" => UnitPrefix::Plain,
"kilo" | "k" => UnitPrefix::Kilo,
"mega" | "M" => UnitPrefix::Mega,
"giga" | "G" => UnitPrefix::Giga,
_ => return Err(anyhow!("Unknown prefix")),
};
Ok(res)
}
}
#[cfg(test)]
mod tests {
use super::{Unit, UnitPrefix};
#[test]
fn unit_serde() {
fn parse_self(u: Unit) -> Unit {
let name = u.unique_name();
name.parse()
.unwrap_or_else(|_| panic!("failed to parse {u:?} unique name {name:?}"))
}
assert_eq!(parse_self(Unit::Unity), Unit::Unity);
assert_eq!(parse_self(Unit::Second), Unit::Second);
assert_eq!(parse_self(Unit::Watt), Unit::Watt);
assert_eq!(parse_self(Unit::Joule), Unit::Joule);
assert_eq!(parse_self(Unit::Volt), Unit::Volt);
assert_eq!(parse_self(Unit::Ampere), Unit::Ampere);
assert_eq!(parse_self(Unit::Hertz), Unit::Hertz);
assert_eq!(parse_self(Unit::DegreeCelsius), Unit::DegreeCelsius);
assert_eq!(parse_self(Unit::DegreeFahrenheit), Unit::DegreeFahrenheit);
assert_eq!(parse_self(Unit::WattHour), Unit::WattHour);
assert_eq!(parse_self(Unit::Byte), Unit::Byte);
}
#[test]
fn prefix_serde() {
fn parse_self(p: UnitPrefix) -> UnitPrefix {
let name = p.unique_name();
name.parse()
.unwrap_or_else(|_| panic!("failed to parse {p:?} unique name {name:?}"))
}
assert_eq!(parse_self(UnitPrefix::Nano), UnitPrefix::Nano);
assert_eq!(parse_self(UnitPrefix::Micro), UnitPrefix::Micro);
assert_eq!(parse_self(UnitPrefix::Milli), UnitPrefix::Milli);
assert_eq!(parse_self(UnitPrefix::Plain), UnitPrefix::Plain);
assert_eq!(parse_self(UnitPrefix::Kilo), UnitPrefix::Kilo);
assert_eq!(parse_self(UnitPrefix::Mega), UnitPrefix::Mega);
assert_eq!(parse_self(UnitPrefix::Giga), UnitPrefix::Giga);
}
}