hrdf_parser/
storage.rs

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