1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
use chrono::FixedOffset; use std::fmt::Display; use strum_macros::{EnumIter, EnumString, IntoStaticStr}; /// Description of a site with a sounding. #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct Site { /// Station number, this should be unique to the site. Site ids sometimes change around. pub station_num: u32, /// Site ID. This is usually a 3 or 4 letter ID. These change from time to time and are not /// always unique to a site. If you need an identifief unique to a location, use the /// station_num. Because these change, it is possible to orphan a site with a site id, hence /// the option. pub id: Option<String>, /// A longer, more human readable name. pub name: Option<String>, /// Any relevant notes about the site. pub notes: Option<String>, /// The state or providence where this location is located. This allows querying sites by what /// state or providence they are in. pub state: Option<StateProv>, /// For programs that download files, this allows marking some sites for automatic download /// without further specification. pub auto_download: bool, /// Time zone information pub time_zone: Option<FixedOffset>, } impl Site { /// Return true if there is any missing data. It ignores the notes field since this is only /// rarely used. pub fn incomplete(&self) -> bool { self.name.is_none() || self.state.is_none() || self.time_zone.is_none() } } impl Display for Site { fn fmt(&self, formatter: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { writeln!( formatter, "Site: station_num - {:6} | name - {:20} | state - {:2} | notes - {} | download - {}", self.station_num, self.id.as_deref().unwrap_or("None"), self.state.map(|s| s.as_static_str()).unwrap_or("None"), self.notes.as_deref().unwrap_or("None"), self.auto_download, ) } } impl Default for Site { fn default() -> Self { Site { station_num: 0, id: None, name: None, notes: None, state: None, auto_download: false, time_zone: None, } } } /// State/Providence abreviations for declaring a state in the site. #[derive(Debug, Hash, Clone, Copy, PartialEq, Eq, EnumString, IntoStaticStr, EnumIter)] #[allow(missing_docs)] pub enum StateProv { AL, // Alabama AK, // Alaska AZ, // Arizona AR, // Arkansas CA, // California CO, // Colorado CT, // Connecticut DE, // Delaware FL, // Florida GA, // Georgia HI, // Hawaii ID, // Idaho IL, // Illinois IN, // Indiana IA, // Iowa KS, // Kansas KY, // Kentucky LA, // Louisiana ME, // Maine MD, // Maryland MA, // Massachussetts MI, // Michigan MN, // Minnesota MS, // Mississippi MO, // Missouri MT, // Montana NE, // Nebraska NV, // Nevada NH, // New Hampshire NJ, // New Jersey NM, // New Mexico NY, // New York NC, // North Carolina ND, // North Dakota OH, // Ohio OK, // Oklahoma OR, // Oregon PA, // Pensylvania RI, // Rhode Island SC, // South Carolina SD, // South Dakota TN, // Tennessee TX, // Texas UT, // Utah VT, // Vermont VA, // Virginia WA, // Washington WV, // West Virginia WI, // Wisconsin WY, // Wyoming // US Commonwealth and Territories AS, // American Samoa DC, // District of Columbia FM, // Federated States of Micronesia MH, // Marshall Islands MP, // Northern Mariana Islands PW, // Palau PR, // Puerto Rico VI, // Virgin Islands } impl StateProv { /// Get a static string representation. pub fn as_static_str(self) -> &'static str { self.into() } } /*-------------------------------------------------------------------------------------------------- Unit Tests --------------------------------------------------------------------------------------------------*/ #[cfg(test)] mod unit { use super::*; use std::str::FromStr; use strum::IntoEnumIterator; #[test] fn test_site_incomplete() { let complete_site = Site { station_num: 1, id: Some("kxly".to_owned()), name: Some("tv station".to_owned()), state: Some(StateProv::VI), notes: Some("".to_owned()), auto_download: false, time_zone: Some(FixedOffset::west(7 * 3600)), }; let incomplete_site = Site { station_num: 1, id: Some("kxly".to_owned()), name: Some("tv station".to_owned()), state: None, notes: None, auto_download: true, time_zone: None, }; assert!(!complete_site.incomplete()); assert!(incomplete_site.incomplete()); } #[test] fn test_to_string_for_state_prov() { assert_eq!(StateProv::AL.as_static_str(), "AL"); } #[test] fn test_from_string_for_state_prov() { assert_eq!(StateProv::from_str("AL").unwrap(), StateProv::AL); } #[test] fn round_trip_strings_for_state_prov() { for state_prov in StateProv::iter() { assert_eq!( StateProv::from_str(state_prov.as_static_str()).unwrap(), state_prov ); } } }