sirena_types/
lib.rs

1//! https://ru.wikipedia.org/wiki/Сирена_(сеть)
2extern crate serde;
3#[macro_use]
4extern crate serde_derive;
5extern crate encoding_rs;
6
7use std::fmt;
8use std::str;
9use std::str::FromStr;
10use std::borrow::Cow;
11
12use encoding_rs::KOI8_R;
13
14macro_rules! gen_display {
15    ($t: ty) => {
16        impl fmt::Display for $t {
17            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
18                write!(f, "{}", self.as_str())
19            }
20        }
21
22	impl fmt::Debug for $t {
23            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
24                write!(f, "{}", self.as_str())
25            }
26        }
27    }
28}
29
30macro_rules! gen_as {
31    () => {
32        pub fn as_str(&self) -> Cow<str> {
33            let (s, _, _) = KOI8_R.decode(&self.0);
34            s
35        }
36
37        pub fn as_bytes(&self) -> &[u8] {
38            &self.0
39        }
40    }
41}
42
43#[derive(Ord, PartialOrd, Eq, PartialEq, Serialize, Deserialize, Hash, Clone, Copy)]
44pub struct AircraftCode([u8; 3]);
45
46gen_display!(AircraftCode);
47impl AircraftCode {
48    gen_as!();
49}
50
51#[derive(Debug)]
52pub enum AircraftCodeParseError {
53    InvalidLength(usize),
54    InvalidLetter(char),
55}
56
57impl fmt::Display for AircraftCodeParseError {
58    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
59        match *self {
60            AircraftCodeParseError::InvalidLength(len) => write!(f, "invalid length {}, expected 3", len),
61            AircraftCodeParseError::InvalidLetter(c) => write!(f, "invalid character {}, expected [А-Я]", c),
62        }
63    }
64}
65
66impl std::error::Error for AircraftCodeParseError {
67    fn description(&self) -> &str {
68        "aircraft code parse error"
69    }
70}
71
72impl FromStr for AircraftCode {
73    type Err = AircraftCodeParseError;
74
75    fn from_str(value: &str) -> Result<Self, Self::Err> {
76        if value.chars().count() != 3 {
77            return Err(AircraftCodeParseError::InvalidLength(value.len()));
78        }
79        for c in value.chars() {
80            if c.is_ascii_digit() || (c >= 'А' && c <= 'Я') {
81                continue;
82            } else {
83                return Err(AircraftCodeParseError::InvalidLetter(c));
84            }
85        }
86        let (koi8str, _, _) = KOI8_R.encode(value);
87        let mut bytes = [0; 3];
88        bytes.copy_from_slice(&koi8str);
89        Ok(AircraftCode(bytes))
90    }
91}
92
93#[derive(Ord, PartialOrd, Eq, PartialEq, Serialize, Deserialize, Hash, Clone, Copy)]
94pub struct AirlineCode([u8; 2]);
95
96gen_display!(AirlineCode);
97
98impl AirlineCode {
99    gen_as!();
100
101    /// Reconstruct AirlineCode from AirlineCode.as_bytes()
102    pub unsafe fn from_bytes_unchecked(bytes: &[u8]) -> Self {
103        let mut mine = [0; 2];
104
105        mine.copy_from_slice(bytes);
106        AirlineCode(mine)
107    }
108}
109
110#[derive(Debug)]
111pub enum AirlineCodeParseError {
112    InvalidLength(usize),
113    InvalidLetter(char),
114    TooManyDigits(u32),
115}
116
117
118impl fmt::Display for AirlineCodeParseError {
119    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
120        match *self {
121            AirlineCodeParseError::InvalidLength(len) => write!(f, "invalid length {}, expected 2", len),
122            AirlineCodeParseError::InvalidLetter(c) => write!(f, "invalid character {}, expected [А-Я]", c),
123            AirlineCodeParseError::TooManyDigits(digits) => write!(f, "got {} digits, only 1 allowed", digits),
124        }
125    }
126}
127
128impl std::error::Error for AirlineCodeParseError {
129    fn description(&self) -> &str {
130        "airline code parse error"
131    }
132}
133
134impl FromStr for AirlineCode {
135    type Err = AirlineCodeParseError;
136
137    fn from_str(value: &str) -> Result<Self, Self::Err> {
138        if value.chars().count() != 2 {
139            return Err(AirlineCodeParseError::InvalidLength(value.len()));
140        }
141        let mut digits = 0;
142        for c in value.chars() {
143            if c >= 'А' && c <= 'Я' {
144                continue;
145            } else if c.is_ascii_digit() {
146                digits += 1;
147                continue;
148            } else {
149                return Err(AirlineCodeParseError::InvalidLetter(c));
150            }
151        }
152        // can't be 2 digits,
153        // https://ru.wikipedia.org/wiki/Код_авиакомпании_ИАТА#Внутренняя_система_кодирования_в_бывшем_СССР
154        if digits > 1 {
155            return Err(AirlineCodeParseError::TooManyDigits(digits));
156        }
157        let (koi8str, _, _) = KOI8_R.encode(value);
158        let mut bytes = [0; 2];
159        bytes.copy_from_slice(&koi8str);
160        Ok(AirlineCode(bytes))
161    }
162}
163
164/// 3 letter airport code
165#[derive(Ord, PartialOrd, Eq, PartialEq, Serialize, Deserialize, Hash, Clone, Copy)]
166pub struct AirportCode([u8; 3]);
167
168gen_display!(AirportCode);
169
170impl AirportCode {
171    gen_as!();
172
173    /// Reconstruct AirportCode from AirportCode.as_bytes()
174    pub unsafe fn from_bytes_unchecked(bytes: &[u8]) -> Self {
175        let mut mine = [0; 3];
176
177        mine.copy_from_slice(bytes);
178        AirportCode(mine)
179    }
180}
181
182#[derive(Debug)]
183pub enum AirportCodeParseError {
184    InvalidLength(usize),
185    InvalidLetter(char),
186}
187
188impl fmt::Display for AirportCodeParseError {
189    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
190        match *self {
191            AirportCodeParseError::InvalidLength(len) => write!(f, "invalid length {}, expected 3", len),
192            AirportCodeParseError::InvalidLetter(c) => write!(f, "invalid character {}, expected [А-Я]", c),
193        }
194    }
195}
196
197impl std::error::Error for AirportCodeParseError {
198    fn description(&self) -> &str {
199        "airport code parse error"
200    }
201}
202
203impl FromStr for AirportCode {
204    type Err = AirportCodeParseError;
205
206    fn from_str(value: &str) -> Result<Self, Self::Err> {
207        if value.chars().count() != 3 {
208            return Err(AirportCodeParseError::InvalidLength(value.len()));
209        }
210        for c in value.chars() {
211            if c >= 'А' && c <= 'Я' {
212                continue;
213            } else {
214                return Err(AirportCodeParseError::InvalidLetter(c));
215            }
216        }
217        let (koi8str, _, _) = KOI8_R.encode(value);
218        let mut bytes = [0; 3];
219        bytes.copy_from_slice(&koi8str);
220        Ok(AirportCode(bytes))
221    }
222}
223
224/// 3 letter airport code
225#[derive(Ord, PartialOrd, Eq, PartialEq, Serialize, Deserialize, Hash, Clone, Copy)]
226pub struct CityCode([u8; 3]);
227
228gen_display!(CityCode);
229
230impl CityCode {
231    gen_as!();
232
233    /// Reconstruct CityCode from CityCode.as_bytes()
234    pub unsafe fn from_bytes_unchecked(bytes: &[u8]) -> Self {
235        let mut mine = [0; 3];
236
237        mine.copy_from_slice(bytes);
238        CityCode(mine)
239    }
240}
241
242#[derive(Debug)]
243pub enum CityCodeParseError {
244    InvalidLength(usize),
245    InvalidLetter(char),
246}
247
248impl fmt::Display for CityCodeParseError {
249    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
250        match *self {
251            CityCodeParseError::InvalidLength(len) => write!(f, "invalid length {}, expected 3", len),
252            CityCodeParseError::InvalidLetter(c) => write!(f, "invalid character {}, expected [А-Я]", c),
253        }
254    }
255}
256
257impl std::error::Error for CityCodeParseError {
258    fn description(&self) -> &str {
259        "city code parse error"
260    }
261}
262
263impl FromStr for CityCode {
264    type Err = CityCodeParseError;
265
266    fn from_str(value: &str) -> Result<Self, Self::Err> {
267        if value.chars().count() != 3 {
268            return Err(CityCodeParseError::InvalidLength(value.len()));
269        }
270        for c in value.chars() {
271            if c >= 'А' && c <= 'Я' {
272                continue;
273            } else {
274                return Err(CityCodeParseError::InvalidLetter(c));
275            }
276        }
277        let (koi8str, _, _) = KOI8_R.encode(value);
278        let mut bytes = [0; 3];
279        bytes.copy_from_slice(&koi8str);
280        Ok(CityCode(bytes))
281    }
282}
283
284#[test]
285fn test_encode_aircraft() {
286    let a = "ПУ1";
287    let code = AircraftCode::from_str(a).unwrap();
288    println!("{:?}", code);
289    assert_eq!(a, &format!("{}", code));
290    assert_eq!(a, &code.as_str());
291}