#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum LoadProfile {
H0,
G0,
G1,
G2,
G3,
G4,
G5,
G6,
L0,
L1,
L2,
P0,
GasEF,
GasMF,
GasGHD,
Custom,
}
impl LoadProfile {
#[must_use]
pub fn as_str(self) -> &'static str {
match self {
Self::H0 => "H0",
Self::G0 => "G0",
Self::G1 => "G1",
Self::G2 => "G2",
Self::G3 => "G3",
Self::G4 => "G4",
Self::G5 => "G5",
Self::G6 => "G6",
Self::L0 => "L0",
Self::L1 => "L1",
Self::L2 => "L2",
Self::P0 => "P0",
Self::GasEF => "EF",
Self::GasMF => "MF",
Self::GasGHD => "GHD",
Self::Custom => "CUSTOM",
}
}
#[must_use]
pub fn parse(s: &str) -> Option<Self> {
match s.to_uppercase().as_str() {
"H0" => Some(Self::H0),
"G0" => Some(Self::G0),
"G1" => Some(Self::G1),
"G2" => Some(Self::G2),
"G3" => Some(Self::G3),
"G4" => Some(Self::G4),
"G5" => Some(Self::G5),
"G6" => Some(Self::G6),
"L0" => Some(Self::L0),
"L1" => Some(Self::L1),
"L2" => Some(Self::L2),
"P0" => Some(Self::P0),
"EF" => Some(Self::GasEF),
"MF" => Some(Self::GasMF),
"GHD" => Some(Self::GasGHD),
_ => None,
}
}
#[must_use]
pub fn is_residential(self) -> bool {
matches!(self, Self::H0 | Self::GasEF | Self::GasMF)
}
#[must_use]
pub fn is_commercial(self) -> bool {
matches!(
self,
Self::G0
| Self::G1
| Self::G2
| Self::G3
| Self::G4
| Self::G5
| Self::G6
| Self::GasGHD
)
}
#[must_use]
pub fn is_agricultural(self) -> bool {
matches!(self, Self::L0 | Self::L1 | Self::L2)
}
#[must_use]
pub fn is_gas(self) -> bool {
matches!(self, Self::GasEF | Self::GasMF | Self::GasGHD)
}
}
impl std::fmt::Display for LoadProfile {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl std::str::FromStr for LoadProfile {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
LoadProfile::parse(s).ok_or_else(|| format!("unknown load profile: {s:?}"))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn round_trip_all_profiles() {
let profiles = [
LoadProfile::H0,
LoadProfile::G0,
LoadProfile::G1,
LoadProfile::G2,
LoadProfile::G3,
LoadProfile::G4,
LoadProfile::G5,
LoadProfile::G6,
LoadProfile::L0,
LoadProfile::L1,
LoadProfile::L2,
LoadProfile::P0,
LoadProfile::GasEF,
LoadProfile::GasMF,
LoadProfile::GasGHD,
];
for p in &profiles {
let s = p.as_str();
let parsed = LoadProfile::parse(s).expect("should round-trip");
assert_eq!(*p, parsed, "round-trip failed for {s}");
}
}
#[test]
fn residential_classification() {
assert!(LoadProfile::H0.is_residential());
assert!(LoadProfile::GasEF.is_residential());
assert!(!LoadProfile::G0.is_residential());
}
#[test]
fn commercial_classification() {
for p in [
LoadProfile::G0,
LoadProfile::G1,
LoadProfile::G2,
LoadProfile::G3,
LoadProfile::G4,
LoadProfile::G5,
LoadProfile::G6,
] {
assert!(p.is_commercial(), "{p} should be commercial");
}
assert!(!LoadProfile::H0.is_commercial());
}
#[test]
fn gas_classification() {
assert!(LoadProfile::GasEF.is_gas());
assert!(LoadProfile::GasMF.is_gas());
assert!(LoadProfile::GasGHD.is_gas());
assert!(!LoadProfile::H0.is_gas());
}
#[test]
fn case_insensitive_parse() {
assert_eq!(LoadProfile::parse("h0"), Some(LoadProfile::H0));
assert_eq!(LoadProfile::parse("H0"), Some(LoadProfile::H0));
assert_eq!(LoadProfile::parse("ef"), Some(LoadProfile::GasEF));
}
#[test]
fn unknown_profile_returns_none() {
assert_eq!(LoadProfile::parse("X9"), None);
assert_eq!(LoadProfile::parse(""), None);
}
}