1#[derive(Default, Debug)]
3pub struct Av<'a> {
4 pub dpt: Option<&'a str>,
5 pub arr: Option<&'a str>,
6 pub date: Option<&'a str>,
7 pub query: Option<&'a str>,
8 pub flights: Vec<AvFlight<'a>>,
9 pub raw_text: &'a str,
10}
11
12#[derive(Default, Debug)]
14pub struct AvFlight<'a> {
15 pub index: u8,
16 pub is_share_flight: bool,
17 pub flight_no: &'a str,
18 pub real_flight_no: Option<&'a str>,
19 pub flight_status: &'a str,
20 pub dpt: &'a str,
21 pub arr: &'a str,
22 pub take_off: &'a str,
23 pub landing: &'a str,
24 pub model: &'a str,
25 pub dpt_terminal: Option<&'a str>,
26 pub arr_terminal: Option<&'a str>,
27 pub duration: Option<&'a str>,
28 pub is_eticket: bool,
29 pub meal: &'a str,
30 pub stops: u8,
31 pub cabins: Vec<AvCabin<'a>>,
32 pub raw_text: &'a str,
33 pub is_marriage_flight: bool,
34 pub union_flights: Vec<AvFlight<'a>>,
35 pub asr: bool,
36}
37
38#[derive(Default, Debug)]
40pub struct AvCabin<'a> {
41 pub name: &'a str,
42 pub state: &'a str,
43 pub is_sub_cabin: bool,
44 pub raw_text: &'a str,
45}
46
47impl<'a> AvCabin<'a> {
48 pub fn is_num_state(&self) -> bool {
51 match self.state.chars().next() {
52 Some(c) => c.is_ascii_digit(),
53 _ => false,
54 }
55 }
56
57 pub fn is_available(&self) -> bool {
60 match self.state.chars().next() {
61 Some('0') | Some('A') => false,
62 Some(c) => c.is_ascii_digit(),
63 _ => false,
64 }
65 }
66
67 pub fn is_locked(&self) -> bool {
70 match self.state {
71 "C" | "Q" => true,
72 _ => false,
73 }
74 }
75
76 pub fn is_soldout(&self) -> bool {
79 !self.is_available() && !self.is_locked()
80 }
81
82 pub fn seat_quantity(&self) -> Option<u8> {
86 match self.state.chars().next() {
87 Some('A') => Some(9),
88 Some(c) if c.is_ascii_digit() => c.to_digit(10).map_or(None, |x| Some(x as u8)),
89 _ => None,
90 }
91 }
92}
93
94impl<'a> Av<'a> {
95 pub fn parse(text: &'a str) -> anyhow::Result<Self> {
96 if text.is_empty() {
97 return Err(anyhow::Error::msg(
98 "av parameter shouldn't be empty.".to_owned(),
99 ));
100 }
101 let mut avinfo = Self {
102 raw_text: text,
103 ..Default::default()
104 };
105
106 let mut start = 0;
107 let mut index = 1;
108 loop {
109 match text.rfind(&format!("\n{}", index)) {
110 Some(end) => {
111 let xs = &text[start..end].trim_start();
112 start = end;
113 if avinfo.dpt.is_none() {
114 let _ = Self::parse_query(xs, &mut avinfo);
115 } else {
116 let curr_flight = Self::parse_flight(xs)?;
117 avinfo.flights.push(curr_flight);
118 }
119 }
120 _ => {
121 let xs = &text[start..].trim_start();
122 let curr_flight = Self::parse_flight(xs)?;
123 avinfo.flights.push(curr_flight);
124 break;
125 }
126 }
127 index += 1;
128 }
129
130 Ok(avinfo)
131 }
132
133 fn parse_query(text: &'a str, avinfo: &mut Av<'a>) -> anyhow::Result<()> {
134 match regex::Regex::new(
135 r"(?<DATE>\d{2}[A-Z]{3}(?:\d{2})?)\(([A-Z]{3})\)[\x1D\s](?<DPT>[A-Z]{3})(?<ARR>[A-Z]{3})",
136 )?
137 .captures(text)
138 {
139 Some(caps) => match (caps.name("DATE"),caps.name("DPT"), caps.name("ARR")) {
140 (Some(date),Some(dpt), Some(arr)) => {
141 avinfo.dpt = Some(dpt.as_str());
142 avinfo.arr = Some(arr.as_str());
143 avinfo.date=Some(date.as_str());
144 avinfo.query=Some(text);
145 }
146 _ => {},
147 },
148 _ => {},
149 }
150 Ok(())
151 }
152
153 pub fn parse_flight(text: &'a str) -> anyhow::Result<AvFlight> {
155 let mut flight = AvFlight {
156 raw_text: text,
157 ..Default::default()
158 };
159
160 for xs in text.split("\n ") {
161 if flight.dpt.len() == 0 {
162 let _ = Self::parse_first_flight(xs, &mut flight);
163 } else {
164 flight.is_marriage_flight = true;
165 let union_flight = Self::parse_union_flight(xs, &flight)?;
166 flight.union_flights.push(union_flight);
167 }
168 }
169 Ok(flight)
170 }
171
172 fn parse_first_flight(text: &'a str, flight: &mut AvFlight<'a>) -> anyhow::Result<()> {
173 for line in text.lines() {
174 if regex::Regex::new(r"^\d").unwrap().is_match(line) {
175 if line.len() < 78 {
176 continue;
177 }
178 flight.index = line[0..1].parse::<u8>()?;
179 flight.is_share_flight = &line[3..4] == "*";
180 flight.flight_no = line[4..11].trim();
181 flight.dpt = line[47..50].trim();
182 flight.arr = line[50..53].trim();
183 flight.take_off = line[54..58].trim();
184 flight.landing = line[61..65].trim();
185 flight.model = line[68..71].trim();
186 flight.stops = line[72..73].parse::<u8>()?;
187 flight.asr = &line[73..74] == "^";
188 flight.flight_status = line[12..15].trim();
189 flight.is_eticket = &line[73..74] == "E";
190 flight.meal = line[74..75].trim();
191 flight.cabins = Self::parse_cabin(&line[15..47]);
192 } else if line.starts_with(">") {
193 flight.real_flight_no = Some(line[4..11].trim());
194 match line.len() {
195 n if n > 73 => {
196 flight.duration = Some(&line[74..]);
197 }
198 n if n > 71 => {
199 flight.arr_terminal = Some(&line[71..73]);
200 }
201 n if n > 68 => {
202 flight.dpt_terminal = Some(&line[68..70]);
203 }
204 _ => {}
205 }
206 let mut cabins = Self::parse_cabin(&line[15..61]);
207 flight.cabins.append(&mut cabins);
208 } else if line.starts_with(" **") {
209 let mut cabins = Self::parse_cabin(line[15..47].trim_end());
210 flight.cabins.append(&mut cabins);
211 }
212 }
213 Ok(())
214 }
215
216 fn parse_union_flight(text: &'a str, flight: &AvFlight<'a>) -> anyhow::Result<AvFlight<'a>> {
217 let mut union_flight = AvFlight {
221 raw_text: &text,
222 ..Default::default()
223 };
224
225 for line in union_flight.raw_text.lines() {
226 if regex::Regex::new(r"^\s+\*?[A-Z0-9]{2}\d+")?.is_match(line) {
227 union_flight.is_share_flight = &line[2..3] == "*";
228 union_flight.flight_no = line[3..10].trim();
229 union_flight.dpt = flight.arr;
230 union_flight.arr = line[49..52].trim();
231 union_flight.take_off = line[53..57].trim();
232 union_flight.landing = line[60..64].trim();
233 union_flight.model = line[67..70].trim();
234 union_flight.stops = line[71..72].parse::<u8>()?;
235 union_flight.flight_status = line[11..14].trim();
236 match line.len() {
237 n if n > 76 => {
238 union_flight.is_eticket = &line[76..77] == "E";
239 union_flight.meal = line[73..74].trim();
240 }
241 n if n > 73 => {
242 union_flight.meal = line[73..74].trim();
243 }
244 _ => {}
245 }
246 let mut cabins = Self::parse_cabin(&line[14..60]);
247 union_flight.cabins.append(&mut cabins);
248 } else if line.starts_with(">") {
249 union_flight.real_flight_no = Some(&line[4..11]);
250 match line.len() {
251 n if n > 73 => {
252 union_flight.duration = Some(&line[74..]);
253 }
254 n if n > 71 => {
255 union_flight.arr_terminal = Some(&line[71..73]);
256 }
257 n if n > 68 => {
258 union_flight.dpt_terminal = Some(&line[68..70]);
259 }
260 _ => {}
261 }
262 let mut cabins = Self::parse_cabin(&line[15..61]);
263 union_flight.cabins.append(&mut cabins);
264 } else if line.starts_with(" **") {
265 let mut cabins = Self::parse_cabin(line[15..47].trim_end());
266 union_flight.cabins.append(&mut cabins);
267 }
268 }
269 Ok(union_flight)
270 }
271
272 pub fn parse_cabin(text: &'a str) -> Vec<AvCabin> {
274 let is_sub_cabin = text.starts_with("**");
275 if is_sub_cabin {
276 text[2..]
277 .split_whitespace()
278 .into_iter()
279 .map(|x| AvCabin {
280 name: &x[0..1],
281 state: &x[1..1],
282 is_sub_cabin,
283 raw_text: x,
284 })
285 .collect::<Vec<_>>()
286 } else {
287 text.split_whitespace()
288 .into_iter()
289 .map(|x| AvCabin {
290 name: &x[0..1],
291 state: &x[1..1],
292 is_sub_cabin,
293 raw_text: x,
294 })
295 .collect::<Vec<_>>()
296 }
297 }
298}