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 kudari: Vec<OUD2Vehicle>,
101 nobori: Vec<OUD2Vehicle>,
102}
103
104struct OUD2Vehicle {
105 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 }
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 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>, ) {
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 let Ok(ast) = parse_oud2_to_ast(data) else {
358 error!("Failed to parse OuDiaSecond data.");
359 return;
360 };
361}