gistools/readers/gtfs/schedule/fare_transfer_rules.rs
1use crate::readers::csv::parse_csv_as_record;
2use alloc::{string::String, vec::Vec};
3use s2json::MValueCompatible;
4
5/// Duration limit type for how transfer durations are measured.
6/// Required if `duration_limit` is defined, forbidden otherwise.
7///
8/// 0 - Between departure of current leg & arrival of next leg
9/// 1 - Between departure of current leg & departure of next leg
10/// 2 - Between arrival of current leg & departure of next leg
11/// 3 - Between arrival of current leg & arrival of next leg
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)]
13pub enum GTFSDurationLimitType {
14 /// Between departure of current leg & arrival of next leg
15 DepCurrentArrNext = 0,
16 /// Between departure of current leg & departure of next leg
17 DepCurrentDepNext = 1,
18 /// Between arrival of current leg & departure of next leg
19 ArrCurrentDepNext = 2,
20 /// Between arrival of current leg & arrival of next leg
21 ArrCurrentArrNext = 3,
22}
23impl From<i8> for GTFSDurationLimitType {
24 fn from(s: i8) -> Self {
25 match s {
26 1 => GTFSDurationLimitType::DepCurrentDepNext,
27 2 => GTFSDurationLimitType::ArrCurrentDepNext,
28 3 => GTFSDurationLimitType::ArrCurrentArrNext,
29 _ => GTFSDurationLimitType::DepCurrentArrNext,
30 }
31 }
32}
33
34/// Fare transfer type describing how costs are processed between consecutive legs:
35///
36/// 0 = (A) + (AB)
37/// 1 = (A) + (AB) + (B)
38/// 2 = (AB)
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)]
40pub enum GTFSFareTransferType {
41 /// A + AB
42 FromLegPlusTransfer = 0, // A + AB
43 /// A + AB + B
44 FromLegTransferToLeg = 1, // A + AB + B
45 /// AB
46 TransferOnly = 2, // AB
47}
48impl From<i8> for GTFSFareTransferType {
49 fn from(s: i8) -> Self {
50 match s {
51 1 => GTFSFareTransferType::FromLegTransferToLeg,
52 2 => GTFSFareTransferType::TransferOnly,
53 _ => GTFSFareTransferType::FromLegPlusTransfer,
54 }
55 }
56}
57
58/// # Fare Transfer Rules
59///
60/// **Optional**
61/// Defines the cost of transferring between fare legs specified in `fare_leg_rules.txt`.
62/// Matching uses:
63/// - from_leg_group_id
64/// - to_leg_group_id
65/// - transfer_count
66/// - duration_limit
67/// - duration_limit_type
68/// - fare_transfer_type
69/// - fare_product_id
70///
71/// **Primary Key**: (from_leg_group_id, to_leg_group_id, fare_product_id, transfer_count, duration_limit)
72#[derive(Debug, Default, Clone, PartialEq, MValueCompatible)]
73pub struct GTFSFareTransferRule {
74 /// **Optional**
75 /// The pre-transfer fare leg group (`fare_leg_rules.leg_group_id`).
76 /// - If no exact match is found, empty corresponds to all leg groups not listed under `from_leg_group_id`.
77 pub from_leg_group_id: Option<String>,
78 /// **Optional**
79 /// The post-transfer fare leg group (`fare_leg_rules.leg_group_id`).
80 /// - If no exact match is found, empty corresponds to all leg groups not listed under `to_leg_group_id`.
81 pub to_leg_group_id: Option<String>,
82 /// **Conditionally Forbidden / Required**
83 /// Defines how many consecutive transfers this rule may be applied to.
84 /// - `-1` means no limit.
85 /// - `1` or more = the transfer count this rule applies to.
86 ///
87 /// Forbidden if `from_leg_group_id !== to_leg_group_id`.
88 /// Required if `from_leg_group_id === to_leg_group_id`.
89 pub transfer_count: Option<i32>,
90 /// **Optional**
91 /// Duration limit (in seconds) for the transfer. Empty means no limit.
92 pub duration_limit: Option<i32>,
93 /// **Conditionally Required**
94 /// Defines how to measure the `durationLimit`.
95 /// - Required if `durationLimit` is defined.
96 /// - Forbidden if `durationLimit` is empty.
97 pub duration_limit_type: Option<i8>, // ?: GTFSDurationLimitType;
98 /// **Required**
99 /// Indicates how to combine transfer costs:
100 /// - 0 = from-leg cost + transfer cost
101 /// - 1 = from-leg + transfer + to-leg cost
102 /// - 2 = transfer cost only
103 pub fare_transfer_type: i8, // GTFSFareTransferType;
104 /// **Optional**
105 /// Fare product ID for the transfer. If empty, cost is 0 (no transfer cost).
106 pub fare_product_id: Option<String>,
107}
108impl GTFSFareTransferRule {
109 /// Create a new GTFSFareTransferRule
110 pub fn new(source: &str) -> Vec<GTFSFareTransferRule> {
111 let mut res = Vec::new();
112 for record in parse_csv_as_record::<GTFSFareTransferRule>(source, None, None) {
113 res.push(record);
114 }
115 res
116 }
117 /// Get the duration_limit_type
118 pub fn duration_limit_type(&self) -> Option<GTFSDurationLimitType> {
119 self.duration_limit_type.map(GTFSDurationLimitType::from)
120 }
121 /// Get the fare_transfer_type
122 pub fn fare_transfer_type(&self) -> GTFSFareTransferType {
123 self.fare_transfer_type.into()
124 }
125}