eterm_parser/
av.rs

1/// The result that av text parsed.
2#[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/// The flights of an Av.
13#[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/// The cabins of an AvFlight.
39#[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    /// Return whether the seat of the cabin is number.
49    /// such as 1-9.
50    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    /// Return whether the seat of the cabin is available.
58    /// such as 1-9,A.
59    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    /// Return whether the seat of the cabin is locked.
68    /// such as C,Q.
69    pub fn is_locked(&self) -> bool {
70        match self.state {
71            "C" | "Q" => true,
72            _ => false,
73        }
74    }
75
76    /// Return whether the seat of the cabin is soldout.
77    /// it's not available and locked.
78    pub fn is_soldout(&self) -> bool {
79        !self.is_available() && !self.is_locked()
80    }
81
82    /// Return quantity of the seat of the cabin.
83    /// when it's number, return the number.
84    /// when it's 'A',return 9.
85    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    ///it easy to parse a text of flight of av specifically. 
154    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 raw_text=text.to_owned();
218        //raw_text.insert_str(0, " ");
219        //println!("{}",text);
220        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    ///it easy to parse a text of cabins of flight specifically. 
273    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}