Skip to content

Commit 2fbd059

Browse files
committed
Use Index<Stop> instead of Arc<Stop>
POC to see if the ergonomics are ok or not
1 parent 90db386 commit 2fbd059

File tree

7 files changed

+131
-49
lines changed

7 files changed

+131
-49
lines changed

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ sha2 = "0.10"
2323
zip = "0.5"
2424
thiserror = "1"
2525
rgb = "0.8"
26+
#typed-generational-arena = "0.2"
27+
typed-generational-arena = { git = "https://gitlab.com/antoine-de/typed-generational-arena", branch = "changes" }
2628

2729
futures = { version = "0.3", optional = true }
2830
reqwest = { version = "0.11", optional = true, features = ["blocking"]}

examples/gtfs_reading.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,22 @@ fn main() {
1212

1313
let route_1 = gtfs.routes.get("1").expect("no route 1");
1414
println!("{}: {:?}", route_1.short_name, route_1);
15+
16+
let trip = gtfs
17+
.trips
18+
.get("trip1")
19+
.expect("impossible to find trip trip1");
20+
21+
let stop_time = trip
22+
.stop_times
23+
.iter()
24+
.next()
25+
.expect("no stop times in trips");
26+
27+
let stop = gtfs
28+
.stops
29+
.get(stop_time.stop)
30+
.expect("no stop in stop time");
31+
32+
println!("first stop of trip 'trip1': {}", &stop.name);
1533
}

src/collection.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
use std::{collections::hash_map::Entry, collections::HashMap, iter::FromIterator};
2+
use typed_generational_arena::{Arena, Index, Iter};
3+
4+
use crate::{Error, Id};
5+
6+
pub struct CollectionWithID<T> {
7+
storage: Arena<T>,
8+
ids: HashMap<String, Index<T>>,
9+
}
10+
11+
impl<T> Default for CollectionWithID<T> {
12+
fn default() -> Self {
13+
CollectionWithID {
14+
storage: Arena::default(),
15+
ids: HashMap::default(),
16+
}
17+
}
18+
}
19+
20+
impl<T: Id> CollectionWithID<T> {
21+
pub fn insert(&mut self, o: T) -> Result<Index<T>, Error> {
22+
let id = o.id().to_owned();
23+
match self.ids.entry(id) {
24+
Entry::Occupied(_) => Err(Error::DuplicateStop(o.id().to_owned())),
25+
Entry::Vacant(e) => {
26+
let index = self.storage.insert(o);
27+
e.insert(index);
28+
Ok(index)
29+
}
30+
}
31+
}
32+
}
33+
34+
impl<T> CollectionWithID<T> {
35+
pub fn get(&self, i: Index<T>) -> Option<&T> {
36+
self.storage.get(i)
37+
}
38+
39+
pub fn get_by_id(&self, id: &str) -> Option<&T> {
40+
self.ids.get(id).and_then(|idx| self.storage.get(*idx))
41+
}
42+
43+
pub fn get_mut_by_id(&mut self, id: &str) -> Option<&mut T> {
44+
let idx = self.ids.get(id)?;
45+
self.storage.get_mut(*idx)
46+
}
47+
48+
pub fn get_index(&self, id: &str) -> Option<&Index<T>> {
49+
self.ids.get(id)
50+
}
51+
52+
pub fn len(&self) -> usize {
53+
self.storage.len()
54+
}
55+
56+
/// Iterates over the `(Index<T>, &T)` of the `CollectionWithID`.
57+
pub fn iter(&self) -> Iter<'_, T> {
58+
self.storage.iter()
59+
}
60+
}
61+
62+
impl<T: Id> FromIterator<T> for CollectionWithID<T> {
63+
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
64+
let mut c = Self::default();
65+
66+
for i in iter {
67+
// Note FromIterator does not handle the insertion error
68+
let _ = c
69+
.insert(i)
70+
.map_err(|e| println!("impossible to insert elt: {}", e));
71+
}
72+
73+
c
74+
}
75+
}

src/error.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ pub enum Error {
2828
/// The color is not given in the RRGGBB format, without a leading `#`
2929
#[error("'{0}' is not a valid color; RRGGBB format is expected, without a leading `#`")]
3030
InvalidColor(String),
31+
/// Several stops have the same id
32+
#[error("duplicate stop: '{0}'")]
33+
DuplicateStop(String),
3134
/// Generic Input/Output error while reading a file
3235
#[error("impossible to read file")]
3336
IO(#[from] std::io::Error),

src/gtfs.rs

Lines changed: 27 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
use crate::{objects::*, Error, RawGtfs};
1+
use crate::{collection::CollectionWithID, objects::*, Error, RawGtfs};
22
use chrono::prelude::NaiveDate;
33
use chrono::Duration;
44
use std::collections::{HashMap, HashSet};
55
use std::convert::TryFrom;
6-
use std::sync::Arc;
76

87
/// Data structure with all the GTFS objects
98
///
@@ -27,8 +26,8 @@ pub struct Gtfs {
2726
pub calendar: HashMap<String, Calendar>,
2827
/// All calendar dates grouped by service_id
2928
pub calendar_dates: HashMap<String, Vec<CalendarDate>>,
30-
/// All stop by `stop_id`. Stops are in an [Arc] because they are also referenced by each [StopTime]
31-
pub stops: HashMap<String, Arc<Stop>>,
29+
/// All stop by `stop_id`
30+
pub stops: CollectionWithID<Stop>,
3231
/// All routes by `route_id`
3332
pub routes: HashMap<String, Route>,
3433
/// All trips by `trip_id`
@@ -49,7 +48,7 @@ impl TryFrom<RawGtfs> for Gtfs {
4948
///
5049
/// It might fail if some mandatory files couldn’t be read or if there are references to other objects that are invalid.
5150
fn try_from(raw: RawGtfs) -> Result<Gtfs, Error> {
52-
let stops = to_stop_map(
51+
let stops = to_stop_collection(
5352
raw.stops?,
5453
raw.transfers.unwrap_or_else(|| Ok(Vec::new()))?,
5554
raw.pathways.unwrap_or(Ok(Vec::new()))?,
@@ -176,10 +175,9 @@ impl Gtfs {
176175

177176
/// Gets a [Stop] by its `stop_id`
178177
pub fn get_stop<'a>(&'a self, id: &str) -> Result<&'a Stop, Error> {
179-
match self.stops.get(id) {
180-
Some(stop) => Ok(stop),
181-
None => Err(Error::ReferenceError(id.to_owned())),
182-
}
178+
self.stops
179+
.get_by_id(id)
180+
.ok_or_else(|| Error::ReferenceError(id.to_owned()))
183181
}
184182

185183
/// Gets a [Trip] by its `trip_id`
@@ -232,37 +230,34 @@ fn to_map<O: Id>(elements: impl IntoIterator<Item = O>) -> HashMap<String, O> {
232230
.collect()
233231
}
234232

235-
fn to_stop_map(
233+
fn to_stop_collection(
236234
stops: Vec<Stop>,
237235
raw_transfers: Vec<RawTransfer>,
238236
raw_pathways: Vec<RawPathway>,
239-
) -> Result<HashMap<String, Arc<Stop>>, Error> {
240-
let mut stop_map: HashMap<String, Stop> =
241-
stops.into_iter().map(|s| (s.id.clone(), s)).collect();
237+
) -> Result<CollectionWithID<Stop>, Error> {
238+
let mut stops: CollectionWithID<Stop> = stops.into_iter().collect();
242239

243240
for transfer in raw_transfers {
244-
stop_map
245-
.get(&transfer.to_stop_id)
241+
stops
242+
.get_by_id(&transfer.to_stop_id)
246243
.ok_or_else(|| Error::ReferenceError(transfer.to_stop_id.to_string()))?;
247-
stop_map
248-
.entry(transfer.from_stop_id.clone())
249-
.and_modify(|stop| stop.transfers.push(StopTransfer::from(transfer)));
244+
let s = stops
245+
.get_mut_by_id(&transfer.from_stop_id)
246+
.ok_or_else(|| Error::ReferenceError(transfer.from_stop_id.to_string()))?;
247+
s.transfers.push(StopTransfer::from(transfer));
250248
}
251249

252250
for pathway in raw_pathways {
253-
stop_map
254-
.get(&pathway.to_stop_id)
251+
stops
252+
.get_by_id(&pathway.to_stop_id)
255253
.ok_or_else(|| Error::ReferenceError(pathway.to_stop_id.to_string()))?;
256-
stop_map
257-
.entry(pathway.from_stop_id.clone())
258-
.and_modify(|stop| stop.pathways.push(Pathway::from(pathway)));
254+
let s = stops
255+
.get_mut_by_id(&pathway.from_stop_id)
256+
.ok_or_else(|| Error::ReferenceError(pathway.to_stop_id.to_string()))?;
257+
s.pathways.push(Pathway::from(pathway));
259258
}
260259

261-
let res = stop_map
262-
.into_iter()
263-
.map(|(i, s)| (i, Arc::new(s)))
264-
.collect();
265-
Ok(res)
260+
Ok(stops)
266261
}
267262

268263
fn to_shape_map(shapes: Vec<Shape>) -> HashMap<String, Vec<Shape>> {
@@ -292,7 +287,7 @@ fn create_trips(
292287
raw_trips: Vec<RawTrip>,
293288
raw_stop_times: Vec<RawStopTime>,
294289
raw_frequencies: Vec<RawFrequency>,
295-
stops: &HashMap<String, Arc<Stop>>,
290+
stops: &CollectionWithID<Stop>,
296291
) -> Result<HashMap<String, Trip>, Error> {
297292
let mut trips = to_map(raw_trips.into_iter().map(|rt| Trip {
298293
id: rt.id,
@@ -313,9 +308,9 @@ fn create_trips(
313308
.get_mut(&s.trip_id)
314309
.ok_or_else(|| Error::ReferenceError(s.trip_id.to_string()))?;
315310
let stop = stops
316-
.get(&s.stop_id)
317-
.ok_or_else(|| Error::ReferenceError(s.stop_id.to_string()))?;
318-
trip.stop_times.push(StopTime::from(&s, Arc::clone(stop)));
311+
.get_index(&s.stop_id)
312+
.ok_or(Error::ReferenceError(s.stop_id.to_string()))?;
313+
trip.stop_times.push(StopTime::from(&s, stop.clone()));
319314
}
320315

321316
for trip in &mut trips.values_mut() {

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ extern crate derivative;
4343
#[macro_use]
4444
extern crate serde_derive;
4545

46+
mod collection;
4647
mod enums;
4748
pub mod error;
4849
mod gtfs;

src/objects.rs

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use chrono::{Datelike, NaiveDate, Weekday};
44
use rgb::RGB8;
55

66
use std::fmt;
7-
use std::sync::Arc;
7+
use typed_generational_arena::Index;
88

99
/// Objects that have an identifier implement this trait
1010
///
@@ -14,24 +14,12 @@ pub trait Id {
1414
fn id(&self) -> &str;
1515
}
1616

17-
impl<T: Id> Id for Arc<T> {
18-
fn id(&self) -> &str {
19-
self.as_ref().id()
20-
}
21-
}
22-
2317
/// Trait to introspect what is the object’s type (stop, route…)
2418
pub trait Type {
2519
/// What is the type of the object
2620
fn object_type(&self) -> ObjectType;
2721
}
2822

29-
impl<T: Type> Type for Arc<T> {
30-
fn object_type(&self) -> ObjectType {
31-
self.as_ref().object_type()
32-
}
33-
}
34-
3523
/// A calender describes on which days the vehicle runs. See <https://gtfs.org/reference/static/#calendartxt>
3624
#[derive(Debug, Deserialize, Serialize)]
3725
pub struct Calendar {
@@ -258,14 +246,14 @@ pub struct RawStopTime {
258246
}
259247

260248
/// The moment where a vehicle, running on [Trip] stops at a [Stop]. See <https://gtfs.org/reference/static/#stopstxt>
261-
#[derive(Clone, Debug, Default)]
249+
#[derive(Clone, Debug)]
262250
pub struct StopTime {
263251
/// Arrival time of the stop time.
264252
/// It's an option since the intermediate stops can have have no arrival
265253
/// and this arrival needs to be interpolated
266254
pub arrival_time: Option<u32>,
267-
/// [Stop] where the vehicle stops
268-
pub stop: Arc<Stop>,
255+
/// [Index] of the [Stop] where the vehicle stops
256+
pub stop: Index<Stop>,
269257
/// Departure time of the stop time.
270258
/// It's an option since the intermediate stops can have have no departure
271259
/// and this departure needs to be interpolated
@@ -290,7 +278,7 @@ pub struct StopTime {
290278

291279
impl StopTime {
292280
/// Creates [StopTime] by linking a [RawStopTime::stop_id] to the actual [Stop]
293-
pub fn from(stop_time_gtfs: &RawStopTime, stop: Arc<Stop>) -> Self {
281+
pub fn from(stop_time_gtfs: &RawStopTime, stop: Index<Stop>) -> Self {
294282
Self {
295283
arrival_time: stop_time_gtfs.arrival_time,
296284
departure_time: stop_time_gtfs.departure_time,

0 commit comments

Comments
 (0)