1use std::{path::Path, time::Instant};
2
3use chrono::{Days, NaiveDate};
4use rustc_hash::{FxHashMap, FxHashSet};
5use serde::{Deserialize, Serialize};
6
7use crate::{
8 JourneyError, JourneyId,
9 error::{HResult, HrdfError},
10 models::{
11 Attribute, BitField, Direction, ExchangeTimeAdministration, ExchangeTimeJourney,
12 ExchangeTimeLine, Holiday, InformationText, Journey, JourneyPlatform, Line, Model,
13 Platform, Stop, StopConnection, ThroughService, TimetableMetadataEntry, TransportCompany,
14 TransportType, Version,
15 },
16 parsing,
17 utils::{count_days_between_two_dates, timetable_end_date, timetable_start_date},
18};
19
20#[derive(Debug, Serialize, Deserialize)]
26pub struct DataStorage {
27 bit_fields: ResourceStorage<BitField>,
29 holidays: ResourceStorage<Holiday>,
30 timetable_metadata: ResourceStorage<TimetableMetadataEntry>,
31
32 attributes: ResourceStorage<Attribute>,
34 information_texts: ResourceStorage<InformationText>,
35 directions: ResourceStorage<Direction>,
36 lines: ResourceStorage<Line>,
37 transport_companies: ResourceStorage<TransportCompany>,
38 transport_types: ResourceStorage<TransportType>,
39
40 stops: ResourceStorage<Stop>,
42 stop_connections: ResourceStorage<StopConnection>,
43
44 journeys: ResourceStorage<Journey>,
46 journey_platform: ResourceStorage<JourneyPlatform>,
47 platforms: ResourceStorage<Platform>,
48 through_service: ResourceStorage<ThroughService>,
49
50 exchange_times_administration: ResourceStorage<ExchangeTimeAdministration>,
52 exchange_times_journey: ResourceStorage<ExchangeTimeJourney>,
53 exchange_times_line: ResourceStorage<ExchangeTimeLine>,
54
55 bit_fields_by_day: FxHashMap<NaiveDate, FxHashSet<i32>>,
57 bit_fields_by_stop_id: FxHashMap<i32, FxHashSet<i32>>,
58 journeys_by_stop_id_and_bit_field_id: FxHashMap<(i32, i32), Vec<i32>>,
59 stop_connections_by_stop_id: FxHashMap<i32, FxHashSet<i32>>,
60 bit_field_id_for_through_service_by_journey_id_stop_id:
61 FxHashMap<(JourneyId, JourneyId, i32), i32>,
62 exchange_times_administration_map: FxHashMap<(Option<i32>, String, String), i32>,
63 exchange_times_journey_map: FxHashMap<(i32, JourneyId, JourneyId), FxHashSet<i32>>,
64
65 default_exchange_time: (i16, i16), }
68
69impl DataStorage {
70 pub fn new(version: Version, path: &Path) -> HResult<Self> {
71 let complete = Instant::now();
73 let now = Instant::now();
74 let bit_fields = parsing::load_bit_fields(path)?;
75 log::info!("Time elapsed for bitfields parsing: {:?}", now.elapsed());
76 let now = Instant::now();
77 let holidays = parsing::load_holidays(path)?;
78 log::info!("Time elapsed for holidays parsing: {:?}", now.elapsed());
79
80 let now = Instant::now();
81 let timetable_metadata = parsing::load_timetable_metadata(path)?;
82 log::info!(
83 "Time elapsed for timetable_metadata parsing: {:?}",
84 now.elapsed()
85 );
86
87 let now = Instant::now();
89 let (attributes, attributes_pk_type_converter) = parsing::load_attributes(path)?;
90 log::info!("Time elapsed for attributes parsing: {:?}", now.elapsed());
91 let now = Instant::now();
92 let (directions, directions_pk_type_converter) = parsing::load_directions(path)?;
93 log::info!("Time elapsed for directions parsing: {:?}", now.elapsed());
94 let now = Instant::now();
95 let information_texts = parsing::load_information_texts(path)?;
96 log::info!(
97 "Time elapsed for information_texts parsing: {:?}",
98 now.elapsed()
99 );
100 let now = Instant::now();
101 let lines = parsing::load_lines(path)?;
102 log::info!("Time elapsed for line parsing: {:?}", now.elapsed());
103 let now = Instant::now();
104 let transport_companies = parsing::load_transport_companies(path)?;
105 log::info!(
106 "Time elapsed for transport_companies parsing: {:?}",
107 now.elapsed()
108 );
109 let now = Instant::now();
110 let (transport_types, transport_types_pk_type_converter) =
111 parsing::load_transport_types(path)?;
112 log::info!(
113 "Time elapsed for transport_types parsing: {:?}",
114 now.elapsed()
115 );
116
117 let now = Instant::now();
119 let stop_connections = parsing::load_stop_connections(path, &attributes_pk_type_converter)?;
120 log::info!(
121 "Time elapsed for stop_connections parsing: {:?}",
122 now.elapsed()
123 );
124 let now = Instant::now();
125 let (stops, default_exchange_time) = parsing::load_stops(version, path)?;
126 log::info!("Time elapsed for stops parsing: {:?}", now.elapsed());
127
128 let now = Instant::now();
130 let (journeys, journeys_pk_type_converter) = parsing::load_journeys(
131 path,
132 &transport_types_pk_type_converter,
133 &attributes_pk_type_converter,
134 &directions_pk_type_converter,
135 )?;
136 log::info!("Time elapsed for journeys parsing: {:?}", now.elapsed());
137
138 let now = Instant::now();
139 let (journey_platform, platforms) =
140 parsing::load_platforms(version, path, &journeys_pk_type_converter)?;
141 log::info!("Time elapsed for platforms parsing: {:?}", now.elapsed());
142 let now = Instant::now();
143 let through_service = parsing::load_through_service(path, &journeys_pk_type_converter)?;
144 log::info!(
145 "Time elapsed for through_service parsing: {:?}",
146 now.elapsed()
147 );
148
149 let now = Instant::now();
151 let exchange_times_administration = parsing::load_exchange_times_administration(path)?;
152 log::info!(
153 "Time elapsed for exchange_times_administration parsing: {:?}",
154 now.elapsed()
155 );
156 let now = Instant::now();
157 let exchange_times_journey =
158 parsing::load_exchange_times_journey(path, &journeys_pk_type_converter)?;
159 log::info!(
160 "Time elapsed for exchange_times_journey parsing: {:?}",
161 now.elapsed()
162 );
163 let now = Instant::now();
164 let exchange_times_line =
165 parsing::load_exchange_times_line(path, &transport_types_pk_type_converter)?;
166 log::info!(
167 "Time elapsed for exchange_times_line parsing: {:?}",
168 now.elapsed()
169 );
170
171 log::info!("Parsing of all HRDF files in {:?}", complete.elapsed());
172
173 log::info!("Building bit_fields_by_day...");
174 let bit_fields_by_day = create_bit_fields_by_day(&bit_fields, &timetable_metadata)?;
175 log::info!("Building bit_fields_by_stop_id...");
176 let bit_fields_by_stop_id = create_bit_fields_by_stop_id(&journeys)?;
177 log::info!("Building journeys by stop id and bit field_id...");
178 let journeys_by_stop_id_and_bit_field_id =
179 create_journeys_by_stop_id_and_bit_field_id(&journeys)?;
180 log::info!("Building stop connections by stop id...");
181 let bit_field_id_for_through_service_by_journey_id_stop_id =
182 create_bit_field_id_through_service_by_journey_id_stop_id(&through_service);
183 log::info!("Building stop connections by stop id...");
184 let stop_connections_by_stop_id = create_stop_connections_by_stop_id(&stop_connections);
185 log::info!("Building exchange times administration map...");
186 let exchange_times_administration_map =
187 create_exchange_times_administration_map(&exchange_times_administration);
188 log::info!("Building exchange times journey_map...");
189 let exchange_times_journey_map = create_exchange_times_journey_map(&exchange_times_journey);
190 log::info!("Building through service map...");
191
192 let data_storage = Self {
193 bit_fields,
195 holidays,
196 timetable_metadata,
197 attributes,
199 information_texts,
200 directions,
201 lines,
202 transport_companies,
203 transport_types,
204 stop_connections,
206 stops,
207 journeys,
209 journey_platform,
210 platforms,
211 through_service,
212 exchange_times_administration,
214 exchange_times_journey,
215 exchange_times_line,
216 bit_fields_by_day,
218 bit_fields_by_stop_id,
219 journeys_by_stop_id_and_bit_field_id,
220 stop_connections_by_stop_id,
221 bit_field_id_for_through_service_by_journey_id_stop_id,
222 exchange_times_administration_map,
223 exchange_times_journey_map,
224 default_exchange_time,
226 };
227
228 Ok(data_storage)
229 }
230
231 pub fn bit_fields(&self) -> &ResourceStorage<BitField> {
234 &self.bit_fields
235 }
236
237 pub fn journeys(&self) -> &ResourceStorage<Journey> {
238 &self.journeys
239 }
240
241 pub fn lines(&self) -> &ResourceStorage<Line> {
242 &self.lines
243 }
244
245 pub fn platforms(&self) -> &ResourceStorage<Platform> {
246 &self.platforms
247 }
248
249 pub fn stop_connections(&self) -> &ResourceStorage<StopConnection> {
250 &self.stop_connections
251 }
252
253 pub fn through_service(&self) -> &ResourceStorage<ThroughService> {
254 &self.through_service
255 }
256
257 pub fn stops(&self) -> &ResourceStorage<Stop> {
258 &self.stops
259 }
260
261 pub fn transport_types(&self) -> &ResourceStorage<TransportType> {
262 &self.transport_types
263 }
264
265 pub fn timetable_metadata(&self) -> &ResourceStorage<TimetableMetadataEntry> {
266 &self.timetable_metadata
267 }
268
269 pub fn exchange_times_administration(&self) -> &ResourceStorage<ExchangeTimeAdministration> {
270 &self.exchange_times_administration
271 }
272
273 pub fn exchange_times_journey(&self) -> &ResourceStorage<ExchangeTimeJourney> {
274 &self.exchange_times_journey
275 }
276
277 pub fn exchange_times_line(&self) -> &ResourceStorage<ExchangeTimeLine> {
278 &self.exchange_times_line
279 }
280
281 pub fn bit_fields_by_day(&self) -> &FxHashMap<NaiveDate, FxHashSet<i32>> {
282 &self.bit_fields_by_day
283 }
284
285 pub fn bit_fields_by_stop_id(&self) -> &FxHashMap<i32, FxHashSet<i32>> {
286 &self.bit_fields_by_stop_id
287 }
288
289 pub fn journeys_by_stop_id_and_bit_field_id(&self) -> &FxHashMap<(i32, i32), Vec<i32>> {
290 &self.journeys_by_stop_id_and_bit_field_id
291 }
292
293 pub fn stop_connections_by_stop_id(&self) -> &FxHashMap<i32, FxHashSet<i32>> {
294 &self.stop_connections_by_stop_id
295 }
296
297 pub fn bit_field_id_for_through_service_by_journey_id_stop_id(
298 &self,
299 ) -> &FxHashMap<(JourneyId, JourneyId, i32), i32> {
300 &self.bit_field_id_for_through_service_by_journey_id_stop_id
301 }
302
303 pub fn exchange_times_administration_map(
304 &self,
305 ) -> &FxHashMap<(Option<i32>, String, String), i32> {
306 &self.exchange_times_administration_map
307 }
308
309 pub fn exchange_times_journey_map(
310 &self,
311 ) -> &FxHashMap<(i32, JourneyId, JourneyId), FxHashSet<i32>> {
312 &self.exchange_times_journey_map
313 }
314
315 pub fn default_exchange_time(&self) -> (i16, i16) {
316 self.default_exchange_time
317 }
318}
319
320#[derive(Debug, Serialize, Deserialize)]
325pub struct ResourceStorage<M: Model<M>> {
326 data: FxHashMap<M::K, M>,
327}
328
329impl<M: Model<M>> ResourceStorage<M> {
330 pub fn new(data: FxHashMap<M::K, M>) -> Self {
331 Self { data }
332 }
333
334 pub fn data(&self) -> &FxHashMap<M::K, M> {
335 &self.data
336 }
337
338 pub fn find(&self, k: M::K) -> Option<&M> {
339 self.data().get(&k)
341 }
342
343 pub fn entries(&self) -> Vec<&M> {
344 self.data.values().collect()
345 }
346
347 pub fn resolve_ids(&self, ids: &FxHashSet<M::K>) -> Option<Vec<&M>> {
348 ids.iter().map(|&id| self.find(id)).collect()
349 }
350}
351
352fn create_bit_fields_by_day(
357 bit_fields: &ResourceStorage<BitField>,
358 timetable_metadata: &ResourceStorage<TimetableMetadataEntry>,
359) -> HResult<FxHashMap<NaiveDate, FxHashSet<i32>>> {
360 let start_date = timetable_start_date(timetable_metadata)?;
361 let num_days =
362 count_days_between_two_dates(start_date, timetable_end_date(timetable_metadata)?);
363
364 let dates = (0..num_days)
365 .map(|i| {
366 let i = i.try_into().unwrap();
367 start_date
368 .checked_add_days(Days::new(i))
370 .ok_or(HrdfError::FailedToAddDays(start_date, i))
371 })
372 .collect::<HResult<Vec<NaiveDate>>>()?;
373
374 let mut map = FxHashMap::default();
375 dates.iter().for_each(|date| {
376 map.entry(*date).or_insert(FxHashSet::default()).insert(0);
377 });
378
379 bit_fields
380 .data()
381 .keys()
382 .try_fold(map, |mut acc, bit_field_id| {
383 let bit_field = bit_fields
384 .find(*bit_field_id)
385 .ok_or(HrdfError::BitFieldIdNotFound(*bit_field_id))?;
386 let indexes: Vec<usize> = bit_field
387 .bits()
388 .iter()
389 .skip(2)
391 .enumerate()
392 .filter(|&(ref i, &x)| *i < num_days && x == 1)
393 .map(|(i, _)| i)
394 .collect();
395
396 indexes.iter().for_each(|&i| {
397 acc.entry(dates[i]).or_default().insert(bit_field.id());
398 });
399
400 Ok(acc)
401 })
402}
403
404fn create_bit_fields_by_stop_id(
405 journeys: &ResourceStorage<Journey>,
406) -> HResult<FxHashMap<i32, FxHashSet<i32>>> {
407 journeys.entries().into_iter().try_fold(
408 FxHashMap::default(),
409 |mut acc: FxHashMap<i32, FxHashSet<i32>>, journey| {
410 journey.route().iter().try_for_each(|route_entry| {
411 acc.entry(route_entry.stop_id())
412 .or_default()
413 .insert(journey.bit_field_id()?.unwrap_or(0));
415 Ok::<(), JourneyError>(())
416 })?;
417 Ok(acc)
418 },
419 )
420}
421
422fn create_journeys_by_stop_id_and_bit_field_id(
423 journeys: &ResourceStorage<Journey>,
424) -> HResult<FxHashMap<(i32, i32), Vec<i32>>> {
425 journeys.entries().into_iter().try_fold(
426 FxHashMap::default(),
427 |mut acc: FxHashMap<(i32, i32), Vec<i32>>, journey| {
428 journey.route().iter().try_for_each(|route_entry| {
429 acc.entry((route_entry.stop_id(), journey.bit_field_id()?.unwrap_or(0)))
431 .or_default()
432 .push(journey.id());
433 Ok::<(), JourneyError>(())
434 })?;
435 Ok(acc)
436 },
437 )
438}
439
440fn create_bit_field_id_through_service_by_journey_id_stop_id(
442 through_services: &ResourceStorage<ThroughService>,
443) -> FxHashMap<(JourneyId, JourneyId, i32), i32> {
444 through_services
445 .entries()
446 .into_iter()
447 .fold(FxHashMap::default(), |mut acc, through_service| {
448 let journey_1_id = through_service.journey_1_id();
449 let journey_2_id = through_service.journey_2_id();
450 let journey_stop_id = through_service.journey_1_stop_id();
451 let bit_field_id = through_service.bit_field_id();
452
453 acc.insert(
454 (journey_1_id.clone(), journey_2_id.clone(), journey_stop_id),
455 bit_field_id,
456 );
457 acc
458 })
459}
460
461fn create_stop_connections_by_stop_id(
462 stop_connections: &ResourceStorage<StopConnection>,
463) -> FxHashMap<i32, FxHashSet<i32>> {
464 stop_connections
465 .entries()
466 .into_iter()
467 .fold(FxHashMap::default(), |mut acc, stop_connection| {
468 acc.entry(stop_connection.stop_id_1())
469 .or_default()
470 .insert(stop_connection.id());
471 acc
472 })
473}
474
475fn create_exchange_times_journey_map(
476 exchange_times_journey: &ResourceStorage<ExchangeTimeJourney>,
477) -> FxHashMap<(i32, JourneyId, JourneyId), FxHashSet<i32>> {
478 exchange_times_journey.entries().into_iter().fold(
479 FxHashMap::default(),
480 |mut acc, exchange_time| {
481 let key = (
482 exchange_time.stop_id(),
483 (
484 exchange_time.journey_legacy_id_1(),
485 exchange_time.administration_1().to_string(),
486 ),
487 (
488 exchange_time.journey_legacy_id_2(),
489 exchange_time.administration_2().to_string(),
490 ),
491 );
492
493 acc.entry(key).or_default().insert(exchange_time.id());
494 acc
495 },
496 )
497}
498
499fn create_exchange_times_administration_map(
500 exchange_times_administration: &ResourceStorage<ExchangeTimeAdministration>,
501) -> FxHashMap<(Option<i32>, String, String), i32> {
502 exchange_times_administration.entries().into_iter().fold(
503 FxHashMap::default(),
504 |mut acc, exchange_time| {
505 let key = (
506 exchange_time.stop_id(),
507 exchange_time.administration_1().into(),
508 exchange_time.administration_2().into(),
509 );
510
511 acc.insert(key, exchange_time.id());
512 acc
513 },
514 )
515}