sameplace/message/originator.rs
1//! Originator code
2
3use std::fmt;
4
5use strum::EnumMessage;
6
7/// SAME message originator code
8///
9/// See [`MessageHeader::originator()`](crate::MessageHeader::originator).
10/// Originator codes may be also parsed from the SAME
11/// [org code and callsign](Originator::from_org_and_call):
12///
13/// ```
14/// use sameplace::Originator;
15///
16/// let orig = Originator::from_org_and_call("WXR", "KLOX/NWS");
17/// assert_eq!(Originator::NationalWeatherService, orig);
18///
19/// // other originators
20/// assert_eq!(Originator::Unknown, Originator::from_org_and_call("HUH", ""));
21/// assert_eq!("CIV", Originator::CivilAuthority.as_code_str());
22/// ```
23///
24/// Originators Display a human-readable string:
25///
26/// ```
27/// # use sameplace::Originator;
28/// # let orig = Originator::from_org_and_call("WXR", "KLOX/NWS");
29/// assert_eq!("National Weather Service", orig.as_display_str());
30/// assert_eq!("National Weather Service", &format!("{}", orig));
31/// assert_eq!("WXR", orig.as_ref());
32/// assert_eq!("WXR", &format!("{:#}", orig));
33/// ```
34///
35/// The callsign is required to reliably detect the National Weather Service
36/// and/or Environment Canada:
37///
38/// ```
39/// # use sameplace::Originator;
40/// assert_eq!(Originator::EnvironmentCanada,
41/// Originator::from_org_and_call("WXR", "EC/GC/CA"));
42/// assert_eq!("WXR", Originator::EnvironmentCanada.as_code_str());
43/// ```
44#[derive(
45 Clone, Copy, Debug, PartialEq, Eq, Hash, strum_macros::EnumMessage, strum_macros::EnumString,
46)]
47pub enum Originator {
48 /// An unknown (and probably invalid) Originator code
49 ///
50 /// Per NWSI 10-172, receivers should accept any originator code.
51 #[strum(serialize = "", detailed_message = "Unknown Originator")]
52 Unknown,
53
54 /// Primary Entry Point station for national activations
55 ///
56 /// Nation-wide activations are authorized by the President of
57 /// the United States. Takes priority over all other
58 /// messages/station programming.
59 #[strum(serialize = "PEP", detailed_message = "Primary Entry Point System")]
60 PrimaryEntryPoint,
61
62 /// Civil authorities
63 #[strum(serialize = "CIV", detailed_message = "Civil authorities")]
64 CivilAuthority,
65
66 /// National Weather Service
67 #[strum(serialize = "WXR", detailed_message = "National Weather Service")]
68 NationalWeatherService,
69
70 /// Environment Canada
71 ///
72 /// In Canada, SAME is only transmitted on the Weatheradio Canada
73 /// radio network to alert weather radios. SAME signals are not
74 /// transmitted on broadcast AM/FM or cable systems.
75 ///
76 /// This enum variant will only be selected if the sending station's
77 /// callsign matches the format of Environment Canada stations.
78 #[strum(message = "WXR", detailed_message = "Environment Canada")]
79 EnvironmentCanada,
80
81 /// EAS participant (usu. broadcast station)
82 #[strum(
83 serialize = "EAS",
84 detailed_message = "Broadcast station or cable system"
85 )]
86 BroadcastStation,
87}
88
89impl Originator {
90 /// Construct from originator string and station callsign
91 pub fn from_org_and_call<S1, S2>(org: S1, call: S2) -> Self
92 where
93 S1: AsRef<str>,
94 S2: AsRef<str>,
95 {
96 let decode = str::parse(org.as_ref()).unwrap_or_default();
97 if decode == Self::NationalWeatherService && call.as_ref().starts_with("EC/") {
98 Self::EnvironmentCanada
99 } else {
100 decode
101 }
102 }
103
104 /// Human-readable string representation
105 ///
106 /// Converts to a human-readable string, like "`Civil authorities`."
107 pub fn as_display_str(&self) -> &'static str {
108 self.get_detailed_message().expect("missing definition")
109 }
110
111 /// SAME string representation
112 ///
113 /// Returns the SAME code for this `Originator`.
114 /// [`Originator::Unknown`] returns the empty string.
115 pub fn as_code_str(&self) -> &'static str {
116 self.get_message()
117 .unwrap_or_else(|| self.get_serializations()[0])
118 }
119}
120
121impl std::default::Default for Originator {
122 fn default() -> Self {
123 Self::Unknown
124 }
125}
126
127impl AsRef<str> for Originator {
128 fn as_ref(&self) -> &'static str {
129 self.as_code_str()
130 }
131}
132
133impl fmt::Display for Originator {
134 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135 if f.alternate() {
136 self.as_code_str().fmt(f)
137 } else {
138 self.as_display_str().fmt(f)
139 }
140 }
141}