iata_types/
lib.rs

1extern crate serde;
2#[macro_use]
3extern crate serde_derive;
4
5use std::fmt;
6use std::str;
7use std::str::FromStr;
8
9macro_rules! gen_as {
10    () => {
11        pub fn as_str(&self) -> &str {
12            str::from_utf8(&self.0).unwrap()
13        }
14
15        pub fn as_bytes(&self) -> &[u8] {
16            &self.0
17        }
18    }
19}
20
21macro_rules! gen_display_debug {
22    ($t: ty) => {
23        impl fmt::Display for $t {
24            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
25                write!(f, "{}", self.as_str())
26            }
27        }
28
29        impl fmt::Debug for $t {
30            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
31                write!(f, "{}", self.as_str())
32            }
33        }
34    }
35}
36
37/// IATA aircraft type code
38#[derive(Ord, PartialOrd, Eq, PartialEq, Serialize, Deserialize, Hash, Clone, Copy)]
39pub struct AircraftCode([u8; 3]);
40
41gen_display_debug!(AircraftCode);
42
43impl AircraftCode {
44    gen_as!();
45
46    /// Reconstruct AircraftCode from AircraftCode.as_bytes()
47    pub unsafe fn from_bytes_unchecked(bytes: &[u8]) -> Self {
48        let mut mine = [0; 3];
49
50        mine.copy_from_slice(bytes);
51        AircraftCode(mine)
52    }
53}
54
55#[derive(Debug)]
56pub enum AircraftCodeParseError {
57    InvalidLength(usize),
58    InvalidCharacter(char)
59}
60
61impl std::fmt::Display for AircraftCodeParseError {
62    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
63        match *self {
64            AircraftCodeParseError::InvalidLength(len) => {
65                write!(f, "invalid length: {}, expected 3", len)
66            },
67            AircraftCodeParseError::InvalidCharacter(c) => {
68                write!(f, "invalid character: {}, expected A-Z0-9", c)
69            }
70        }
71    }
72}
73
74impl std::error::Error for AircraftCodeParseError {
75    fn description(&self) -> &str {
76        "aircraft code parsing error"
77    }
78}
79
80impl FromStr for AircraftCode {
81    type Err = AircraftCodeParseError;
82
83    fn from_str(value: &str) -> Result<Self, Self::Err> {
84        if value.len() != 3 {
85            return Err(AircraftCodeParseError::InvalidLength(value.len()));
86        }
87
88        for c in value.chars() {
89            if c.is_ascii_uppercase() || c.is_ascii_digit() {
90                continue;
91            } else {
92                return Err(AircraftCodeParseError::InvalidCharacter(c));
93            }
94        }
95        let mut bytes = [0; 3];
96        bytes.copy_from_slice(value.as_bytes());
97        Ok(AircraftCode(bytes))
98    }
99}
100
101// Airline designator codes follow the format xx(a), i.e.,
102// two alphanumeric characters (letters or digits) followed by an optional letter.
103// Although the IATA standard provides for three-character airline designators,
104// IATA has not used the optional third character in any assigned code.
105// This is because some legacy computer systems, especially the "central reservations systems",
106// have failed to comply with the standard, notwithstanding the fact that it has been in place for 20 years.
107// The codes issued to date comply with IATA Resolution 762, which provides for only two characters.
108/// IATA airline designators are used to identify an airline for commercial purposes in reservations,
109/// timetables, tickets, tariffs, air waybills and in telecommunications.
110#[derive(Ord, PartialOrd, Eq, PartialEq, Serialize, Deserialize, Hash, Clone, Copy)]
111pub struct AirlineCode([u8; 2]);
112
113gen_display_debug!(AirlineCode);
114
115impl AirlineCode {
116    gen_as!();
117
118
119    /// Reconstruct AirlineCode from AirlineCode.as_bytes()
120    pub unsafe fn from_bytes_unchecked(bytes: &[u8]) -> Self {
121        let mut mine = [0; 2];
122
123        mine.copy_from_slice(bytes);
124        AirlineCode(mine)
125    }
126}
127
128#[derive(Debug)]
129pub enum AirlineCodeParseError {
130    InvalidLength(usize),
131    InvalidCharacter(char)
132}
133
134impl std::fmt::Display for AirlineCodeParseError {
135    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
136        match *self {
137            AirlineCodeParseError::InvalidLength(len) => {
138                write!(f, "invalid length: {}, expected 2", len)
139            },
140            AirlineCodeParseError::InvalidCharacter(c) => {
141                write!(f, "invalid character: {}, expected A-Z0-9", c)
142            }
143        }
144    }
145}
146
147impl std::error::Error for AirlineCodeParseError {
148    fn description(&self) -> &str {
149        "airline code parsing error"
150    }
151}
152
153impl FromStr for AirlineCode {
154    type Err = AirlineCodeParseError;
155
156    fn from_str(value: &str) -> Result<Self, Self::Err> {
157        if value.len() != 2 {
158            return Err(AirlineCodeParseError::InvalidLength(value.len()));
159        }
160
161        for c in value.chars() {
162            if c.is_ascii_uppercase() || c.is_ascii_digit() {
163                continue;
164            } else {
165                return Err(AirlineCodeParseError::InvalidCharacter(c))
166            }
167        }
168        let mut bytes = [0; 2];
169        bytes.copy_from_slice(value.as_bytes());
170        Ok(AirlineCode(bytes))
171    }
172}
173
174/// 3 letter airport code
175#[derive(Ord, PartialOrd, Eq, PartialEq, Serialize, Deserialize, Hash, Clone, Copy)]
176pub struct AirportCode([u8; 3]);
177
178gen_display_debug!(AirportCode);
179
180impl AirportCode {
181    gen_as!();
182
183
184    /// Reconstruct AirportCode from AirportCode.as_bytes()
185    pub unsafe fn from_bytes_unchecked(bytes: &[u8]) -> Self {
186        let mut mine = [0; 3];
187
188        mine.copy_from_slice(bytes);
189        AirportCode(mine)
190    }
191}
192
193#[derive(Debug)]
194pub enum AirportCodeParseError {
195    InvalidLength(usize),
196    InvalidCharacter(char)
197}
198
199impl std::fmt::Display for AirportCodeParseError {
200    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
201        match *self {
202            AirportCodeParseError::InvalidLength(len) => {
203                write!(f, "invalid length: {}, expected 3", len)
204            },
205            AirportCodeParseError::InvalidCharacter(c) => {
206                write!(f, "invalid character: {}, expected A-Z0-9", c)
207            }
208        }
209    }
210}
211
212impl std::error::Error for AirportCodeParseError {
213    fn description(&self) -> &str {
214        "airport code parsing error"
215    }
216}
217
218impl FromStr for AirportCode {
219    type Err = AirportCodeParseError;
220
221    fn from_str(value: &str) -> Result<Self, Self::Err> {
222        if value.len() != 3 {
223            return Err(AirportCodeParseError::InvalidLength(value.len()));
224        }
225        for c in value.chars() {
226            if c.is_ascii_uppercase() {
227                continue;
228            } else {
229                return Err(AirportCodeParseError::InvalidCharacter(c));
230            }
231        }
232        let mut bytes = [0; 3];
233        bytes.copy_from_slice(value.as_bytes());
234        Ok(AirportCode(bytes))
235    }
236}
237
238/// 3 letter location code
239#[derive(Ord, PartialOrd, Eq, PartialEq, Serialize, Deserialize, Hash, Clone, Copy)]
240pub struct CityCode([u8; 3]);
241
242gen_display_debug!(CityCode);
243
244impl CityCode {
245    gen_as!();
246
247    /// Reconstruct CityCode from CityCode.as_bytes()
248    pub unsafe fn from_bytes_unchecked(bytes: &[u8]) -> Self {
249        let mut mine = [0; 3];
250
251        mine.copy_from_slice(bytes);
252        CityCode(mine)
253    }
254}
255
256#[derive(Debug)]
257pub enum CityCodeParseError {
258    InvalidLength(usize),
259    InvalidCharacter(char)
260}
261
262impl std::fmt::Display for CityCodeParseError {
263    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
264        match *self {
265            CityCodeParseError::InvalidLength(len) => {
266                write!(f, "invalid length: {}, expected 3", len)
267            },
268            CityCodeParseError::InvalidCharacter(c) => {
269                write!(f, "invalid character: {}, expected A-Z0-9", c)
270            }
271        }
272    }
273}
274
275impl std::error::Error for CityCodeParseError {
276    fn description(&self) -> &str {
277        "airport code parsing error"
278    }
279}
280
281impl FromStr for CityCode {
282    type Err = CityCodeParseError;
283
284    fn from_str(value: &str) -> Result<Self, Self::Err> {
285        if value.len() != 3 {
286            return Err(CityCodeParseError::InvalidLength(value.len()));
287        }
288        for c in value.chars() {
289            if c.is_ascii_uppercase() {
290                continue;
291            } else {
292                return Err(CityCodeParseError::InvalidCharacter(c));
293            }
294        }
295        let mut bytes = [0; 3];
296        bytes.copy_from_slice(value.as_bytes());
297        Ok(CityCode(bytes))
298    }
299}
300
301/// Numeric part of flight designator
302#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Serialize, Deserialize, Hash, Clone, Copy)]
303pub struct FlightNumber(u16);
304
305impl FlightNumber {
306    pub fn to_u16(&self) -> u16 {
307        self.0
308    }
309
310    pub fn from_u16(num: u16) -> Result<Self, FlightNumberParseError> {
311        if num >= 1 && num <= 9999 {
312            return Ok(FlightNumber(num))
313        }
314        Err(FlightNumberParseError::InvalidNumber)
315    }
316}
317
318impl fmt::Display for FlightNumber {
319    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
320        write!(f, "{:04}", self.0)
321    }
322}
323
324#[derive(Debug)]
325pub enum FlightNumberParseError {
326    NotANumber,
327    InvalidNumber,
328}
329
330impl std::fmt::Display for FlightNumberParseError {
331    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
332        match *self {
333            FlightNumberParseError::NotANumber => {
334                write!(f, "not a number")
335            },
336            FlightNumberParseError::InvalidNumber => {
337                write!(f, "invalid number, expect 0001 to 9999")
338            }
339        }
340    }
341}
342
343impl std::error::Error for FlightNumberParseError {
344    fn description(&self) -> &str {
345        "airport code parsing error"
346    }
347}
348
349impl FromStr for FlightNumber {
350    type Err = FlightNumberParseError;
351
352    fn from_str(value: &str) -> Result<Self, Self::Err> {
353        let num = value.parse()
354                .map_err(|_| FlightNumberParseError::NotANumber)?;
355        Self::from_u16(num)
356    }
357}