1use crate::error::EveryMapResult;
2use crate::types::{BoundingBox, Coordinate, Polyline};
3use async_trait::async_trait;
4use serde::{Deserialize, Deserializer, Serialize, Serializer};
5use std::fmt;
6
7impl fmt::Display for DepartureTime {
8 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
9 match self {
10 DepartureTime::Now => f.write_str("now"),
11 DepartureTime::Timestamp(ts) => write!(f, "{}", ts),
12 DepartureTime::Iso8601(s) => f.write_str(s),
13 }
14 }
15}
16
17#[derive(Debug, Clone, PartialEq)]
22pub enum DepartureTime {
23 Now,
25 Timestamp(i64),
27 Iso8601(String),
29}
30
31impl Serialize for DepartureTime {
32 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
33 let s = match self {
34 DepartureTime::Now => "now".to_string(),
35 DepartureTime::Timestamp(ts) => ts.to_string(),
36 DepartureTime::Iso8601(s) => s.clone(),
37 };
38 serializer.serialize_str(&s)
39 }
40}
41
42impl<'de> Deserialize<'de> for DepartureTime {
43 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
44 let s = String::deserialize(deserializer)?;
45 if s == "now" {
46 Ok(DepartureTime::Now)
47 } else if let Ok(ts) = s.parse::<i64>() {
48 Ok(DepartureTime::Timestamp(ts))
49 } else {
50 Ok(DepartureTime::Iso8601(s))
51 }
52 }
53}
54
55#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
57pub enum AvoidType {
58 Tolls,
59 Ferries,
60 Tunnels,
61 Highways,
62 DirtRoads,
63}
64
65#[derive(Debug, Clone, Default, Serialize, Deserialize)]
67pub struct RouteOptions {
68 pub transport_mode: Option<TransportMode>,
70 pub alternatives: Option<u32>,
72 pub avoid: Vec<AvoidType>,
74 pub departure_time: Option<DepartureTime>,
76 pub arrival_time: Option<DepartureTime>,
78 pub language: Option<String>,
80 #[serde(default, skip_serializing_if = "Option::is_none")]
82 pub provider_extra: Option<serde_json::Value>,
83}
84
85#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
87pub enum TransportMode {
88 Car,
89 Truck,
90 Pedestrian,
91 Bicycle,
92 Scooter,
93 Bus,
94 Taxi,
95 Unknown,
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct RouteStep {
101 pub instruction: Option<String>,
103 pub distance: Option<f64>,
105 pub duration: Option<f64>,
107 pub start_coordinate: Option<Coordinate>,
109 pub end_coordinate: Option<Coordinate>,
111}
112
113#[derive(Debug, Clone, Serialize, Deserialize)]
115pub struct RouteResult {
116 pub distance: f64,
118 pub duration: f64,
120 pub geometry: Polyline,
122 pub transport_mode: Option<TransportMode>,
124 pub steps: Vec<RouteStep>,
126 pub bounding_box: Option<BoundingBox>,
128 #[serde(default, skip_serializing_if = "Option::is_none")]
130 pub raw: Option<serde_json::Value>,
131}
132
133#[derive(Debug, Clone, Serialize, Deserialize)]
135pub struct RouteResponse {
136 pub routes: Vec<RouteResult>,
138}
139
140#[async_trait]
141pub trait Router: Send + Sync {
142 async fn calculate_route(
143 &self,
144 start: &Coordinate,
145 end: &Coordinate,
146 options: &RouteOptions,
147 ) -> EveryMapResult<RouteResponse>;
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153
154 #[test]
155 fn test_route_options_default() {
156 let options = RouteOptions::default();
157 assert!(options.transport_mode.is_none());
158 assert!(options.alternatives.is_none());
159 assert!(options.avoid.is_empty());
160 assert!(options.departure_time.is_none());
161 assert!(options.arrival_time.is_none());
162 assert!(options.language.is_none());
163 assert!(options.provider_extra.is_none());
164 }
165
166 #[test]
167 fn test_route_options_with_fields() {
168 let options = RouteOptions {
169 transport_mode: Some(TransportMode::Car),
170 alternatives: Some(3),
171 avoid: vec![AvoidType::Tolls, AvoidType::Ferries],
172 language: Some("de-DE".to_string()),
173 provider_extra: Some(serde_json::json!({"routing_mode": "fast"})),
174 ..Default::default()
175 };
176 assert_eq!(options.transport_mode, Some(TransportMode::Car));
177 assert_eq!(options.alternatives, Some(3));
178 assert_eq!(options.avoid.len(), 2);
179 }
180
181 #[test]
182 fn test_transport_mode_serialization() {
183 assert_eq!(
184 serde_json::to_string(&TransportMode::Car).unwrap(),
185 "\"Car\""
186 );
187 let mode: TransportMode = serde_json::from_str("\"Truck\"").unwrap();
188 assert_eq!(mode, TransportMode::Truck);
189 }
190
191 #[test]
192 fn test_avoid_type_serialization() {
193 assert_eq!(
194 serde_json::to_string(&AvoidType::Tolls).unwrap(),
195 "\"Tolls\""
196 );
197 let avoid: AvoidType = serde_json::from_str("\"Highways\"").unwrap();
198 assert_eq!(avoid, AvoidType::Highways);
199 }
200
201 #[test]
202 fn test_route_result_construction() {
203 let result = RouteResult {
204 distance: 15000.0,
205 duration: 1800.0,
206 geometry: Polyline::new(vec![]),
207 transport_mode: Some(TransportMode::Car),
208 steps: vec![],
209 bounding_box: None,
210 raw: None,
211 };
212 assert_eq!(result.distance, 15000.0);
213 assert_eq!(result.duration, 1800.0);
214 assert_eq!(result.transport_mode, Some(TransportMode::Car));
215 }
216
217 #[test]
220 fn test_avoid_type_all_variants_serde() {
221 let variants = [
222 AvoidType::Tolls,
223 AvoidType::Ferries,
224 AvoidType::Tunnels,
225 AvoidType::Highways,
226 AvoidType::DirtRoads,
227 ];
228 for v in &variants {
229 let json = serde_json::to_string(v).unwrap();
230 let back: AvoidType = serde_json::from_str(&json).unwrap();
231 assert_eq!(*v, back, "Failed roundtrip for {:?}", v);
232 }
233 }
234
235 #[test]
236 fn test_avoid_type_all_variants_distinct() {
237 let variants = [
238 AvoidType::Tolls,
239 AvoidType::Ferries,
240 AvoidType::Tunnels,
241 AvoidType::Highways,
242 AvoidType::DirtRoads,
243 ];
244 for i in 0..variants.len() {
245 for j in 0..variants.len() {
246 if i != j {
247 assert_ne!(variants[i], variants[j]);
248 }
249 }
250 }
251 }
252
253 #[test]
256 fn test_transport_mode_all_variants_serde() {
257 let variants = [
258 TransportMode::Car,
259 TransportMode::Truck,
260 TransportMode::Pedestrian,
261 TransportMode::Bicycle,
262 TransportMode::Scooter,
263 TransportMode::Bus,
264 TransportMode::Taxi,
265 TransportMode::Unknown,
266 ];
267 for v in &variants {
268 let json = serde_json::to_string(v).unwrap();
269 let back: TransportMode = serde_json::from_str(&json).unwrap();
270 assert_eq!(*v, back, "Failed roundtrip for {:?}", v);
271 }
272 }
273
274 #[test]
275 fn test_transport_mode_all_variants_distinct() {
276 let variants = [
277 TransportMode::Car,
278 TransportMode::Truck,
279 TransportMode::Pedestrian,
280 TransportMode::Bicycle,
281 TransportMode::Scooter,
282 TransportMode::Bus,
283 TransportMode::Taxi,
284 TransportMode::Unknown,
285 ];
286 for i in 0..variants.len() {
287 for j in 0..variants.len() {
288 if i != j {
289 assert_ne!(variants[i], variants[j]);
290 }
291 }
292 }
293 }
294
295 #[test]
298 fn test_route_response_serde_roundtrip() {
299 let response = RouteResponse {
300 routes: vec![RouteResult {
301 distance: 15000.0,
302 duration: 1800.0,
303 geometry: Polyline::new(vec![]),
304 transport_mode: Some(TransportMode::Car),
305 steps: vec![],
306 bounding_box: None,
307 raw: None,
308 }],
309 };
310 let json = serde_json::to_string(&response).unwrap();
311 let back: RouteResponse = serde_json::from_str(&json).unwrap();
312 assert_eq!(back.routes.len(), 1);
313 assert_eq!(back.routes[0].distance, 15000.0);
314 assert_eq!(back.routes[0].transport_mode, Some(TransportMode::Car));
315 }
316
317 #[test]
318 fn test_route_response_empty_routes() {
319 let response = RouteResponse { routes: vec![] };
320 let json = serde_json::to_string(&response).unwrap();
321 let back: RouteResponse = serde_json::from_str(&json).unwrap();
322 assert!(back.routes.is_empty());
323 }
324
325 #[test]
328 fn test_route_options_serde_roundtrip() {
329 let options = RouteOptions {
330 transport_mode: Some(TransportMode::Truck),
331 alternatives: Some(2),
332 avoid: vec![AvoidType::Tolls, AvoidType::Ferries],
333 departure_time: Some(DepartureTime::Iso8601("2024-06-01T08:00:00".to_string())),
334 arrival_time: None,
335 language: Some("en".to_string()),
336 provider_extra: Some(
337 serde_json::json!({"routing_mode": "fast", "truck": {"weight": 18}}),
338 ),
339 };
340 let json = serde_json::to_string(&options).unwrap();
341 let back: RouteOptions = serde_json::from_str(&json).unwrap();
342 assert_eq!(back.transport_mode, Some(TransportMode::Truck));
343 assert_eq!(back.alternatives, Some(2));
344 assert_eq!(back.avoid.len(), 2);
345 assert!(back.provider_extra.is_some());
346 }
347
348 #[test]
351 fn test_route_step_serde_roundtrip() {
352 let step = RouteStep {
353 instruction: Some("Turn right onto Main St".to_string()),
354 distance: Some(500.0),
355 duration: Some(60.0),
356 start_coordinate: Some(Coordinate::new(52.5, 13.4).unwrap()),
357 end_coordinate: Some(Coordinate::new(52.51, 13.41).unwrap()),
358 };
359 let json = serde_json::to_string(&step).unwrap();
360 let back: RouteStep = serde_json::from_str(&json).unwrap();
361 assert_eq!(back.instruction.as_deref(), Some("Turn right onto Main St"));
362 assert_eq!(back.distance, Some(500.0));
363 assert_eq!(back.duration, Some(60.0));
364 }
365
366 #[test]
369 fn test_route_result_full_serde_roundtrip() {
370 let result = RouteResult {
371 distance: 0.0,
372 duration: 0.0,
373 geometry: Polyline::new(vec![Coordinate::new(52.5, 13.4).unwrap()]),
374 transport_mode: Some(TransportMode::Pedestrian),
375 steps: vec![RouteStep {
376 instruction: Some("Walk north".to_string()),
377 distance: Some(100.0),
378 duration: Some(120.0),
379 start_coordinate: None,
380 end_coordinate: None,
381 }],
382 bounding_box: Some(BoundingBox::new(
383 Coordinate::new(52.6, 13.5).unwrap(),
384 Coordinate::new(52.4, 13.3).unwrap(),
385 )),
386 raw: Some(serde_json::json!({"legs": []})),
387 };
388 let json = serde_json::to_string(&result).unwrap();
389 let back: RouteResult = serde_json::from_str(&json).unwrap();
390 assert_eq!(back.distance, 0.0);
391 assert_eq!(back.steps.len(), 1);
392 assert!(back.bounding_box.is_some());
393 assert!(back.raw.is_some());
394 }
395
396 #[test]
399 fn test_route_result_zero_distance_duration() {
400 let result = RouteResult {
401 distance: 0.0,
402 duration: 0.0,
403 geometry: Polyline::new(vec![]),
404 transport_mode: None,
405 steps: vec![],
406 bounding_box: None,
407 raw: None,
408 };
409 assert_eq!(result.distance, 0.0);
410 assert_eq!(result.duration, 0.0);
411 }
412
413 #[test]
414 fn test_route_options_all_avoid_types() {
415 let options = RouteOptions {
416 avoid: vec![
417 AvoidType::Tolls,
418 AvoidType::Ferries,
419 AvoidType::Tunnels,
420 AvoidType::Highways,
421 AvoidType::DirtRoads,
422 ],
423 ..Default::default()
424 };
425 assert_eq!(options.avoid.len(), 5);
426 }
427
428 #[test]
431 fn test_departure_time_now_serde() {
432 let json = serde_json::to_string(&DepartureTime::Now).unwrap();
433 assert_eq!(json, "\"now\"");
434 let back: DepartureTime = serde_json::from_str(&json).unwrap();
435 assert_eq!(back, DepartureTime::Now);
436 }
437
438 #[test]
439 fn test_departure_time_timestamp_serde() {
440 let dt = DepartureTime::Timestamp(1717200000);
441 let json = serde_json::to_string(&dt).unwrap();
442 assert_eq!(json, "\"1717200000\"");
443 let back: DepartureTime = serde_json::from_str(&json).unwrap();
444 assert_eq!(back, DepartureTime::Timestamp(1717200000));
445 }
446
447 #[test]
448 fn test_departure_time_iso8601_serde() {
449 let dt = DepartureTime::Iso8601("2024-06-01T08:00:00Z".to_string());
450 let json = serde_json::to_string(&dt).unwrap();
451 assert_eq!(json, "\"2024-06-01T08:00:00Z\"");
452 let back: DepartureTime = serde_json::from_str(&json).unwrap();
453 assert_eq!(
454 back,
455 DepartureTime::Iso8601("2024-06-01T08:00:00Z".to_string())
456 );
457 }
458}