From 59c13dc46abe409f819424ae81d106e73bf3eb52 Mon Sep 17 00:00:00 2001 From: Nahuel Lofeudo Date: Sun, 17 May 2026 22:34:51 +0100 Subject: [PATCH] Get rid of a bunch of unwrap()s --- src/gtfs/loader.rs | 59 ++++++++++++++++++++++++++-------------------- src/gtfs/mod.rs | 22 ++++++++--------- src/main.rs | 22 ++++++++++------- 3 files changed, 58 insertions(+), 45 deletions(-) diff --git a/src/gtfs/loader.rs b/src/gtfs/loader.rs index 20a630e..b3298d5 100644 --- a/src/gtfs/loader.rs +++ b/src/gtfs/loader.rs @@ -1,6 +1,6 @@ use log::debug; use gtfs_structures::{Calendar, CalendarDate, RawStopTime, RawTrip, Route, Stop}; -use serde::de::DeserializeOwned; +use serde::de::{DeserializeOwned, value::MapAccessDeserializer}; use std::{ collections::{HashMap, HashSet}, fs::File, @@ -14,14 +14,14 @@ use crate::gtfs::{ }; trait Filter { - fn accept(&self, v: &T) -> bool; + fn accept(&self, v: &T) -> Option; } // No filter on loaded records struct LoadAll {} impl Filter for LoadAll { - fn accept(&self, _: &T) -> bool { - return true; + fn accept(&self, _: &T) -> Option { + return Some(true); } } @@ -29,9 +29,9 @@ struct LoadRoutes<'a> { routes: &'a HashSet, } impl Filter for LoadRoutes<'_> { - fn accept(&self, r: &Route) -> bool { + fn accept(&self, r: &Route) -> Option { let short_name = &r.short_name; - return short_name.is_some() && self.routes.contains(short_name.as_ref().unwrap()); + return Some(short_name.is_some() && self.routes.contains(short_name.as_ref()?)); } } @@ -39,9 +39,9 @@ struct LoadStops<'a> { stops: &'a HashSet, } impl Filter for LoadStops<'_> { - fn accept(&self, s: &Stop) -> bool { + fn accept(&self, s: &Stop) -> Option { let stop_code = &s.code; - return stop_code.is_some() && self.stops.contains(s.code.as_ref().unwrap()); + return Some(stop_code.is_some() && self.stops.contains(s.code.as_ref()?)); } } @@ -49,9 +49,9 @@ struct LoadTrips<'a> { route_ids: &'a HashSet, } impl Filter for LoadTrips<'_> { - fn accept(&self, t: &RawTrip) -> bool { + fn accept(&self, t: &RawTrip) -> Option { let route_id = &t.route_id; - return self.route_ids.contains(route_id); + return Some(self.route_ids.contains(route_id)); } } @@ -60,8 +60,8 @@ struct LoadStopTimes<'a> { stop_ids: &'a HashSet, } impl Filter for LoadStopTimes<'_> { - fn accept(&self, st: &RawStopTime) -> bool { - return self.stop_ids.contains(&st.stop_id) && self.trip_ids.contains(&st.trip_id); + fn accept(&self, st: &RawStopTime) -> Option { + return Some(self.stop_ids.contains(&st.stop_id) && self.trip_ids.contains(&st.trip_id)); } } @@ -70,14 +70,15 @@ fn load_vector( destination: &mut Vec, zip_reader: &mut ZipArchive, table_name: &str, -) { - let file_reader = zip_reader.by_name(table_name).unwrap(); - let mut rdr = csv::Reader::from_reader(file_reader); +) -> Option { + let file_reader = zip_reader.by_name(table_name); + let mut rdr = csv::Reader::from_reader(file_reader.ok()?); for row in rdr.deserialize() { - let record: T = row.unwrap(); + let record: T = row.ok()?; destination.push(record); } + return Some(true); } // Loads a HashMap of the selected type, using the provided index function as the key @@ -87,27 +88,30 @@ fn load_map( table_name: &str, index: IndexFn, filter: FilterT, -) where +) -> Option + where K: Eq + Hash, V: DeserializeOwned, IndexFn: Fn(&V) -> K, - FilterT: Filter, + FilterT: Filter { - let file_reader = zip_reader.by_name(table_name).unwrap(); + let file_reader = (zip_reader.by_name(table_name)).ok()?; let mut rdr = csv::Reader::from_reader(file_reader); for row in rdr.deserialize() { if row.is_ok() { - let record: V = row.unwrap(); - if filter.accept(&record) { + let record: V = row.ok()?; + let accepted = filter.accept(&record); + if accepted.is_some() && accepted? { let idx: K = index(&record); destination.insert(idx, record); } } else { print!("Row failed to deserialize row {:#?}", row.err()); - panic!(); + return None; } } + return Some(true); } // Loads a HashMap of a vector of the selected type, using the provided index function as the key @@ -118,22 +122,25 @@ fn load_vector_map<'a, K, V, IndexFn, FilterT>( table_name: &str, index: IndexFn, filter: FilterT, -) where +) -> Option + where K: Eq + Hash, V: DeserializeOwned, IndexFn: Fn(&V) -> K, FilterT: Filter, { - let file_reader = zip_reader.by_name(table_name).unwrap(); + let file_reader = zip_reader.by_name(table_name).ok()?; let mut rdr = csv::Reader::from_reader(file_reader); for row in rdr.deserialize() { - let record: V = row.unwrap(); - if filter.accept(&record) { + let record: V = row.ok()?; + let accepted = filter.accept(&record); + if accepted.is_some() && accepted? { let idx = index(&record); destination.entry(idx).or_insert_with(Vec::new).push(record); } } + return Some(true) } pub fn load_gtfs( diff --git a/src/gtfs/mod.rs b/src/gtfs/mod.rs index 587b393..29412c0 100644 --- a/src/gtfs/mod.rs +++ b/src/gtfs/mod.rs @@ -5,8 +5,7 @@ pub mod structs; use chrono::{DateTime, Local, NaiveTime, Timelike}; use log::{debug}; use std::{ - collections::{HashMap, HashSet}, - fs::File, + collections::{HashMap, HashSet}, fs::File, io::Error }; use gtfs_structures::{Exception, RawTrip}; @@ -15,7 +14,7 @@ use crate::gtfs::{loader::load_gtfs, structs::{Arrival, Gtfs, Preferences}}; impl Gtfs { - pub fn get_next_arrivals_for(&self, target_datetime: &DateTime) -> Box>> { + pub fn get_next_arrivals_for(&self, target_datetime: &DateTime) -> Option>> { let naive_target = target_datetime.naive_local(); let target_date = naive_target.date(); @@ -66,13 +65,13 @@ impl Gtfs { for (_id, stop_time) in self.stop_times.iter() { if trips.contains_key(&stop_time.trip_id) { - let stop_timestamp = stop_time.departure_time.or(stop_time.arrival_time).unwrap(); + let stop_timestamp = stop_time.departure_time.or(stop_time.arrival_time)?; debug!("Stop timestamp {} current timestamp {}", stop_timestamp, current_timestamp); if current_timestamp < stop_timestamp.into() { let arrival: Arrival = Arrival { - route: self.routes.get(&self.trips.get(&stop_time.trip_id).unwrap().route_id).unwrap(), - stop: self.stops.get(&stop_time.stop_id).unwrap(), - departure_time: NaiveTime::from_num_seconds_from_midnight_opt(stop_timestamp, 0).unwrap() + route: self.routes.get(&self.trips.get(&stop_time.trip_id)?.route_id)?, + stop: self.stops.get(&stop_time.stop_id)?, + departure_time: NaiveTime::from_num_seconds_from_midnight_opt(stop_timestamp, 0)? }; arrivals.push(arrival); } @@ -80,14 +79,15 @@ impl Gtfs { } debug!("Found {} arrivals", arrivals.len()); - return Box::from(arrivals); + return Some(arrivals); } /// Load a GTFS structure from a zip file - pub fn load(src_file: &str, prefs: &Preferences) -> Gtfs { + pub fn load(src_file: &str, prefs: &Preferences) -> Result { // Open zip file - let mut zip_reader = zip::ZipArchive::new(File::open(src_file).unwrap()).unwrap(); + let zip_file = File::open(src_file)?; + let mut zip_reader = zip::ZipArchive::new(zip_file)?; let mut gtfs: Gtfs = Gtfs { agencies: Vec::new(), @@ -102,6 +102,6 @@ impl Gtfs { load_gtfs(&mut gtfs, &mut zip_reader, &prefs.route_numbers, &prefs.stop_codes); - return gtfs; + return Ok(gtfs); } } diff --git a/src/main.rs b/src/main.rs index 577cf70..05bbec1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ mod gtfs; mod renderer; -use std::{collections::HashSet, ops::Add, process, thread::Builder, time::SystemTime}; +use std::{collections::HashSet, ops::Add, os::unix::process::ExitStatusExt, process, thread::Builder, time::SystemTime}; use chrono::{DateTime, Duration, Local, NaiveTime}; use log::{Metadata, Record, debug, error, info}; use sdl3::event::Event; @@ -15,21 +15,21 @@ struct RefreshDataEvent { } -fn refresh_schedule(gtfs: &Gtfs) -> Box>> { +fn refresh_schedule(gtfs: &Gtfs) -> Option>> { let current_timestamp = SystemTime::now(); let datetime: DateTime = current_timestamp.clone().into(); - let mut next_arrivals: Box>> = gtfs.get_next_arrivals_for(&datetime); + let mut next_arrivals: Vec> = gtfs.get_next_arrivals_for(&datetime)?; if next_arrivals.len() < NUM_ARRIVALS { // If we don't have enough entries today, look for arrivals tomorrow. let mut tomorrow: DateTime = datetime.clone(); tomorrow = tomorrow.with_time(NaiveTime::from_hms_opt(0, 0, 0).unwrap()).unwrap(); tomorrow = tomorrow.add(Duration::days(1)); - next_arrivals.append(&mut gtfs.get_next_arrivals_for(&tomorrow)); + next_arrivals.append(&mut gtfs.get_next_arrivals_for(&tomorrow)?); } next_arrivals.sort(); - return next_arrivals; + return Some(next_arrivals); } @@ -51,7 +51,7 @@ fn main() { fn flush(&self) {} } - log::set_logger(&MY_LOGGER).unwrap(); + log::set_logger(&MY_LOGGER); log::set_max_level(log::LevelFilter::Trace); // Create preferences structures from config @@ -68,7 +68,13 @@ fn main() { // Init GTFS static info info!("Loading GTFS data..."); - let gtfs = Gtfs::load(SRC_FILE, >fs_prefs); + let res = Gtfs::load(SRC_FILE, >fs_prefs); + + if let Err(e) = res { + error!("Error loading GTFS data: {}", e); + process::exit(-1); + } + let gtfs = res.unwrap(); // Init screen info!("Initializing screen..."); @@ -112,7 +118,7 @@ fn main() { let refresh_data = event.as_user_event_type::(); if refresh_data.is_some() { debug!("Received user event: {:#?}", refresh_data.unwrap()); - let _data: Box>> = refresh_schedule(>fs); + let _data: Option>> = refresh_schedule(>fs); debug!("-------------------------------- Refresh done."); } }