1extern 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 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 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#[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 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#[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 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}