use std::fmt;
use crate::eventcodes::{parse_event, CodeEntry};
use super::phenomenon::Phenomenon;
use super::significance::SignificanceLevel;
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub struct EventCode {
phenomenon: Phenomenon,
significance: SignificanceLevel,
}
impl EventCode {
pub fn from<S>(code: S) -> Self
where
S: AsRef<str>,
{
parse_event(code).unwrap_or_default().into()
}
pub fn phenomenon(&self) -> Phenomenon {
self.phenomenon
}
pub fn significance(&self) -> SignificanceLevel {
self.significance
}
pub fn to_display_string(&self) -> String {
self.to_string()
}
pub fn is_test(&self) -> bool {
self.significance() == SignificanceLevel::Test || self.phenomenon().is_test()
}
pub fn is_unrecognized(&self) -> bool {
self.phenomenon == Phenomenon::Unrecognized
|| self.significance == SignificanceLevel::Unknown
}
pub(crate) const fn unrecognized() -> Self {
Self {
phenomenon: Phenomenon::Unrecognized,
significance: SignificanceLevel::Unknown,
}
}
}
impl Default for EventCode {
fn default() -> Self {
Self::unrecognized()
}
}
impl From<&str> for EventCode {
fn from(value: &str) -> Self {
EventCode::from(value)
}
}
impl From<&String> for EventCode {
fn from(value: &String) -> Self {
EventCode::from(value)
}
}
impl fmt::Display for EventCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
self.phenomenon().fmt(f)
} else {
let phenom_pattern = self.phenomenon().as_full_pattern_str();
if let Some(phenom_need_sig) = phenom_pattern.strip_suffix("%") {
write!(f, "{}{}", phenom_need_sig, self.significance)
} else {
phenom_pattern.fmt(f)
}
}
}
}
impl Ord for EventCode {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.significance().cmp(&other.significance())
}
}
impl PartialOrd for EventCode {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl From<CodeEntry> for EventCode {
fn from(value: CodeEntry) -> Self {
Self {
phenomenon: value.0,
significance: value.1,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_unrecognized() {
assert_eq!(EventCode::from(""), EventCode::default());
assert_eq!(EventCode::default(), EventCode::unrecognized());
}
#[test]
fn basic_parsing() {
let unk = EventCode::from("");
assert_eq!(unk, EventCode::default());
assert_eq!(Phenomenon::Unrecognized, unk.phenomenon());
assert_eq!(SignificanceLevel::Unknown, unk.significance());
let code_tor = EventCode::from("TOR");
assert_eq!(Phenomenon::Tornado, code_tor.phenomenon());
assert_eq!(SignificanceLevel::Warning, code_tor.significance());
let code_toe = EventCode::from("TOE");
assert_eq!(Phenomenon::TelephoneOutage, code_toe.phenomenon());
assert_eq!(SignificanceLevel::Emergency, code_toe.significance());
let code_toa = EventCode::from("TOA");
assert_eq!(Phenomenon::Tornado, code_toa.phenomenon());
assert_eq!(SignificanceLevel::Watch, code_toa.significance());
let code_tow = EventCode::from("TOW");
assert_eq!(Phenomenon::Tornado, code_tow.phenomenon());
assert_eq!(SignificanceLevel::Warning, code_tow.significance());
assert_eq!(EventCode::from("TORZ"), EventCode::default());
let code_dew = EventCode::from("DEW");
assert_eq!(Phenomenon::Unrecognized, code_dew.phenomenon());
assert_eq!(SignificanceLevel::Warning, code_dew.significance());
let code_bz = EventCode::from("BZ!");
assert_eq!(Phenomenon::Blizzard, code_bz.phenomenon());
assert_eq!(SignificanceLevel::Unknown, code_bz.significance());
}
#[test]
fn basic_display() {
let evt = EventCode::from("EAN");
assert_eq!("National Emergency Message", evt.to_string());
let evt = EventCode::from("TOR");
assert_eq!("Tornado Warning", evt.to_string());
let evt = EventCode::from("BZW");
assert_eq!("Blizzard Warning", evt.to_string());
let evt = EventCode::from("BZS");
assert_eq!("Blizzard Statement", evt.to_string());
let evt = EventCode::from("TOE");
assert_eq!("911 Telephone Outage", &format!("{:#}", evt));
assert_eq!("911 Telephone Outage Emergency", &format!("{}", evt));
let evt = EventCode::from("EVI");
assert_eq!("Evacuation", &format!("{:#}", evt));
assert_eq!("Evacuation Immediate", &format!("{}", evt));
let evt = EventCode::from("!!!");
assert_eq!("Unrecognized Warning", &format!("{}", evt));
assert_eq!("Unrecognized", &format!("{:#}", evt));
}
#[test]
fn test_support_required_codes() {
const TEST_CODES: &[&str] = &[
"ADR", "AVA", "AVW", "BLU", "BZW", "CAE", "CDW", "CEM", "CFA", "CFW", "DMO", "DSW",
"EAN", "EQW", "EVI", "EWW", "FFA", "FFS", "FFW", "FLA", "FLS", "FLW", "FRW", "FSW",
"FZW", "HLS", "HMW", "HUA", "HUW", "HWA", "HWW", "LAE", "LEW", "NAT", "NIC", "NMN",
"NPT", "NST", "NUW", "RHW", "RMT", "RWT", "SMW", "SPS", "SPW", "SQW", "SSA", "SSW",
"SVA", "SVR", "SVS", "TOA", "TOE", "TOR", "TRA", "TRW", "TSA", "TSW", "VOW", "WSA",
"WSW",
];
for code in TEST_CODES.iter().cloned() {
let evt = EventCode::from(code);
assert!(
evt.phenomenon().is_recognized(),
"event code {} was not recognized",
code
);
assert!(
evt.significance() != SignificanceLevel::Unknown,
"event code {} has unknown significance; must be known",
code
);
let disp = format!("{}", evt);
assert!(!disp.contains("%"));
if evt.phenomenon().is_test() {
assert_eq!(evt.significance(), SignificanceLevel::Test);
}
}
}
}