1use serde::Serialize;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
13#[serde(rename_all = "camelCase")]
14pub struct EngineDescriptor {
15 pub key: &'static str,
18 pub label: &'static str,
20 pub pill: &'static str,
22 pub accent: &'static str,
24 pub summary: &'static str,
26}
27
28pub const ENGINES: &[EngineDescriptor] = &[EngineDescriptor {
31 key: "wds",
32 label: "Water Distribution",
33 pill: "WD",
34 accent: "#4a90d9",
35 summary: "Pressurized water distribution network simulation — hydraulics, \
36 water quality, and energy on the EPANET data model.",
37}];
38
39#[derive(Debug, Clone, PartialEq, Eq)]
45pub struct UnknownEngineError {
46 pub key: String,
48}
49
50impl std::fmt::Display for UnknownEngineError {
51 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52 write!(f, "unknown engine key: {:?}", self.key)
53 }
54}
55
56impl std::error::Error for UnknownEngineError {}
57
58pub fn engine_by_key(key: &str) -> Result<&'static EngineDescriptor, UnknownEngineError> {
60 ENGINES
61 .iter()
62 .find(|e| e.key == key)
63 .ok_or_else(|| UnknownEngineError { key: key.into() })
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69
70 #[test]
71 fn registry_contains_wds_first() {
72 assert_eq!(ENGINES[0].key, "wds");
73 assert_eq!(ENGINES[0].label, "Water Distribution");
74 assert_eq!(ENGINES[0].pill, "WD");
75 }
76
77 #[test]
78 fn descriptor_field_invariants_hold_for_every_engine() {
79 for e in ENGINES {
80 assert!(
81 e.key.chars().all(|c| c.is_ascii_lowercase()),
82 "key {:?} must be lowercase ASCII",
83 e.key
84 );
85 assert_eq!(
86 e.pill.chars().count(),
87 2,
88 "pill {:?} must be 2 chars",
89 e.pill
90 );
91 assert!(e.pill.chars().all(|c| c.is_ascii_uppercase()));
92 assert!(
93 e.accent.len() == 7 && e.accent.starts_with('#'),
94 "accent {:?} must be #rrggbb",
95 e.accent
96 );
97 assert!(!e.summary.is_empty());
98 }
99 }
100
101 #[test]
102 fn keys_are_unique() {
103 let mut keys: Vec<_> = ENGINES.iter().map(|e| e.key).collect();
104 keys.sort_unstable();
105 keys.dedup();
106 assert_eq!(keys.len(), ENGINES.len());
107 }
108
109 #[test]
110 fn lookup_resolves_and_rejects() {
111 assert_eq!(engine_by_key("wds").unwrap().pill, "WD");
112 let err = engine_by_key("och").unwrap_err();
113 assert_eq!(err.key, "och");
114 assert!(err.to_string().contains("och"));
115 }
116}