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
            );
        }
    }
}