hrdf_parser/
models.rs

1use std::{
2    collections::BTreeSet,
3    hash::{DefaultHasher, Hash, Hasher},
4};
5
6use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
7use rustc_hash::FxHashMap;
8use serde::{Deserialize, Serialize};
9use strum_macros::{self, Display, EnumString};
10
11use thiserror::Error;
12
13use crate::{
14    error::{HResult, HrdfError},
15    storage::DataStorage,
16    utils::{add_1_day, sub_1_day},
17};
18
19pub(crate) type JourneyId = (i32, String); // (legacy_id, administration)
20
21// ------------------------------------------------------------------------------------------------
22// --- Model
23// ------------------------------------------------------------------------------------------------
24
25pub trait Model<M: Model<M>> {
26    // Primary key type.
27    type K: Copy + Eq + Hash + Serialize + for<'a> Deserialize<'a>;
28
29    fn id(&self) -> M::K;
30}
31
32macro_rules! impl_Model {
33    ($m:ty) => {
34        impl Model<$m> for $m {
35            type K = i32;
36
37            fn id(&self) -> Self::K {
38                self.id
39            }
40        }
41    };
42}
43
44// ------------------------------------------------------------------------------------------------
45// --- Attribute
46// ------------------------------------------------------------------------------------------------
47
48#[derive(Debug, Serialize, Deserialize)]
49pub struct Attribute {
50    id: i32,
51    designation: String,
52    stop_scope: i16,
53    main_sorting_priority: i16,
54    secondary_sorting_priority: i16,
55    description: FxHashMap<Language, String>,
56}
57
58impl_Model!(Attribute);
59
60impl Attribute {
61    pub fn new(
62        id: i32,
63        designation: String,
64        stop_scope: i16,
65        main_sorting_priority: i16,
66        secondary_sorting_priority: i16,
67    ) -> Self {
68        Self {
69            id,
70            designation,
71            stop_scope,
72            main_sorting_priority,
73            secondary_sorting_priority,
74            description: FxHashMap::default(),
75        }
76    }
77
78    // Getters/Setters
79
80    pub fn set_description(&mut self, language: Language, value: &str) {
81        self.description.insert(language, value.to_string());
82    }
83}
84
85// ------------------------------------------------------------------------------------------------
86// --- BitField
87// ------------------------------------------------------------------------------------------------
88
89#[derive(Debug, Serialize, Deserialize)]
90pub struct BitField {
91    id: i32,
92    bits: Vec<u8>,
93}
94
95impl_Model!(BitField);
96
97impl BitField {
98    pub fn new(id: i32, bits: Vec<u8>) -> Self {
99        Self { id, bits }
100    }
101
102    // Getters/Setters
103
104    pub fn bits(&self) -> &Vec<u8> {
105        &self.bits
106    }
107}
108
109// ------------------------------------------------------------------------------------------------
110// --- Color
111// ------------------------------------------------------------------------------------------------
112
113#[derive(Debug, Default, Serialize, Deserialize)]
114pub struct Color {
115    r: i16,
116    g: i16,
117    b: i16,
118}
119
120#[allow(unused)]
121impl Color {
122    pub fn new(r: i16, g: i16, b: i16) -> Self {
123        Self { r, g, b }
124    }
125
126    // Getters/Setters
127
128    pub fn r(&self) -> i16 {
129        self.r
130    }
131
132    pub fn g(&self) -> i16 {
133        self.g
134    }
135
136    pub fn b(&self) -> i16 {
137        self.b
138    }
139}
140
141// ------------------------------------------------------------------------------------------------
142// --- CoordinateSystem
143// ------------------------------------------------------------------------------------------------
144
145#[derive(Clone, Copy, Debug, Default, Display, Eq, Hash, PartialEq, Serialize, Deserialize)]
146pub enum CoordinateSystem {
147    #[default]
148    LV95,
149    WGS84,
150}
151
152// ------------------------------------------------------------------------------------------------
153// --- Coordinates
154// ------------------------------------------------------------------------------------------------
155
156#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize)]
157pub struct Coordinates {
158    coordinate_system: CoordinateSystem,
159    x: f64,
160    y: f64,
161}
162
163#[allow(unused)]
164impl Coordinates {
165    pub fn new(coordinate_system: CoordinateSystem, x: f64, y: f64) -> Self {
166        Self {
167            coordinate_system,
168            x,
169            y,
170        }
171    }
172
173    // Getters/Setters
174
175    pub fn easting(&self) -> Option<f64> {
176        match self.coordinate_system {
177            CoordinateSystem::LV95 => Some(self.x),
178            CoordinateSystem::WGS84 => None,
179        }
180    }
181
182    pub fn northing(&self) -> Option<f64> {
183        match self.coordinate_system {
184            CoordinateSystem::LV95 => Some(self.y),
185            CoordinateSystem::WGS84 => None,
186        }
187    }
188
189    pub fn latitude(&self) -> Option<f64> {
190        match self.coordinate_system {
191            CoordinateSystem::WGS84 => Some(self.x),
192            CoordinateSystem::LV95 => None,
193        }
194    }
195
196    pub fn longitude(&self) -> Option<f64> {
197        match self.coordinate_system {
198            CoordinateSystem::WGS84 => Some(self.y),
199            CoordinateSystem::LV95 => None,
200        }
201    }
202}
203
204// ------------------------------------------------------------------------------------------------
205// --- Direction
206// ------------------------------------------------------------------------------------------------
207
208#[derive(Debug, Serialize, Deserialize)]
209pub struct Direction {
210    id: i32,
211    name: String,
212}
213
214impl_Model!(Direction);
215
216impl Direction {
217    pub fn new(id: i32, name: String) -> Self {
218        Self { id, name }
219    }
220}
221
222// ------------------------------------------------------------------------------------------------
223// --- DirectionType
224// ------------------------------------------------------------------------------------------------
225
226#[derive(
227    Clone, Copy, Debug, Default, Display, Eq, Hash, PartialEq, EnumString, Serialize, Deserialize,
228)]
229pub enum DirectionType {
230    #[default]
231    #[strum(serialize = "R")]
232    Outbound,
233
234    #[strum(serialize = "H")]
235    Return,
236}
237
238// ------------------------------------------------------------------------------------------------
239// --- Holiday
240// ------------------------------------------------------------------------------------------------
241
242#[derive(Debug, Serialize, Deserialize)]
243pub struct Holiday {
244    id: i32,
245    date: NaiveDate,
246    name: FxHashMap<Language, String>,
247}
248
249impl_Model!(Holiday);
250
251impl Holiday {
252    pub fn new(id: i32, date: NaiveDate, name: FxHashMap<Language, String>) -> Self {
253        Self { id, date, name }
254    }
255}
256
257// ------------------------------------------------------------------------------------------------
258// --- ExchangeTimeAdministration
259// ------------------------------------------------------------------------------------------------
260
261#[derive(Debug, Serialize, Deserialize)]
262pub struct ExchangeTimeAdministration {
263    id: i32,
264    stop_id: Option<i32>, // A None value means that the exchange time applies to all stops if there is no specific entry for the stop and the 2 administrations.
265    administration_1: String,
266    administration_2: String,
267    duration: i16, // Exchange time from administration 1 to administration 2 is in minutes.
268}
269
270impl_Model!(ExchangeTimeAdministration);
271
272impl ExchangeTimeAdministration {
273    pub fn new(
274        id: i32,
275        stop_id: Option<i32>,
276        administration_1: String,
277        administration_2: String,
278        duration: i16,
279    ) -> Self {
280        Self {
281            id,
282            stop_id,
283            administration_1,
284            administration_2,
285            duration,
286        }
287    }
288
289    // Getters/Setters
290
291    pub fn stop_id(&self) -> Option<i32> {
292        self.stop_id
293    }
294
295    pub fn administration_1(&self) -> &str {
296        &self.administration_1
297    }
298
299    pub fn administration_2(&self) -> &str {
300        &self.administration_2
301    }
302
303    pub fn duration(&self) -> i16 {
304        self.duration
305    }
306}
307
308// ------------------------------------------------------------------------------------------------
309// --- ExchangeTimeJourney
310// ------------------------------------------------------------------------------------------------
311
312#[derive(Debug, Serialize, Deserialize)]
313pub struct ExchangeTimeJourney {
314    id: i32,
315    stop_id: i32,
316    journey_legacy_id_1: i32,
317    administration_1: String,
318    journey_legacy_id_2: i32,
319    administration_2: String,
320    duration: i16, // Exchange time from journey 1 to journey 2 is in minutes.
321    is_guaranteed: bool,
322    bit_field_id: Option<i32>,
323}
324
325impl_Model!(ExchangeTimeJourney);
326
327impl ExchangeTimeJourney {
328    pub fn new(
329        id: i32,
330        stop_id: i32,
331        (journey_legacy_id_1, administration_1): JourneyId,
332        (journey_legacy_id_2, administration_2): JourneyId,
333        duration: i16,
334        is_guaranteed: bool,
335        bit_field_id: Option<i32>,
336    ) -> Self {
337        Self {
338            id,
339            stop_id,
340            journey_legacy_id_1,
341            administration_1,
342            journey_legacy_id_2,
343            administration_2,
344            duration,
345            is_guaranteed,
346            bit_field_id,
347        }
348    }
349
350    // Getters/Setters
351
352    pub fn stop_id(&self) -> i32 {
353        self.stop_id
354    }
355
356    pub fn journey_legacy_id_1(&self) -> i32 {
357        self.journey_legacy_id_1
358    }
359
360    pub fn administration_1(&self) -> &str {
361        &self.administration_1
362    }
363
364    pub fn journey_legacy_id_2(&self) -> i32 {
365        self.journey_legacy_id_2
366    }
367
368    pub fn administration_2(&self) -> &str {
369        &self.administration_2
370    }
371
372    pub fn duration(&self) -> i16 {
373        self.duration
374    }
375
376    pub fn bit_field_id(&self) -> Option<i32> {
377        self.bit_field_id
378    }
379}
380
381// ------------------------------------------------------------------------------------------------
382// --- ExchangeTimeLine
383// ------------------------------------------------------------------------------------------------
384
385#[derive(Debug, Serialize, Deserialize)]
386pub struct ExchangeTimeLine {
387    id: i32,
388    stop_id: Option<i32>,
389    line_1: LineInfo,
390    line_2: LineInfo,
391    duration: i16, // Exchange time from line 1 to line 2 is in minutes.
392    is_guaranteed: bool,
393}
394
395impl_Model!(ExchangeTimeLine);
396
397#[derive(Debug, Serialize, Deserialize)]
398pub(crate) struct LineInfo {
399    administration: String,
400    transport_type_id: i32,
401    line_id: Option<String>,
402    direction: Option<DirectionType>,
403}
404
405impl LineInfo {
406    pub(crate) fn new(
407        administration: String,
408        transport_type_id: i32,
409        line_id: Option<String>,
410        direction: Option<DirectionType>,
411    ) -> Self {
412        Self {
413            administration,
414            transport_type_id,
415            line_id,
416            direction,
417        }
418    }
419}
420
421impl ExchangeTimeLine {
422    pub(crate) fn new(
423        id: i32,
424        stop_id: Option<i32>,
425        line_1: LineInfo,
426        line_2: LineInfo,
427        duration: i16,
428        is_guaranteed: bool,
429    ) -> Self {
430        Self {
431            id,
432            stop_id,
433            line_1,
434            line_2,
435            duration,
436            is_guaranteed,
437        }
438    }
439}
440
441// ------------------------------------------------------------------------------------------------
442// --- InformationText
443// ------------------------------------------------------------------------------------------------
444
445#[derive(Debug, Serialize, Deserialize)]
446pub struct InformationText {
447    id: i32,
448    content: FxHashMap<Language, String>,
449}
450
451impl_Model!(InformationText);
452
453impl InformationText {
454    pub fn new(id: i32) -> Self {
455        Self {
456            id,
457            content: FxHashMap::default(),
458        }
459    }
460
461    // Getters/Setters
462
463    pub fn set_content(&mut self, language: Language, value: &str) {
464        self.content.insert(language, value.to_string());
465    }
466}
467
468// ------------------------------------------------------------------------------------------------
469// --- Journey
470// ------------------------------------------------------------------------------------------------
471
472#[derive(Debug, Default, Serialize, Deserialize)]
473pub struct Journey {
474    id: i32,
475    legacy_id: i32,
476    administration: String,
477    metadata: FxHashMap<JourneyMetadataType, Vec<JourneyMetadataEntry>>,
478    route: Vec<JourneyRouteEntry>,
479}
480
481impl_Model!(Journey);
482
483impl Journey {
484    pub fn new(id: i32, legacy_id: i32, administration: String) -> Self {
485        Self {
486            id,
487            legacy_id,
488            administration,
489            metadata: FxHashMap::default(),
490            route: Vec::new(),
491        }
492    }
493
494    // Getters/Setters
495
496    pub fn administration(&self) -> &str {
497        &self.administration
498    }
499
500    pub fn legacy_id(&self) -> i32 {
501        self.legacy_id
502    }
503
504    fn metadata(&self) -> &FxHashMap<JourneyMetadataType, Vec<JourneyMetadataEntry>> {
505        &self.metadata
506    }
507
508    pub fn route(&self) -> &Vec<JourneyRouteEntry> {
509        &self.route
510    }
511
512    // Functions
513
514    pub fn add_metadata_entry(&mut self, k: JourneyMetadataType, v: JourneyMetadataEntry) {
515        self.metadata.entry(k).or_default().push(v);
516    }
517
518    pub fn add_route_entry(&mut self, entry: JourneyRouteEntry) {
519        self.route.push(entry);
520    }
521
522    pub(crate) fn bit_field_id(&self) -> JResult<Option<i32>> {
523        let entry = self
524            .metadata()
525            .get(&JourneyMetadataType::BitField)
526            .ok_or(JourneyError::MissingBitFieldMetadata)?;
527
528        Ok(entry
529            .first()
530            .ok_or(JourneyError::EmptyJourneyMetadata)?
531            .bit_field_id)
532    }
533
534    pub fn transport_type_id(&self) -> HResult<i32> {
535        let entry = self
536            .metadata()
537            .get(&JourneyMetadataType::TransportType)
538            .ok_or(JourneyError::MissingTransportType)?;
539        entry
540            .first()
541            .ok_or::<HrdfError>((JourneyError::EmptyJourneyMetadata).into())?
542            .resource_id
543            .ok_or(JourneyError::MissingRessourceId.into())
544    }
545
546    pub fn transport_type<'a>(
547        &'a self,
548        data_storage: &'a DataStorage,
549    ) -> HResult<&'a TransportType> {
550        let transport_id = self.transport_type_id()?;
551        data_storage
552            .transport_types()
553            .find(transport_id)
554            .ok_or(JourneyError::TransportIdNotFound(transport_id).into())
555    }
556
557    pub fn first_stop_id(&self) -> HResult<i32> {
558        Ok(self
559            .route
560            .first()
561            .ok_or(JourneyError::EmptyRoute)?
562            .stop_id())
563    }
564
565    pub fn last_stop_id(&self) -> HResult<i32> {
566        Ok(self.route.last().ok_or(JourneyError::EmptyRoute)?.stop_id())
567    }
568
569    pub fn is_last_stop(&self, stop_id: i32, ignore_loop: bool) -> HResult<bool> {
570        if ignore_loop && self.first_stop_id()? == self.last_stop_id()? {
571            Ok(false)
572        } else {
573            Ok(stop_id == self.last_stop_id()?)
574        }
575    }
576
577    pub fn count_stops(&self, departure_stop_id: i32, arrival_stop_id: i32) -> usize {
578        self.route()
579            .iter()
580            .skip_while(|stop| stop.stop_id() != departure_stop_id)
581            .take_while(|stop| stop.stop_id() != arrival_stop_id)
582            .count()
583            + 1
584    }
585
586    pub fn hash_route(&self, departure_stop_id: i32) -> Option<u64> {
587        let index = self
588            .route
589            .iter()
590            .position(|route_entry| route_entry.stop_id() == departure_stop_id)?;
591
592        let mut hasher = DefaultHasher::new();
593        self.route
594            .iter()
595            .skip(index)
596            .map(|route_entry| route_entry.stop_id())
597            .collect::<BTreeSet<_>>()
598            .hash(&mut hasher);
599        Some(hasher.finish())
600    }
601
602    /// unwrap: Do not call this function if the stop is not part of the route.
603    /// unwrap: Do not call this function if the stop has no departure time (only the last stop has no departure time).
604    pub fn departure_time_of(&self, stop_id: i32) -> HResult<(NaiveTime, bool)> {
605        let route = self.route();
606        let index = route
607            .iter()
608            .position(|route_entry| route_entry.stop_id() == stop_id)
609            .ok_or_else(|| HrdfError::MissingStopId(stop_id))?;
610        let departure_time = route[index]
611            .departure_time()
612            .ok_or_else(|| HrdfError::MissingDepartureTime(index))?;
613
614        Ok((
615            departure_time,
616            // The departure time is on the next day if this evaluates to true.
617            departure_time
618                < route
619                    .first()
620                    .ok_or(HrdfError::MissingRoute)?
621                    .departure_time()
622                    .ok_or(HrdfError::MissingDepartureTime(0))?,
623        ))
624    }
625
626    /// The date must correspond to the route's first entry.
627    /// Do not call this function if the stop is not part of the route.
628    /// Do not call this function if the stop has no departure time (only the last stop has no departure time).
629    pub fn departure_at_of(&self, stop_id: i32, date: NaiveDate) -> HResult<NaiveDateTime> {
630        match self.departure_time_of(stop_id)? {
631            (departure_time, false) => Ok(NaiveDateTime::new(date, departure_time)),
632            (departure_time, true) => Ok(NaiveDateTime::new(add_1_day(date)?, departure_time)),
633        }
634    }
635
636    /// The date must be associated with the origin_stop_id.
637    /// Do not call this function if the stop is not part of the route.
638    pub fn departure_at_of_with_origin(
639        &self,
640        stop_id: i32,
641        date: NaiveDate,
642        // If it's not a departure date, it's an arrival date.
643        is_departure_date: bool,
644        origin_stop_id: i32,
645    ) -> HResult<NaiveDateTime> {
646        let (departure_time, is_next_day) = self.departure_time_of(stop_id)?;
647        let (_, origin_is_next_day) = if is_departure_date {
648            self.departure_time_of(origin_stop_id)?
649        } else {
650            self.arrival_time_of(origin_stop_id)?
651        };
652
653        match (is_next_day, origin_is_next_day) {
654            (true, false) => Ok(NaiveDateTime::new(add_1_day(date)?, departure_time)),
655            (false, true) => Ok(NaiveDateTime::new(sub_1_day(date)?, departure_time)),
656            _ => Ok(NaiveDateTime::new(date, departure_time)),
657        }
658    }
659
660    pub fn arrival_time_of(&self, stop_id: i32) -> HResult<(NaiveTime, bool)> {
661        let route = self.route();
662        let index = route
663            .iter()
664            // The first route entry has no arrival time.
665            .skip(1)
666            .position(|route_entry| route_entry.stop_id() == stop_id)
667            .map(|i| i + 1)
668            .ok_or_else(|| HrdfError::MissingStopId(stop_id))?;
669        let arrival_time = route[index]
670            .arrival_time()
671            .ok_or_else(|| HrdfError::MissingArrivalTime(index))?;
672
673        Ok((
674            arrival_time,
675            // The arrival time is on the next day if this evaluates to true.
676            arrival_time
677                < route
678                    .first()
679                    .ok_or(HrdfError::MissingRoute)?
680                    .departure_time()
681                    .ok_or(HrdfError::MissingDepartureTime(0))?,
682        ))
683    }
684
685    /// The date must be associated with the origin_stop_id.
686    pub fn arrival_at_of_with_origin(
687        &self,
688        stop_id: i32,
689        date: NaiveDate,
690        // If it's not a departure date, it's an arrival date.
691        is_departure_date: bool,
692        origin_stop_id: i32,
693    ) -> HResult<NaiveDateTime> {
694        let (arrival_time, is_next_day) = self.arrival_time_of(stop_id)?;
695        let (_, origin_is_next_day) = if is_departure_date {
696            self.departure_time_of(origin_stop_id)?
697        } else {
698            self.arrival_time_of(origin_stop_id)?
699        };
700
701        match (is_next_day, origin_is_next_day) {
702            (true, false) => Ok(NaiveDateTime::new(add_1_day(date)?, arrival_time)),
703            (false, true) => Ok(NaiveDateTime::new(sub_1_day(date)?, arrival_time)),
704            _ => Ok(NaiveDateTime::new(date, arrival_time)),
705        }
706    }
707
708    /// Excluding departure stop.
709    pub fn route_section(
710        &self,
711        departure_stop_id: i32,
712        arrival_stop_id: i32,
713    ) -> Vec<&JourneyRouteEntry> {
714        let mut route_iter = self.route().iter();
715
716        for route_entry in route_iter.by_ref() {
717            if route_entry.stop_id() == departure_stop_id {
718                break;
719            }
720        }
721
722        let mut result = Vec::new();
723
724        for route_entry in route_iter {
725            result.push(route_entry);
726
727            if route_entry.stop_id() == arrival_stop_id {
728                break;
729            }
730        }
731
732        result
733    }
734}
735
736type JResult<T> = Result<T, JourneyError>;
737
738#[derive(Debug, Error)]
739pub enum JourneyError {
740    #[error("Missing MitField Metadata")]
741    MissingBitFieldMetadata,
742    #[error("JourneyMetaData is empty")]
743    EmptyJourneyMetadata,
744    #[error("Missing Transport Type Metadata")]
745    MissingTransportType,
746    #[error("Missing Reoussirce Id")]
747    MissingRessourceId,
748    #[error("Transport Id: {0} not found")]
749    TransportIdNotFound(i32),
750    #[error("Empty Route")]
751    EmptyRoute,
752    #[error("Stop Id: {0} not found")]
753    StopIdNotFound(i32),
754}
755
756// ------------------------------------------------------------------------------------------------
757// --- JourneyMetadataType
758// ------------------------------------------------------------------------------------------------
759
760#[derive(Clone, Copy, Debug, Default, Display, Eq, Hash, PartialEq, Serialize, Deserialize)]
761pub enum JourneyMetadataType {
762    #[default]
763    Attribute,
764    BitField,
765    Direction,
766    InformationText,
767    Line,
768    ExchangeTimeBoarding,
769    ExchangeTimeDisembarking,
770    TransportType,
771}
772
773// ------------------------------------------------------------------------------------------------
774// --- JourneyMetadataEntry
775// ------------------------------------------------------------------------------------------------
776
777#[derive(Debug, Serialize, Deserialize)]
778pub struct JourneyMetadataEntry {
779    from_stop_id: Option<i32>,
780    until_stop_id: Option<i32>,
781    resource_id: Option<i32>,
782    bit_field_id: Option<i32>,
783    departure_time: Option<NaiveTime>,
784    arrival_time: Option<NaiveTime>,
785    extra_field_1: Option<String>,
786    extra_field_2: Option<i32>,
787}
788
789impl JourneyMetadataEntry {
790    #[allow(clippy::too_many_arguments)]
791    pub fn new(
792        from_stop_id: Option<i32>,
793        until_stop_id: Option<i32>,
794        resource_id: Option<i32>,
795        bit_field_id: Option<i32>,
796        departure_time: Option<NaiveTime>,
797        arrival_time: Option<NaiveTime>,
798        extra_field_1: Option<String>,
799        extra_field_2: Option<i32>,
800    ) -> Self {
801        Self {
802            from_stop_id,
803            until_stop_id,
804            resource_id,
805            bit_field_id,
806            departure_time,
807            arrival_time,
808            extra_field_1,
809            extra_field_2,
810        }
811    }
812}
813
814// ------------------------------------------------------------------------------------------------
815// --- JourneyRouteEntry
816// ------------------------------------------------------------------------------------------------
817
818#[derive(Debug, Serialize, Deserialize)]
819pub struct JourneyRouteEntry {
820    stop_id: i32,
821    arrival_time: Option<NaiveTime>,
822    departure_time: Option<NaiveTime>,
823}
824
825impl JourneyRouteEntry {
826    pub fn new(
827        stop_id: i32,
828        arrival_time: Option<NaiveTime>,
829        departure_time: Option<NaiveTime>,
830    ) -> Self {
831        Self {
832            stop_id,
833            arrival_time,
834            departure_time,
835        }
836    }
837
838    // Getters/Setters
839
840    pub fn stop_id(&self) -> i32 {
841        self.stop_id
842    }
843
844    pub fn arrival_time(&self) -> &Option<NaiveTime> {
845        &self.arrival_time
846    }
847
848    pub fn departure_time(&self) -> &Option<NaiveTime> {
849        &self.departure_time
850    }
851
852    // Functions
853
854    pub fn stop<'a>(&'a self, data_storage: &'a DataStorage) -> HResult<&'a Stop> {
855        let stop_id = self.stop_id();
856        data_storage
857            .stops()
858            .find(stop_id)
859            .ok_or(JourneyError::StopIdNotFound(stop_id).into())
860    }
861}
862
863// ------------------------------------------------------------------------------------------------
864// --- JourneyPlatform
865// ------------------------------------------------------------------------------------------------
866
867#[derive(Debug, Serialize, Deserialize)]
868pub struct JourneyPlatform {
869    journey_legacy_id: i32,
870    administration: String,
871    platform_id: i32,
872    time: Option<NaiveTime>,
873    bit_field_id: Option<i32>,
874}
875
876impl JourneyPlatform {
877    pub fn new(
878        journey_legacy_id: i32,
879        administration: String,
880        platform_id: i32,
881        time: Option<NaiveTime>,
882        bit_field_id: Option<i32>,
883    ) -> Self {
884        Self {
885            journey_legacy_id,
886            administration,
887            platform_id,
888            time,
889            bit_field_id,
890        }
891    }
892}
893
894impl Model<JourneyPlatform> for JourneyPlatform {
895    type K = (i32, i32);
896
897    fn id(&self) -> Self::K {
898        (self.journey_legacy_id, self.platform_id)
899    }
900}
901
902// ------------------------------------------------------------------------------------------------
903// --- Language
904// ------------------------------------------------------------------------------------------------
905
906#[derive(
907    Clone, Copy, Debug, Default, Display, Eq, Hash, PartialEq, EnumString, Serialize, Deserialize,
908)]
909pub enum Language {
910    #[default]
911    #[strum(serialize = "deu", serialize = "DE")]
912    German,
913
914    #[strum(serialize = "fra", serialize = "FR")]
915    French,
916
917    #[strum(serialize = "ita", serialize = "IT")]
918    Italian,
919
920    #[strum(serialize = "eng", serialize = "EN")]
921    English,
922}
923
924// ------------------------------------------------------------------------------------------------
925// --- Line
926// ------------------------------------------------------------------------------------------------
927
928#[derive(Debug, Default, Serialize, Deserialize)]
929pub struct Line {
930    id: i32,
931    name: String,
932    short_name: String,
933    long_name: String,
934    internal_designation: String,
935    text_color: Color,
936    background_color: Color,
937}
938
939impl_Model!(Line);
940
941impl Line {
942    pub fn new(id: i32, name: String) -> Self {
943        Self {
944            id,
945            name,
946            short_name: String::default(),
947            long_name: String::default(),
948            internal_designation: String::default(),
949            text_color: Color::default(),
950            background_color: Color::default(),
951        }
952    }
953
954    // Getters/Setters
955
956    pub fn set_short_name(&mut self, value: String) {
957        self.short_name = value;
958    }
959
960    pub fn set_long_name(&mut self, value: String) {
961        self.long_name = value;
962    }
963
964    pub fn set_internal_designation(&mut self, value: String) {
965        self.internal_designation = value;
966    }
967
968    pub fn set_text_color(&mut self, value: Color) {
969        self.text_color = value;
970    }
971
972    pub fn set_background_color(&mut self, value: Color) {
973        self.background_color = value;
974    }
975}
976
977// ------------------------------------------------------------------------------------------------
978// --- Platform
979// ------------------------------------------------------------------------------------------------
980
981#[derive(Debug, Serialize, Deserialize)]
982pub struct Platform {
983    id: i32,
984    name: String,
985    sectors: Option<String>,
986    stop_id: i32,
987    sloid: String,
988    lv95_coordinates: Coordinates,
989    wgs84_coordinates: Coordinates,
990}
991
992impl_Model!(Platform);
993
994impl Platform {
995    pub fn new(id: i32, name: String, sectors: Option<String>, stop_id: i32) -> Self {
996        Self {
997            id,
998            name,
999            sectors,
1000            stop_id,
1001            sloid: String::default(),
1002            lv95_coordinates: Coordinates::default(),
1003            wgs84_coordinates: Coordinates::default(),
1004        }
1005    }
1006
1007    // Getters/Setters
1008
1009    pub fn set_sloid(&mut self, value: String) {
1010        self.sloid = value;
1011    }
1012
1013    pub fn set_lv95_coordinates(&mut self, value: Coordinates) {
1014        self.lv95_coordinates = value;
1015    }
1016
1017    pub fn set_wgs84_coordinates(&mut self, value: Coordinates) {
1018        self.wgs84_coordinates = value;
1019    }
1020}
1021
1022// ------------------------------------------------------------------------------------------------
1023// --- Stop
1024// ------------------------------------------------------------------------------------------------
1025
1026#[derive(Debug, Serialize, Deserialize)]
1027pub struct Stop {
1028    id: i32,
1029    name: String,
1030    long_name: Option<String>,
1031    abbreviation: Option<String>,
1032    synonyms: Option<Vec<String>>,
1033    lv95_coordinates: Option<Coordinates>,
1034    wgs84_coordinates: Option<Coordinates>,
1035    exchange_priority: i16,
1036    exchange_flag: i16,
1037    exchange_time: Option<(i16, i16)>, // (InterCity exchange time, Exchange time for all other journey types)
1038    restrictions: i16,
1039    sloid: String,
1040    boarding_areas: Vec<String>,
1041}
1042
1043impl_Model!(Stop);
1044
1045impl Stop {
1046    pub fn new(
1047        id: i32,
1048        name: String,
1049        long_name: Option<String>,
1050        abbreviation: Option<String>,
1051        synonyms: Option<Vec<String>>,
1052    ) -> Self {
1053        Self {
1054            id,
1055            name,
1056            long_name,
1057            abbreviation,
1058            synonyms,
1059            lv95_coordinates: None,
1060            wgs84_coordinates: None,
1061            exchange_priority: 8, // 8 is the default priority.
1062            exchange_flag: 0,
1063            exchange_time: None,
1064            restrictions: 0,
1065            sloid: String::default(),
1066            boarding_areas: Vec::new(),
1067        }
1068    }
1069
1070    // Getters/Setters
1071
1072    pub fn name(&self) -> &str {
1073        &self.name
1074    }
1075
1076    pub fn lv95_coordinates(&self) -> Option<Coordinates> {
1077        self.lv95_coordinates
1078    }
1079
1080    pub fn set_lv95_coordinates(&mut self, value: Coordinates) {
1081        self.lv95_coordinates = Some(value);
1082    }
1083
1084    pub fn wgs84_coordinates(&self) -> Option<Coordinates> {
1085        self.wgs84_coordinates
1086    }
1087
1088    pub fn set_wgs84_coordinates(&mut self, value: Coordinates) {
1089        self.wgs84_coordinates = Some(value);
1090    }
1091
1092    pub fn set_exchange_priority(&mut self, value: i16) {
1093        self.exchange_priority = value;
1094    }
1095
1096    pub fn exchange_flag(&self) -> i16 {
1097        self.exchange_flag
1098    }
1099
1100    pub fn set_exchange_flag(&mut self, value: i16) {
1101        self.exchange_flag = value;
1102    }
1103
1104    pub fn exchange_time(&self) -> Option<(i16, i16)> {
1105        self.exchange_time
1106    }
1107
1108    pub fn set_exchange_time(&mut self, value: Option<(i16, i16)>) {
1109        self.exchange_time = value;
1110    }
1111
1112    pub fn set_restrictions(&mut self, value: i16) {
1113        self.restrictions = value;
1114    }
1115
1116    pub fn set_sloid(&mut self, value: String) {
1117        self.sloid = value;
1118    }
1119
1120    // Functions
1121
1122    pub fn add_boarding_area(&mut self, value: String) {
1123        self.boarding_areas.push(value);
1124    }
1125
1126    pub fn can_be_used_as_exchange_point(&self) -> bool {
1127        self.exchange_flag() != 0
1128    }
1129}
1130
1131// ------------------------------------------------------------------------------------------------
1132// --- StopConnection
1133// ------------------------------------------------------------------------------------------------
1134
1135#[derive(Debug, Default, Serialize, Deserialize)]
1136pub struct StopConnection {
1137    id: i32,
1138    stop_id_1: i32,
1139    stop_id_2: i32,
1140    duration: i16, // Exchange time from stop 1 to stop 2 is in minutes.
1141    attribute: i32,
1142}
1143
1144impl_Model!(StopConnection);
1145
1146impl StopConnection {
1147    pub fn new(id: i32, stop_id_1: i32, stop_id_2: i32, duration: i16) -> Self {
1148        Self {
1149            id,
1150            stop_id_1,
1151            stop_id_2,
1152            duration,
1153            attribute: 0,
1154        }
1155    }
1156
1157    // Getters/Setters
1158
1159    pub fn stop_id_1(&self) -> i32 {
1160        self.stop_id_1
1161    }
1162
1163    pub fn stop_id_2(&self) -> i32 {
1164        self.stop_id_2
1165    }
1166
1167    pub fn duration(&self) -> i16 {
1168        self.duration
1169    }
1170
1171    pub fn set_attribute(&mut self, value: i32) {
1172        self.attribute = value;
1173    }
1174}
1175
1176// ------------------------------------------------------------------------------------------------
1177// --- ThroughService
1178// ------------------------------------------------------------------------------------------------
1179
1180#[derive(Debug, Serialize, Deserialize)]
1181pub struct ThroughService {
1182    id: i32,
1183    journey_1_id: JourneyId,
1184    journey_1_stop_id: i32, // Last stop of journey 1.
1185    journey_2_id: JourneyId,
1186    journey_2_stop_id: i32, // First stop of journey 2.
1187    bit_field_id: i32,
1188}
1189
1190impl_Model!(ThroughService);
1191
1192impl ThroughService {
1193    pub fn new(
1194        id: i32,
1195        journey_1_id: JourneyId,
1196        journey_1_stop_id: i32,
1197        journey_2_id: JourneyId,
1198        journey_2_stop_id: i32,
1199        bit_field_id: i32,
1200    ) -> Self {
1201        Self {
1202            id,
1203            journey_1_id,
1204            journey_1_stop_id,
1205            journey_2_id,
1206            journey_2_stop_id,
1207            bit_field_id,
1208        }
1209    }
1210
1211    pub fn journey_1_id(&self) -> &JourneyId {
1212        &self.journey_1_id
1213    }
1214
1215    pub fn journey_1_stop_id(&self) -> i32 {
1216        self.journey_1_stop_id
1217    }
1218
1219    pub fn journey_2_id(&self) -> &JourneyId {
1220        &self.journey_2_id
1221    }
1222
1223    pub fn journey_2_stop_id(&self) -> i32 {
1224        self.journey_2_stop_id
1225    }
1226
1227    pub fn bit_field_id(&self) -> i32 {
1228        self.bit_field_id
1229    }
1230}
1231
1232// ------------------------------------------------------------------------------------------------
1233// --- TimetableMetadataEntry
1234// ------------------------------------------------------------------------------------------------
1235
1236#[derive(Debug, Serialize, Deserialize)]
1237pub struct TimetableMetadataEntry {
1238    id: i32,
1239    key: String,
1240    value: String,
1241}
1242
1243impl_Model!(TimetableMetadataEntry);
1244
1245impl TimetableMetadataEntry {
1246    pub fn new(id: i32, key: String, value: String) -> Self {
1247        Self { id, key, value }
1248    }
1249
1250    // Getters/Setters
1251
1252    pub fn key(&self) -> &str {
1253        &self.key
1254    }
1255
1256    pub fn value(&self) -> &str {
1257        &self.value
1258    }
1259
1260    /// unwrap: Do not call this function if the value is not a date.
1261    pub fn value_as_naive_date(&self) -> NaiveDate {
1262        NaiveDate::parse_from_str(self.value(), "%Y-%m-%d").unwrap()
1263    }
1264}
1265
1266// ------------------------------------------------------------------------------------------------
1267// --- TransportCompany
1268// ------------------------------------------------------------------------------------------------
1269
1270#[derive(Debug, Serialize, Deserialize)]
1271pub struct TransportCompany {
1272    id: i32,
1273    short_name: FxHashMap<Language, String>,
1274    long_name: FxHashMap<Language, String>,
1275    full_name: FxHashMap<Language, String>,
1276    administrations: Vec<String>,
1277}
1278
1279impl_Model!(TransportCompany);
1280
1281impl TransportCompany {
1282    pub fn new(id: i32) -> Self {
1283        Self {
1284            id,
1285            short_name: FxHashMap::default(),
1286            long_name: FxHashMap::default(),
1287            full_name: FxHashMap::default(),
1288            administrations: Vec::new(),
1289        }
1290    }
1291
1292    // Getters/Setters
1293
1294    pub fn set_administrations(&mut self, administrations: Vec<String>) {
1295        self.administrations = administrations;
1296    }
1297
1298    pub fn set_short_name(&mut self, language: Language, value: &str) {
1299        self.short_name.insert(language, value.to_string());
1300    }
1301
1302    pub fn set_long_name(&mut self, language: Language, value: &str) {
1303        self.long_name.insert(language, value.to_string());
1304    }
1305
1306    pub fn set_full_name(&mut self, language: Language, value: &str) {
1307        self.full_name.insert(language, value.to_string());
1308    }
1309}
1310
1311// ------------------------------------------------------------------------------------------------
1312// --- TransportType
1313// ------------------------------------------------------------------------------------------------
1314
1315#[derive(Debug, Default, Serialize, Deserialize)]
1316pub struct TransportType {
1317    id: i32,
1318    designation: String,
1319    product_class_id: i16,
1320    tariff_group: String,
1321    output_control: i16,
1322    short_name: String,
1323    surcharge: i16,
1324    flag: String,
1325    product_class_name: FxHashMap<Language, String>,
1326    category_name: FxHashMap<Language, String>,
1327}
1328
1329impl_Model!(TransportType);
1330
1331impl TransportType {
1332    #[allow(clippy::too_many_arguments)]
1333    pub fn new(
1334        id: i32,
1335        designation: String,
1336        product_class_id: i16,
1337        tariff_group: String,
1338        output_control: i16,
1339        short_name: String,
1340        surcharge: i16,
1341        flag: String,
1342    ) -> Self {
1343        Self {
1344            id,
1345            designation,
1346            product_class_id,
1347            tariff_group,
1348            output_control,
1349            short_name,
1350            surcharge,
1351            flag,
1352            product_class_name: FxHashMap::default(),
1353            category_name: FxHashMap::default(),
1354        }
1355    }
1356
1357    // Getters/Setters
1358
1359    pub fn designation(&self) -> &str {
1360        &self.designation
1361    }
1362
1363    pub fn product_class_id(&self) -> i16 {
1364        self.product_class_id
1365    }
1366
1367    pub fn set_product_class_name(&mut self, language: Language, value: &str) {
1368        self.product_class_name.insert(language, value.to_string());
1369    }
1370
1371    pub fn set_category_name(&mut self, language: Language, value: &str) {
1372        self.category_name.insert(language, value.to_string());
1373    }
1374}
1375
1376// ------------------------------------------------------------------------------------------------
1377// --- Version
1378// ------------------------------------------------------------------------------------------------
1379
1380#[derive(Clone, Copy, Debug, Display, Eq, Hash, PartialEq, Serialize, Deserialize)]
1381#[allow(non_camel_case_types)]
1382pub enum Version {
1383    V_5_40_41_2_0_4,
1384    V_5_40_41_2_0_5,
1385    V_5_40_41_2_0_6,
1386    V_5_40_41_2_0_7,
1387}