use std::fmt;
use std::str::FromStr;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
pub enum Utility {
#[serde(rename = "Electricity")]
Electricity,
#[serde(rename = "Cold Water", alias = "DCW", alias = "Water")]
ColdWater,
#[serde(rename = "Hot Water", alias = "DHW")]
HotWater,
#[serde(rename = "Heating")]
Heating,
#[serde(rename = "Cooling")]
Cooling,
#[serde(rename = "Gas")]
Gas,
#[serde(rename = "EV")]
ElectricVehicle,
}
impl Utility {
pub const ALL: [Self; 7] = [
Self::Electricity,
Self::ColdWater,
Self::HotWater,
Self::Heating,
Self::Cooling,
Self::Gas,
Self::ElectricVehicle,
];
pub const fn as_str(self) -> &'static str {
match self {
Self::Electricity => "Electricity",
Self::ColdWater => "Cold Water",
Self::HotWater => "Hot Water",
Self::Heating => "Heating",
Self::Cooling => "Cooling",
Self::Gas => "Gas",
Self::ElectricVehicle => "EV",
}
}
}
impl fmt::Display for Utility {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
#[error("unknown utility {0:?}")]
pub struct UnknownUtility(pub String);
impl FromStr for Utility {
type Err = UnknownUtility;
fn from_str(s: &str) -> Result<Self, Self::Err> {
serde_json::from_value(serde_json::Value::String(s.to_owned()))
.map_err(|_| UnknownUtility(s.to_owned()))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_portal_name_deserializes() {
let cases = [
("Electricity", Utility::Electricity),
("Cold Water", Utility::ColdWater),
("DCW", Utility::ColdWater),
("Water", Utility::ColdWater),
("Hot Water", Utility::HotWater),
("DHW", Utility::HotWater),
("Heating", Utility::Heating),
("Cooling", Utility::Cooling),
("Gas", Utility::Gas),
("EV", Utility::ElectricVehicle),
];
for (name, expected) in cases {
let json = serde_json::to_string(name).unwrap();
assert_eq!(
serde_json::from_str::<Utility>(&json).unwrap(),
expected,
"{name}"
);
assert_eq!(name.parse::<Utility>().unwrap(), expected, "{name}");
}
}
#[test]
fn serializes_to_canonical_name_and_round_trips() {
for utility in Utility::ALL {
let json = serde_json::to_string(&utility).unwrap();
assert_eq!(json, format!("{:?}", utility.as_str()));
assert_eq!(serde_json::from_str::<Utility>(&json).unwrap(), utility);
assert_eq!(utility.to_string().parse::<Utility>().unwrap(), utility);
}
}
#[test]
fn rejects_unknown_and_rust_names() {
for name in ["ColdWater", "ElectricVehicle", "cold water", "Steam", ""] {
assert_eq!(
name.parse::<Utility>(),
Err(UnknownUtility(name.into())),
"{name}"
);
}
}
}