gistools/readers/gtfs/schedule/transfers.rs
1use crate::readers::csv::parse_csv_as_record;
2use alloc::{string::String, vec::Vec};
3use s2json::MValueCompatible;
4
5/// TransferType enumerates how a rider can transfer between routes/trips/stops:
6/// - 0 or empty = Recommended transfer
7/// - 1 = Timed transfer
8/// - 2 = Requires a minimum transfer time
9/// - 3 = Transfers not possible
10/// - 4 = In-seat transfer (stay onboard, same vehicle)
11/// - 5 = In-seat transfers not allowed
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)]
13pub enum GTFSTransferType {
14 /// 0 = Recommended transfer
15 Recommended = 0,
16 /// 1 = Timed transfer
17 Timed = 1,
18 /// 2 = Requires a minimum transfer time
19 MinTimeRequired = 2,
20 /// 3 = Transfers not possible
21 NotPossible = 3,
22 /// 4 = In-seat transfer (stay onboard, same vehicle)
23 InSeatTransfer = 4,
24 /// 5 = In-seat transfers not allowed
25 InSeatNotAllowed = 5,
26}
27impl From<i8> for GTFSTransferType {
28 fn from(value: i8) -> Self {
29 match value {
30 1 => GTFSTransferType::Timed,
31 2 => GTFSTransferType::MinTimeRequired,
32 3 => GTFSTransferType::NotPossible,
33 4 => GTFSTransferType::InSeatTransfer,
34 5 => GTFSTransferType::InSeatNotAllowed,
35 _ => GTFSTransferType::Recommended,
36 }
37 }
38}
39
40/// # Transfers
41///
42/// **Optional**
43/// Defines additional rules/overrides for transfers between routes/trips/stops.
44/// The level of specificity is determined by which fields are present:
45/// - from_trip_id & to_trip_id (most specific)
46/// - route vs. trip combos
47/// - only from_stop_id & to_stop_id (least specific)
48///
49/// **Primary Key**: (from_stop_id, to_stop_id, from_trip_id, to_trip_id, from_route_id, to_route_id)
50#[derive(Debug, Default, Clone, PartialEq, MValueCompatible)]
51pub struct GTFSTransfer {
52 /// **Conditionally Required**
53 /// Identifies where a connection begins (`stops.stop_id`, location_type=0 or 1).
54 /// Required if transfer_type is 1, 2, or 3. Optional if transfer_type is 4 or 5.
55 pub from_stop_id: Option<String>,
56 /// **Conditionally Required**
57 /// Identifies where a connection ends (`stops.stop_id`, location_type=0 or 1).
58 /// Required if transfer_type is 1, 2, or 3. Optional if transfer_type is 4 or 5.
59 pub to_stop_id: Option<String>,
60 /// **Optional**
61 /// Identifies a route on which the arriving trip is running.
62 /// If both `fromTripId` and `fromRouteId` are defined, the trip must belong to that route,
63 /// but `fromTripId` takes precedence.
64 pub from_route_id: Option<String>,
65 /// **Optional**
66 /// Identifies a route on which the departing trip is running.
67 /// If both `toTripId` and `toRouteId` are defined, the trip must belong to that route,
68 /// but `toTripId` takes precedence.
69 pub to_route_id: Option<String>,
70 /// **Conditionally Required**
71 /// Identifies the arriving trip (`trips.trip_id`).
72 /// Required if transfer_type is 4 or 5; optional otherwise.
73 pub from_trip_id: Option<String>,
74 /// **Conditionally Required**
75 /// Identifies the departing trip (`trips.trip_id`).
76 /// Required if transfer_type is 4 or 5; optional otherwise.
77 pub to_trip_id: Option<String>,
78 /// **Required**
79 /// Indicates the type of connection:
80 /// - 0 = Recommended
81 /// - 1 = Timed
82 /// - 2 = Requires min_transfer_time
83 /// - 3 = Not possible
84 /// - 4 = In-seat transfer
85 /// - 5 = In-seat transfer not allowed
86 pub transfer_type: i8,
87 /// **Optional**
88 /// Time in seconds required for a rider to complete the transfer. If `transfer_type=2`,
89 /// this is the minimum transfer time.
90 pub min_transfer_time: Option<u32>,
91}
92impl GTFSTransfer {
93 /// Create a new GTFSTransfer
94 pub fn new(source: &str) -> Vec<GTFSTransfer> {
95 let mut res = Vec::new();
96 for record in parse_csv_as_record::<GTFSTransfer>(source, None, None) {
97 res.push(record);
98 }
99 res
100 }
101 /// Get the transfer_type
102 pub fn transfer_type(&self) -> GTFSTransferType {
103 GTFSTransferType::from(self.transfer_type)
104 }
105}