transit_model/
add_prefix.rs

1// Copyright (C) 2017 Hove and/or its affiliates.
2//
3// This program is free software: you can redistribute it and/or modify it
4// under the terms of the GNU Affero General Public License as published by the
5// Free Software Foundation, version 3.
6
7// This program is distributed in the hope that it will be useful, but WITHOUT
8// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
10// details.
11
12// You should have received a copy of the GNU Affero General Public License
13// along with this program. If not, see <https://www.gnu.org/licenses/>
14
15//! A trait for every structure that needs to be updated with a prefix
16
17use crate::model::Collections;
18use derivative::Derivative;
19use std::collections::HashMap;
20use typed_index_collection::{Collection, CollectionWithId, Id};
21
22/// Metadata for building the prefix.
23#[derive(Derivative, Debug)]
24#[derivative(Default)]
25pub struct PrefixConfiguration {
26    /// Separator used in the prefix, usually ':'.
27    #[derivative(Default(value = "\":\".to_string()"))]
28    sep: String,
29    /// General data prefix (historically a trigram) used for discriminating
30    /// referential objects (like Network).  Usually useful to avoid collisions
31    /// when merging dataset from different contributors.
32    data_prefix: Option<String>,
33    /// Sub prefix used for discriminating scheduled
34    /// objects (like Calendar).  Usually useful to avoid collisions when
35    /// merging datasets from the same contributor.
36    schedule_subprefix: Option<String>,
37}
38
39impl PrefixConfiguration {
40    /// Set the prefix separator for PrefixConfiguration.
41    pub fn set_sep<S>(&mut self, sep: S)
42    where
43        S: ToString,
44    {
45        self.sep = sep.to_string();
46    }
47
48    /// Set the data_prefix in the PrefixConfiguration.
49    pub fn set_data_prefix<S>(&mut self, data_prefix: S)
50    where
51        S: ToString,
52    {
53        self.data_prefix = Some(data_prefix.to_string());
54    }
55
56    /// Set the schedule_subprefix in the PrefixConfiguration.
57    pub fn set_schedule_subprefix<S>(&mut self, schedule_subprefix: S)
58    where
59        S: ToString,
60    {
61        self.schedule_subprefix = Some(schedule_subprefix.to_string());
62    }
63
64    /// Add prefix for referential-type object.
65    ///
66    /// Example of objects from the referential are Line or StopPoint.
67    pub fn referential_prefix(&self, id: &str) -> String {
68        let mut prefix = String::new();
69        if let Some(data_prefix) = self.data_prefix.as_ref() {
70            prefix = prefix + data_prefix + &self.sep;
71        }
72        prefix + id
73    }
74
75    /// Add prefix for schedule-type object.
76    ///
77    /// Example of objects from the schedule are VehicleJourney or StopTime.
78    pub fn schedule_prefix(&self, id: &str) -> String {
79        let mut prefix = String::new();
80        if let Some(data_prefix) = self.data_prefix.as_ref() {
81            prefix = prefix + data_prefix + &self.sep;
82        }
83        if let Some(schedule_subprefix) = self.schedule_subprefix.as_ref() {
84            prefix = prefix + schedule_subprefix + &self.sep;
85        }
86        prefix + id
87    }
88}
89
90/// Trait for object that can be prefixed
91pub trait AddPrefix {
92    /// Add the prefix to all elements of the object that needs to be prefixed.
93    #[deprecated(since = "0.24.0", note = "please use `AddPrefix::prefix()` instead")]
94    fn add_prefix(&mut self, prefix: &str) {
95        let prefix_conf = PrefixConfiguration {
96            sep: String::new(),
97            data_prefix: Some(prefix.to_string()),
98            schedule_subprefix: None,
99        };
100        self.prefix(&prefix_conf);
101    }
102
103    /// Add the prefix to all elements of the object that needs to be prefixed.
104    /// A separator will be placed between the prefix and the identifier.
105    #[deprecated(since = "0.24.0", note = "please use `AddPrefix::prefix()` instead")]
106    fn add_prefix_with_sep(&mut self, prefix: &str, sep: &str) {
107        let prefix_conf = PrefixConfiguration {
108            sep: String::from(sep),
109            data_prefix: Some(prefix.to_string()),
110            schedule_subprefix: None,
111        };
112        self.prefix(&prefix_conf);
113    }
114
115    /// Add the prefix to all elements of the object that needs to be prefixed.
116    /// PrefixConfiguration contains all the needed metadata to create the
117    /// complete prefix.
118    fn prefix(&mut self, prefix_conf: &PrefixConfiguration);
119}
120
121impl<T> AddPrefix for Collection<T>
122where
123    T: AddPrefix,
124{
125    fn prefix(&mut self, prefix_conf: &PrefixConfiguration) {
126        for obj in &mut self.values_mut() {
127            obj.prefix(prefix_conf);
128        }
129    }
130}
131
132impl<T> AddPrefix for CollectionWithId<T>
133where
134    T: Id<T> + AddPrefix,
135{
136    fn prefix(&mut self, prefix_conf: &PrefixConfiguration) {
137        for index in self.indexes() {
138            self.index_mut(index).prefix(prefix_conf);
139        }
140    }
141}
142
143fn add_prefix_on_vehicle_journey_ids(
144    vehicle_journey_ids: &HashMap<(String, u32), String>,
145    prefix_conf: &PrefixConfiguration,
146) -> HashMap<(String, u32), String> {
147    vehicle_journey_ids
148        .iter()
149        .map(|((trip_id, sequence), value)| {
150            (
151                (prefix_conf.schedule_prefix(trip_id.as_str()), *sequence),
152                value.to_string(),
153            )
154        })
155        .collect()
156}
157
158fn add_prefix_on_vehicle_journey_ids_and_values(
159    vehicle_journey_ids: &HashMap<(String, u32), String>,
160    prefix_conf: &PrefixConfiguration,
161) -> HashMap<(String, u32), String> {
162    vehicle_journey_ids
163        .iter()
164        .map(|((trip_id, sequence), value)| {
165            (
166                (prefix_conf.schedule_prefix(trip_id.as_str()), *sequence),
167                prefix_conf.schedule_prefix(value.as_str()),
168            )
169        })
170        .collect()
171}
172
173impl AddPrefix for Collections {
174    fn prefix(&mut self, prefix_conf: &PrefixConfiguration) {
175        self.contributors.prefix(prefix_conf);
176        self.datasets.prefix(prefix_conf);
177        self.networks.prefix(prefix_conf);
178        self.lines.prefix(prefix_conf);
179        self.routes.prefix(prefix_conf);
180        self.vehicle_journeys.prefix(prefix_conf);
181        self.frequencies.prefix(prefix_conf);
182        self.stop_areas.prefix(prefix_conf);
183        self.stop_points.prefix(prefix_conf);
184        self.stop_locations.prefix(prefix_conf);
185        self.calendars.prefix(prefix_conf);
186        self.companies.prefix(prefix_conf);
187        self.comments.prefix(prefix_conf);
188        self.equipments.prefix(prefix_conf);
189        self.transfers.prefix(prefix_conf);
190        self.trip_properties.prefix(prefix_conf);
191        self.geometries.prefix(prefix_conf);
192        self.admin_stations.prefix(prefix_conf);
193        self.prices_v1.prefix(prefix_conf);
194        self.od_fares_v1.prefix(prefix_conf);
195        self.fares_v1.prefix(prefix_conf);
196        self.tickets.prefix(prefix_conf);
197        self.ticket_prices.prefix(prefix_conf);
198        self.ticket_uses.prefix(prefix_conf);
199        self.ticket_use_perimeters.prefix(prefix_conf);
200        self.ticket_use_restrictions.prefix(prefix_conf);
201        self.pathways.prefix(prefix_conf);
202        self.levels.prefix(prefix_conf);
203        self.grid_calendars.prefix(prefix_conf);
204        self.grid_exception_dates.prefix(prefix_conf);
205        self.grid_periods.prefix(prefix_conf);
206        self.grid_rel_calendar_line.prefix(prefix_conf);
207        self.occupancies.prefix(prefix_conf);
208        self.stop_time_headsigns =
209            add_prefix_on_vehicle_journey_ids(&self.stop_time_headsigns, prefix_conf);
210        self.stop_time_ids =
211            add_prefix_on_vehicle_journey_ids_and_values(&self.stop_time_ids, prefix_conf);
212        self.stop_time_comments =
213            add_prefix_on_vehicle_journey_ids_and_values(&self.stop_time_comments, prefix_conf);
214    }
215}
216
217#[cfg(test)]
218mod tests {
219    use super::*;
220    use pretty_assertions::assert_eq;
221
222    struct Obj(String);
223    impl Id<Obj> for Obj {
224        fn id(&self) -> &str {
225            self.0.as_str()
226        }
227        fn set_id(&mut self, _id: String) {
228            unimplemented!()
229        }
230    }
231    impl AddPrefix for Obj {
232        fn prefix(&mut self, prefix_conf: &PrefixConfiguration) {
233            self.0 = prefix_conf.schedule_prefix(self.0.as_str());
234        }
235    }
236
237    #[test]
238    fn collection_referential() {
239        let obj1 = Obj(String::from("some_id"));
240        let obj2 = Obj(String::from("other_id"));
241        let mut collection = Collection::new(vec![obj1, obj2]);
242        let mut prefix_conf = PrefixConfiguration::default();
243        prefix_conf.set_data_prefix("pre");
244        collection.prefix(&prefix_conf);
245        let mut values = collection.values();
246        let element = values.next().unwrap();
247        assert_eq!(String::from("pre:some_id"), element.0);
248        let element = values.next().unwrap();
249        assert_eq!(String::from("pre:other_id"), element.0);
250    }
251
252    #[test]
253    fn collection_referential_and_schedule() {
254        let obj1 = Obj(String::from("some_id"));
255        let obj2 = Obj(String::from("other_id"));
256        let mut collection = Collection::new(vec![obj1, obj2]);
257        let mut prefix_conf = PrefixConfiguration::default();
258        prefix_conf.set_data_prefix("pre");
259        prefix_conf.set_schedule_subprefix("winter");
260        collection.prefix(&prefix_conf);
261        let mut values = collection.values();
262        let element = values.next().unwrap();
263        assert_eq!(String::from("pre:winter:some_id"), element.0);
264        let element = values.next().unwrap();
265        assert_eq!(String::from("pre:winter:other_id"), element.0);
266    }
267
268    #[test]
269    fn collection_schedule() {
270        let obj1 = Obj(String::from("some_id"));
271        let obj2 = Obj(String::from("other_id"));
272        let mut collection = Collection::new(vec![obj1, obj2]);
273        let mut prefix_conf = PrefixConfiguration::default();
274        prefix_conf.set_schedule_subprefix("winter");
275        collection.prefix(&prefix_conf);
276        let mut values = collection.values();
277        let element = values.next().unwrap();
278        assert_eq!(String::from("winter:some_id"), element.0);
279        let element = values.next().unwrap();
280        assert_eq!(String::from("winter:other_id"), element.0);
281    }
282
283    #[test]
284    fn collection_no_prefix() {
285        let obj1 = Obj(String::from("some_id"));
286        let obj2 = Obj(String::from("other_id"));
287        let mut collection = Collection::new(vec![obj1, obj2]);
288        let prefix_conf = PrefixConfiguration::default();
289        collection.prefix(&prefix_conf);
290        let mut values = collection.values();
291        let element = values.next().unwrap();
292        assert_eq!(String::from("some_id"), element.0);
293        let element = values.next().unwrap();
294        assert_eq!(String::from("other_id"), element.0);
295    }
296
297    #[test]
298    #[allow(deprecated)]
299    fn collection_deprecated() {
300        let obj1 = Obj(String::from("some_id"));
301        let obj2 = Obj(String::from("other_id"));
302        let mut collection = Collection::new(vec![obj1, obj2]);
303        collection.add_prefix("pre:");
304        let mut values = collection.values();
305        let element = values.next().unwrap();
306        assert_eq!(String::from("pre:some_id"), element.0);
307        let element = values.next().unwrap();
308        assert_eq!(String::from("pre:other_id"), element.0);
309    }
310
311    #[test]
312    fn collection_with_id_referential() {
313        let obj1 = Obj(String::from("some_id"));
314        let obj2 = Obj(String::from("other_id"));
315        let mut collection = CollectionWithId::new(vec![obj1, obj2]).unwrap();
316        let mut prefix_conf = PrefixConfiguration::default();
317        prefix_conf.set_data_prefix("pre");
318        collection.prefix(&prefix_conf);
319        let mut values = collection.values();
320        let element = values.next().unwrap();
321        assert_eq!(String::from("pre:some_id"), element.0);
322        let element = values.next().unwrap();
323        assert_eq!(String::from("pre:other_id"), element.0);
324    }
325
326    #[test]
327    fn collection_with_id_referential_and_schedule() {
328        let obj1 = Obj(String::from("some_id"));
329        let obj2 = Obj(String::from("other_id"));
330        let mut collection = CollectionWithId::new(vec![obj1, obj2]).unwrap();
331        let mut prefix_conf = PrefixConfiguration::default();
332        prefix_conf.set_data_prefix("pre");
333        prefix_conf.set_schedule_subprefix("summer");
334        collection.prefix(&prefix_conf);
335        let mut values = collection.values();
336        let element = values.next().unwrap();
337        assert_eq!(String::from("pre:summer:some_id"), element.0);
338        let element = values.next().unwrap();
339        assert_eq!(String::from("pre:summer:other_id"), element.0);
340    }
341
342    #[test]
343    fn collection_with_id_schedule() {
344        let obj1 = Obj(String::from("some_id"));
345        let obj2 = Obj(String::from("other_id"));
346        let mut collection = CollectionWithId::new(vec![obj1, obj2]).unwrap();
347        let mut prefix_conf = PrefixConfiguration::default();
348        prefix_conf.set_schedule_subprefix("summer");
349        collection.prefix(&prefix_conf);
350        let mut values = collection.values();
351        let element = values.next().unwrap();
352        assert_eq!(String::from("summer:some_id"), element.0);
353        let element = values.next().unwrap();
354        assert_eq!(String::from("summer:other_id"), element.0);
355    }
356
357    #[test]
358    fn collection_with_id_no_prefix() {
359        let obj1 = Obj(String::from("some_id"));
360        let obj2 = Obj(String::from("other_id"));
361        let mut collection = CollectionWithId::new(vec![obj1, obj2]).unwrap();
362        let prefix_conf = PrefixConfiguration::default();
363        collection.prefix(&prefix_conf);
364        let mut values = collection.values();
365        let element = values.next().unwrap();
366        assert_eq!(String::from("some_id"), element.0);
367        let element = values.next().unwrap();
368        assert_eq!(String::from("other_id"), element.0);
369    }
370
371    #[test]
372    #[allow(deprecated)]
373    fn collection_with_id_deprecated() {
374        let obj1 = Obj(String::from("some_id"));
375        let obj2 = Obj(String::from("other_id"));
376        let mut collection = CollectionWithId::new(vec![obj1, obj2]).unwrap();
377        collection.add_prefix("pre:");
378        let mut values = collection.values();
379        let element = values.next().unwrap();
380        assert_eq!(String::from("pre:some_id"), element.0);
381        let element = values.next().unwrap();
382        assert_eq!(String::from("pre:other_id"), element.0);
383    }
384}