/// Parse the optional `travel` query param, returning an empty Vec when absent or empty. pub fn parse_optional_travel(travel: Option<&str>) -> Result, String> { match travel.filter(|val| !val.is_empty()) { Some(s) => parse_travel_entries(s), None => Ok(Vec::new()), } } /// A parsed travel time entry from the `travel` query parameter. pub struct TravelEntry { pub mode: String, pub slug: String, pub use_best: bool, pub filter_min: Option, pub filter_max: Option, } /// Parse `travel` param into a list of travel entries. /// Format: `mode:slug` or `mode:slug:best` or `mode:slug:min:max` or `mode:slug:best:min:max` fn parse_travel_entries(travel_str: &str) -> Result, String> { let mut entries = Vec::new(); let mut seen_keys = Vec::new(); for segment in travel_str.split('|') { let parts: Vec<&str> = segment.split(':').collect(); if parts.len() < 2 { return Err(format!( "each travel entry must be 'mode:slug' or 'mode:slug:min:max', got '{}'", segment )); } let mode = parts[0].trim().to_string(); let slug = parts[1].trim().to_string(); let use_best = parts.len() >= 3 && parts[2].trim() == "best"; let filter_offset = if use_best { 1 } else { 0 }; let (filter_min, filter_max) = if parts.len() >= 4 + filter_offset { let min: f32 = parts[2 + filter_offset] .trim() .parse() .map_err(|_| format!("invalid travel filter min in '{}'", segment))?; let max: f32 = parts[3 + filter_offset] .trim() .parse() .map_err(|_| format!("invalid travel filter max in '{}'", segment))?; (Some(min), Some(max)) } else { (None, None) }; let key = format!("{}:{}", mode, slug); if seen_keys.contains(&key) { return Err(format!("duplicate travel entry '{}'", key)); } seen_keys.push(key); entries.push(TravelEntry { mode, slug, use_best, filter_min, filter_max, }); } Ok(entries) } /// Per-cell travel time aggregation. pub struct TravelTimeAgg { pub min: f32, pub max: f32, pub sum: f64, pub count: u32, } impl TravelTimeAgg { pub fn new() -> Self { TravelTimeAgg { min: f32::INFINITY, max: f32::NEG_INFINITY, sum: 0.0, count: 0, } } #[inline] pub fn add(&mut self, value: f32) { if value < self.min { self.min = value; } if value > self.max { self.max = value; } self.sum += value as f64; self.count += 1; } }