use std::borrow::Cow;
use std::fmt;
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum PhysicalQuantity {
Concentration {
component: usize,
},
Temperature,
Pressure,
Velocity {
component: usize,
},
Custom {
name: Cow<'static, str>,
component: usize,
},
}
impl PhysicalQuantity {
#[inline]
pub fn concentration() -> Self {
Self::Concentration { component: 0 }
}
#[inline]
pub fn temperature() -> Self {
Self::Temperature
}
#[inline]
pub fn pressure() -> Self {
Self::Pressure
}
#[inline]
pub fn velocity() -> Self {
Self::Velocity { component: 0 }
}
#[inline]
pub fn custom(name: impl Into<Cow<'static, str>>) -> Self {
Self::Custom {
name: name.into(),
component: 0,
}
}
pub fn component(&self) -> usize {
match self {
Self::Concentration { component } => *component,
Self::Temperature => 0,
Self::Pressure => 0,
Self::Velocity { component } => *component,
Self::Custom { component, .. } => *component,
}
}
pub fn kind_str(&self) -> &str {
match self {
Self::Concentration { .. } => "Concentration",
Self::Temperature => "Temperature",
Self::Pressure => "Pressure",
Self::Velocity { .. } => "Velocity",
Self::Custom { name, .. } => name.as_ref(),
}
}
}
impl fmt::Display for PhysicalQuantity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Concentration { component } => write!(f, "Concentration({component})"),
Self::Temperature => write!(f, "Temperature"),
Self::Pressure => write!(f, "Pressure"),
Self::Velocity { component } => write!(f, "Velocity({component})"),
Self::Custom { name, component } => write!(f, "{name}({component})"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashMap;
#[test]
fn concentration_default_is_component_zero() {
assert_eq!(
PhysicalQuantity::concentration(),
PhysicalQuantity::Concentration { component: 0 }
);
}
#[test]
fn temperature_equals_temperature() {
assert_eq!(
PhysicalQuantity::temperature(),
PhysicalQuantity::Temperature
);
}
#[test]
fn pressure_equals_pressure() {
assert_eq!(PhysicalQuantity::pressure(), PhysicalQuantity::Pressure);
}
#[test]
fn velocity_default_is_component_zero() {
assert_eq!(
PhysicalQuantity::velocity(),
PhysicalQuantity::Velocity { component: 0 }
);
}
#[test]
fn custom_default_is_component_zero() {
assert_eq!(
PhysicalQuantity::custom("WaterDepth"),
PhysicalQuantity::Custom {
name: "WaterDepth".into(),
component: 0,
}
);
}
#[test]
fn different_components_are_not_equal() {
let c0 = PhysicalQuantity::Concentration { component: 0 };
let c1 = PhysicalQuantity::Concentration { component: 1 };
assert_ne!(c0, c1);
}
#[test]
fn different_kinds_are_not_equal() {
assert_ne!(
PhysicalQuantity::concentration(),
PhysicalQuantity::temperature()
);
assert_ne!(
PhysicalQuantity::concentration(),
PhysicalQuantity::pressure()
);
assert_ne!(
PhysicalQuantity::concentration(),
PhysicalQuantity::velocity()
);
}
#[test]
fn custom_different_names_not_equal() {
assert_ne!(
PhysicalQuantity::custom("WaterDepth"),
PhysicalQuantity::custom("MagneticFlux")
);
}
#[test]
fn custom_same_name_different_component_not_equal() {
let a = PhysicalQuantity::Custom {
name: "B".into(),
component: 0,
};
let b = PhysicalQuantity::Custom {
name: "B".into(),
component: 1,
};
assert_ne!(a, b);
}
#[test]
fn component_returns_correct_index() {
assert_eq!(
PhysicalQuantity::Concentration { component: 2 }.component(),
2
);
assert_eq!(PhysicalQuantity::Temperature.component(), 0);
assert_eq!(PhysicalQuantity::Pressure.component(), 0);
assert_eq!(PhysicalQuantity::Velocity { component: 1 }.component(), 1);
assert_eq!(
PhysicalQuantity::Custom {
name: "B".into(),
component: 3
}
.component(),
3
);
}
#[test]
fn kind_str_standard_variants() {
assert_eq!(
PhysicalQuantity::concentration().kind_str(),
"Concentration"
);
assert_eq!(PhysicalQuantity::temperature().kind_str(), "Temperature");
assert_eq!(PhysicalQuantity::pressure().kind_str(), "Pressure");
assert_eq!(PhysicalQuantity::velocity().kind_str(), "Velocity");
}
#[test]
fn kind_str_custom_returns_name() {
assert_eq!(
PhysicalQuantity::custom("WaterDepth").kind_str(),
"WaterDepth"
);
}
#[test]
fn display_concentration() {
assert_eq!(
format!("{}", PhysicalQuantity::Concentration { component: 1 }),
"Concentration(1)"
);
}
#[test]
fn display_temperature_and_pressure() {
assert_eq!(format!("{}", PhysicalQuantity::Temperature), "Temperature");
assert_eq!(format!("{}", PhysicalQuantity::Pressure), "Pressure");
}
#[test]
fn display_velocity() {
assert_eq!(
format!("{}", PhysicalQuantity::Velocity { component: 0 }),
"Velocity(0)"
);
}
#[test]
fn display_custom() {
assert_eq!(
format!(
"{}",
PhysicalQuantity::Custom {
name: "WaterDepth".into(),
component: 0
}
),
"WaterDepth(0)"
);
}
#[test]
fn usable_as_hashmap_key() {
let mut map: HashMap<PhysicalQuantity, f64> = HashMap::new();
map.insert(PhysicalQuantity::concentration(), 1.0);
map.insert(PhysicalQuantity::Concentration { component: 1 }, 2.0);
map.insert(PhysicalQuantity::temperature(), 298.15);
map.insert(PhysicalQuantity::custom("WaterDepth"), 0.5);
assert_eq!(map[&PhysicalQuantity::concentration()], 1.0);
assert_eq!(map[&PhysicalQuantity::Concentration { component: 1 }], 2.0);
assert_eq!(map[&PhysicalQuantity::temperature()], 298.15);
assert_eq!(map[&PhysicalQuantity::custom("WaterDepth")], 0.5);
}
#[test]
fn three_component_scenario() {
let mut map: HashMap<PhysicalQuantity, &str> = HashMap::new();
map.insert(PhysicalQuantity::Concentration { component: 0 }, "Malic");
map.insert(PhysicalQuantity::Concentration { component: 1 }, "Citric");
map.insert(PhysicalQuantity::Concentration { component: 2 }, "Tartaric");
assert_eq!(
map[&PhysicalQuantity::Concentration { component: 0 }],
"Malic"
);
assert_eq!(
map[&PhysicalQuantity::Concentration { component: 1 }],
"Citric"
);
assert_eq!(
map[&PhysicalQuantity::Concentration { component: 2 }],
"Tartaric"
);
assert_eq!(map.len(), 3);
}
#[test]
fn clone_preserves_equality() {
let variants = [
PhysicalQuantity::Concentration { component: 0 },
PhysicalQuantity::Temperature,
PhysicalQuantity::Pressure,
PhysicalQuantity::Velocity { component: 1 },
PhysicalQuantity::Custom {
name: "B".into(),
component: 0,
},
];
for v in &variants {
assert_eq!(v.clone(), *v);
}
}
}