use phf::phf_map;
use crate::{Phenomenon, SignificanceLevel};
pub(crate) type CodeEntry = (Phenomenon, SignificanceLevel);
pub(crate) fn parse_event<S>(code: S) -> Option<CodeEntry>
where
S: AsRef<str>,
{
let code = code.as_ref();
if code.len() != 3 {
return None;
}
lookup_threecharacter(code)
.or_else(|| lookup_twocharacter(code))
.or_else(|| lookup_onecharacter(code))
}
static CODEBOOK3: phf::Map<&'static str, CodeEntry> = phf_map! {
"EAN" => (Phenomenon::NationalEmergency, SignificanceLevel::Warning),
"NIC" => (Phenomenon::NationalInformationCenter, SignificanceLevel::Statement),
"DMO" => (Phenomenon::PracticeDemoWarning, SignificanceLevel::Warning),
"NAT" => (Phenomenon::NationalAudibleTest, SignificanceLevel::Test),
"NPT" => (Phenomenon::NationalPeriodicTest, SignificanceLevel::Test),
"NST" => (Phenomenon::NationalSilentTest, SignificanceLevel::Test),
"RMT" => (Phenomenon::RequiredMonthlyTest, SignificanceLevel::Test),
"RWT" => (Phenomenon::RequiredWeeklyTest, SignificanceLevel::Test),
"ADR" => (Phenomenon::AdministrativeMessage, SignificanceLevel::Statement),
"BLU" => (Phenomenon::BlueAlert, SignificanceLevel::Warning),
"CAE" => (Phenomenon::ChildAbduction, SignificanceLevel::Emergency),
"CDW" => (Phenomenon::CivilDanger, SignificanceLevel::Warning),
"CEM" => (Phenomenon::CivilEmergency, SignificanceLevel::Warning),
"EQW" => (Phenomenon::Earthquake, SignificanceLevel::Warning),
"EVI" => (Phenomenon::Evacuation, SignificanceLevel::Warning),
"FRW" => (Phenomenon::Fire, SignificanceLevel::Warning),
"HMW" => (Phenomenon::HazardousMaterials, SignificanceLevel::Warning),
"LAE" => (Phenomenon::LocalAreaEmergency, SignificanceLevel::Emergency),
"LEW" => (Phenomenon::LawEnforcementWarning, SignificanceLevel::Warning),
"NMN" => (Phenomenon::NetworkMessageNotification, SignificanceLevel::Statement),
"NUW" => (Phenomenon::NuclearPowerPlant, SignificanceLevel::Warning),
"RHW" => (Phenomenon::RadiologicalHazard, SignificanceLevel::Warning),
"SPW" => (Phenomenon::ShelterInPlace, SignificanceLevel::Warning),
"TOE" => (Phenomenon::TelephoneOutage, SignificanceLevel::Emergency),
"VOW" => (Phenomenon::Volcano, SignificanceLevel::Warning),
"HLS" => (Phenomenon::HurricaneLocalStatement, SignificanceLevel::Statement),
"SPS" => (Phenomenon::SpecialWeatherStatement, SignificanceLevel::Statement),
"SVR" => (Phenomenon::SevereThunderstorm, SignificanceLevel::Warning),
"SVS" => (Phenomenon::SevereWeather, SignificanceLevel::Statement),
"TOR" => (Phenomenon::Tornado, SignificanceLevel::Warning),
"FSW" => (Phenomenon::FlashFreeze, SignificanceLevel::Warning),
};
static CODEBOOK2: phf::Map<&'static str, Phenomenon> = phf_map! {
"AV" => Phenomenon::Avalanche,
"BZ" => Phenomenon::Blizzard,
"CF" => Phenomenon::CoastalFlood,
"DS" => Phenomenon::DustStorm,
"EW" => Phenomenon::ExtremeWind,
"FF" => Phenomenon::FlashFlood,
"FL" => Phenomenon::Flood,
"FZ" => Phenomenon::Freeze,
"HU" => Phenomenon::Hurricane,
"HW" => Phenomenon::HighWind,
"SM" => Phenomenon::SpecialMarine,
"SQ" => Phenomenon::SnowSquall,
"SS" => Phenomenon::StormSurge,
"SV" => Phenomenon::SevereThunderstorm,
"TO" => Phenomenon::Tornado,
"TR" => Phenomenon::TropicalStorm,
"TS" => Phenomenon::Tsunami,
"WS" => Phenomenon::WinterStorm,
};
fn lookup_threecharacter(code: &str) -> Option<CodeEntry> {
CODEBOOK3.get(code.get(0..3)?).cloned()
}
fn lookup_twocharacter(code: &str) -> Option<CodeEntry> {
let phenom = CODEBOOK2.get(code.get(0..2)?).cloned()?;
Some((phenom, code.get(2..3)?.into()))
}
fn lookup_onecharacter(code: &str) -> Option<CodeEntry> {
Some((Phenomenon::Unrecognized, code.get(2..3)?.into()))
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashSet;
use lazy_static::lazy_static;
use regex::Regex;
use strum::IntoEnumIterator;
#[test]
fn check_codebooks() {
lazy_static! {
static ref ASCII_UPPER: Regex = Regex::new(r"^[[A-Z]]{2,3}$").expect("bad test regexp");
}
let mut codebook_phenomenon = HashSet::new();
for (key, val) in CODEBOOK3.entries() {
assert!(key.is_ascii());
assert_eq!(key.len(), 3);
ASCII_UPPER.is_match(key);
assert_ne!(Phenomenon::Unrecognized, val.0);
assert_ne!(SignificanceLevel::Unknown, val.1);
codebook_phenomenon.insert(val.0);
}
for (key, val) in CODEBOOK2.entries() {
assert!(key.is_ascii());
assert_eq!(key.len(), 2);
ASCII_UPPER.is_match(key);
assert_ne!(&Phenomenon::Unrecognized, val);
codebook_phenomenon.insert(*val);
}
for phen in Phenomenon::iter() {
if phen.is_unrecognized() {
continue;
}
assert!(
codebook_phenomenon.contains(&phen),
"phenomenon {} not covered by any codebook entries",
phen
);
}
}
}