use serde::Serialize;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct EngineDescriptor {
pub key: &'static str,
pub label: &'static str,
pub pill: &'static str,
pub accent: &'static str,
pub summary: &'static str,
}
pub const ENGINES: &[EngineDescriptor] = &[EngineDescriptor {
key: "wds",
label: "Water Distribution",
pill: "WD",
accent: "#4a90d9",
summary: "Pressurized water distribution network simulation — hydraulics, \
water quality, and energy on the EPANET data model.",
}];
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UnknownEngineError {
pub key: String,
}
impl std::fmt::Display for UnknownEngineError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "unknown engine key: {:?}", self.key)
}
}
impl std::error::Error for UnknownEngineError {}
pub fn engine_by_key(key: &str) -> Result<&'static EngineDescriptor, UnknownEngineError> {
ENGINES
.iter()
.find(|e| e.key == key)
.ok_or_else(|| UnknownEngineError { key: key.into() })
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn registry_contains_wds_first() {
assert_eq!(ENGINES[0].key, "wds");
assert_eq!(ENGINES[0].label, "Water Distribution");
assert_eq!(ENGINES[0].pill, "WD");
}
#[test]
fn descriptor_field_invariants_hold_for_every_engine() {
for e in ENGINES {
assert!(
e.key.chars().all(|c| c.is_ascii_lowercase()),
"key {:?} must be lowercase ASCII",
e.key
);
assert_eq!(
e.pill.chars().count(),
2,
"pill {:?} must be 2 chars",
e.pill
);
assert!(e.pill.chars().all(|c| c.is_ascii_uppercase()));
assert!(
e.accent.len() == 7 && e.accent.starts_with('#'),
"accent {:?} must be #rrggbb",
e.accent
);
assert!(!e.summary.is_empty());
}
}
#[test]
fn keys_are_unique() {
let mut keys: Vec<_> = ENGINES.iter().map(|e| e.key).collect();
keys.sort_unstable();
keys.dedup();
assert_eq!(keys.len(), ENGINES.len());
}
#[test]
fn lookup_resolves_and_rejects() {
assert_eq!(engine_by_key("wds").unwrap().pill, "WD");
let err = engine_by_key("och").unwrap_err();
assert_eq!(err.key, "och");
assert!(err.to_string().contains("och"));
}
}