paiagram/rw_data/
oudiasecond.rs

1use bevy::{
2    ecs::{message::MessageReader, name},
3    log::error,
4};
5use pest::Parser;
6use pest_derive::Parser;
7
8use crate::{rw_data::ModifyData, units::time::TimetableTime};
9
10#[derive(Parser)]
11#[grammar = "rw_data/oudiasecond.pest"]
12pub struct OUD2Parser;
13
14#[derive(Debug)]
15enum OUD2AstStruct<'a> {
16    Struct(&'a str, Vec<OUD2AstStruct<'a>>),
17    Pair(&'a str, OUD2AstValue<'a>),
18}
19
20#[derive(Debug)]
21enum OUD2AstValue<'a> {
22    Single(&'a str),
23    List(Vec<&'a str>),
24}
25
26fn parse_oud2_to_ast(file: &str) -> Result<OUD2AstStruct<'_>, pest::error::Error<Rule>> {
27    let oud2 = OUD2Parser::parse(Rule::file, file)?
28        .next()
29        .unwrap()
30        .into_inner()
31        .next()
32        .unwrap();
33    use pest::iterators::Pair;
34    fn parse_struct(pair: Pair<Rule>) -> OUD2AstStruct {
35        match pair.as_rule() {
36            Rule::r#struct => {
37                let mut inner = pair.into_inner();
38                let name = inner.next().unwrap().as_str();
39                let mut fields = Vec::new();
40                for field_pair in inner {
41                    let field_struct = parse_struct(field_pair);
42                    fields.push(field_struct);
43                }
44                OUD2AstStruct::Struct(name, fields)
45            }
46            Rule::wrapper => {
47                let inner = pair.into_inner();
48                let name = "file";
49                let mut fields = Vec::new();
50                for field_pair in inner {
51                    let field_struct = parse_struct(field_pair);
52                    fields.push(field_struct);
53                }
54                OUD2AstStruct::Struct(name, fields)
55            }
56            Rule::kvpair => {
57                let mut inner = pair.into_inner();
58                let key = inner.next().unwrap().as_str();
59                let val = inner.next().unwrap();
60                let val = match val.as_rule() {
61                    Rule::value => OUD2AstValue::Single(val.as_str()),
62                    Rule::list => {
63                        let list_vals = val.into_inner().map(|v| v.as_str()).collect();
64                        OUD2AstValue::List(list_vals)
65                    }
66                    _ => unreachable!(),
67                };
68                OUD2AstStruct::Pair(key, val)
69            }
70            _ => unreachable!(),
71        }
72    }
73    Ok(parse_struct(oud2))
74}
75
76struct OUD2File {
77    file_type: String,
78    line: OUD2Line,
79}
80
81struct OUD2Line {
82    name: String,
83    stations: Vec<OUD2Station>,
84    vehicle_sets: Vec<OUD2VehicleSet>,
85}
86
87struct OUD2Station {
88    name: String,
89    tracks: Vec<OUD2Track>,
90}
91
92struct OUD2Track {
93    name: String,
94    alias: String,
95}
96
97struct OUD2VehicleSet {
98    name: String,
99    // down and up. kudari for down and nobori for up.
100    kudari: Vec<OUD2Vehicle>,
101    nobori: Vec<OUD2Vehicle>,
102}
103
104struct OUD2Vehicle {
105    // class: String,
106    /// Ressyabangou
107    name: String,
108    times: Vec<Option<OUD2Entry>>,
109}
110
111struct OUD2Entry {
112    pass_type: OUD2PassType,
113    arrival_time: Option<TimetableTime>,
114    departure_time: Option<TimetableTime>,
115    track: usize,
116    operation_tree: OUD2OperationTree,
117}
118
119enum OUD2PassType {
120    Stop,
121    NonStop,
122    NoOp,
123}
124
125struct OUD2OperationTree {
126    value: Option<[String; 6]>,
127    before: Option<Box<OUD2OperationTree>>,
128    after: Option<Box<OUD2OperationTree>>,
129}
130
131fn parse_to_file(ast: OUD2AstStruct<'_>) -> Result<OUD2File, String> {
132    let mut line: Option<OUD2Line> = None;
133    let mut file_type: Option<String> = None;
134    match ast {
135        OUD2AstStruct::Pair("FileType", OUD2AstValue::Single(s)) => {
136            file_type = Some(s.to_string());
137        }
138        OUD2AstStruct::Struct("Rosen", inner) => {
139            line = Some(parse_to_line(inner)?);
140        }
141        _ => {}
142    };
143    if let (Some(file_type), Some(line)) = (file_type, line) {
144        Ok(OUD2File { file_type, line })
145    } else {
146        Err("Missing required fields in OUD2 file.".to_string())
147    }
148}
149
150fn parse_to_line(inner: Vec<OUD2AstStruct<'_>>) -> Result<OUD2Line, String> {
151    let mut name: Option<String> = None;
152    let mut stations = Vec::new();
153    let mut vehicle_sets = Vec::new();
154    for field in inner {
155        match field {
156            OUD2AstStruct::Pair("DiaName", OUD2AstValue::Single(s)) => {
157                name = Some(s.to_string());
158            }
159            OUD2AstStruct::Struct("Eki", inner) => {
160                let station = parse_to_station(inner)?;
161                stations.push(station);
162            }
163            OUD2AstStruct::Struct("Dia", inner) => {
164                let vehicle_set = parse_to_vehicle_set(inner)?;
165                vehicle_sets.push(vehicle_set);
166            }
167            _ => {}
168        }
169    }
170    if let Some(name) = name {
171        Ok(OUD2Line {
172            name,
173            stations,
174            vehicle_sets,
175        })
176    } else {
177        Err("Missing line name in OUD2 file.".to_string())
178    }
179}
180
181fn parse_to_station(inner: Vec<OUD2AstStruct<'_>>) -> Result<OUD2Station, String> {
182    let mut name: Option<String> = None;
183    let mut tracks = Vec::new();
184    for field in inner {
185        match field {
186            OUD2AstStruct::Pair("Ekimei", OUD2AstValue::Single(s)) => {
187                name = Some(s.to_string());
188            }
189            OUD2AstStruct::Struct("EkiTrack2Cont", inner) => {
190                for field in inner {
191                    let OUD2AstStruct::Struct(_, inner) = field else {
192                        continue;
193                    };
194                    let mut name: Option<String> = None;
195                    let mut alias: Option<String> = None;
196                    for field in inner {
197                        match field {
198                            OUD2AstStruct::Pair("TrackName", OUD2AstValue::Single(s)) => {
199                                name = Some(s.to_string());
200                            }
201                            OUD2AstStruct::Pair("TrackRyakusyou", OUD2AstValue::Single(s)) => {
202                                alias = Some(s.to_string());
203                            }
204                            _ => {}
205                        }
206                    }
207                    if let (Some(name), Some(alias)) = (name, alias) {
208                        tracks.push(OUD2Track { name, alias });
209                    }
210                }
211            }
212            _ => {}
213        }
214    }
215    if let Some(name) = name {
216        Ok(OUD2Station { name, tracks })
217    } else {
218        Err("Missing station name in OUD2 file.".to_string())
219    }
220}
221
222fn parse_to_vehicle_set(inner: Vec<OUD2AstStruct<'_>>) -> Result<OUD2VehicleSet, String> {
223    let mut name: Option<String> = None;
224    let mut kudari = Vec::new();
225    let mut nobori = Vec::new();
226    for field in inner {
227        match field {
228            OUD2AstStruct::Pair("DiaName", OUD2AstValue::Single(s)) => {
229                name = Some(s.to_string());
230            }
231            OUD2AstStruct::Struct("Kudari", inner) => {
232                for field in inner {
233                    let OUD2AstStruct::Struct(name, inner) = field else {
234                        continue;
235                    };
236                    if name != "Ressya" {
237                        continue;
238                    }
239                    let vehicle = parse_to_vehicle(inner)?;
240                    kudari.push(vehicle);
241                }
242            }
243            OUD2AstStruct::Struct("Nobori", inner) => {
244                for field in inner {
245                    let OUD2AstStruct::Struct(name, inner) = field else {
246                        continue;
247                    };
248                    if name != "Ressya" {
249                        continue;
250                    }
251                    let vehicle = parse_to_vehicle(inner)?;
252                    nobori.push(vehicle);
253                }
254            }
255            _ => {}
256        }
257    }
258    if let Some(name) = name {
259        Ok(OUD2VehicleSet {
260            name,
261            kudari,
262            nobori,
263        })
264    } else {
265        Err("Missing vehicle set name in OUD2 file.".to_string())
266    }
267}
268
269fn parse_to_vehicle(inner: Vec<OUD2AstStruct<'_>>) -> Result<OUD2Vehicle, String> {
270    let mut name: Option<String> = None;
271    let mut class: Option<usize> = None;
272    let mut times: Vec<Option<OUD2Entry>> = Vec::new();
273    let mut operations: Option<String> = None;
274    for field in inner {
275        match field {
276            OUD2AstStruct::Pair("Syubetsu", OUD2AstValue::Single(s)) => {
277                class = s.parse().ok();
278            }
279            OUD2AstStruct::Pair("Ressyabangou", OUD2AstValue::Single(s)) => {
280                name = Some(s.to_string());
281            }
282            OUD2AstStruct::Pair("EkiJikoku", inner) => {
283                let inner = match inner {
284                    OUD2AstValue::Single(s) => vec![s],
285                    OUD2AstValue::List(v) => v,
286                };
287                for entry in inner {
288                    let time = parse_to_time(entry).ok();
289                    times.push(time);
290                }
291            }
292            OUD2AstStruct::Pair(s, v) if s.starts_with("Operation") => {
293                // TODO
294            }
295            _ => {}
296        };
297    }
298    if let Some(name) = name {
299        Ok(OUD2Vehicle { name, times })
300    } else {
301        Err("Missing vehicle name in OUD2 file.".to_string())
302    }
303}
304
305fn parse_to_time(inner: &str) -> Result<OUD2Entry, String> {
306    let parts = OUD2Parser::parse(Rule::timetable_entry, inner).map_err(|e| e.to_string())?;
307    let mut pass_type = OUD2PassType::NoOp;
308    let mut arrival_time: Option<TimetableTime> = None;
309    let mut departure_time: Option<TimetableTime> = None;
310    let mut track: usize = 0;
311    // for part in parts {
312    //     match part.as_rule() {
313    //         Rule::service_mode => {
314    //             if part.as_str() == "0" {
315    //                 pass_type = OUD2PassType::Stop;
316    //             } else if part.as_str() == "1" {
317    //                 pass_type = OUD2PassType::NonStop;
318    //             }
319    //         }
320    //         Rule::arrival => {
321    //             let time_str = part.into_inner().next().unwrap().as_str();
322    //             let time = TimetableTime::from_str(time_str).map_err(|e| e.to_string())?;
323    //             arrival_time = Some(time);
324    //         }
325    //         // TODO finish for tomorrow
326    //     }
327    // }
328    Ok(OUD2Entry {
329        pass_type,
330        arrival_time,
331        departure_time,
332        track,
333        operation_tree: OUD2OperationTree {
334            value: None,
335            before: None,
336            after: None,
337        },
338    })
339}
340
341use bevy::prelude::*;
342pub fn load_oud2(
343    mut commands: Commands,
344    mut msg_read_rw: MessageReader<ModifyData>, // TODO: error display
345                                                // mut msg_display_error: MessageWriter<DisplayError>,
346) {
347    let mut data: Option<&str> = None;
348    for msg in msg_read_rw.read() {
349        if let ModifyData::LoadOuDiaSecond(content) = msg {
350            data = Some(content);
351        }
352    }
353    let Some(data) = data else {
354        return;
355    };
356    // TODO: error display
357    let Ok(ast) = parse_oud2_to_ast(data) else {
358        error!("Failed to parse OuDiaSecond data.");
359        return;
360    };
361}