1use crate::error::EveryMapResult;
2use crate::types::Coordinate;
3use async_trait::async_trait;
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Default, Serialize, Deserialize)]
8pub struct TrafficOptions {
9 pub radius: Option<f64>,
11 pub language: Option<String>,
13 pub include_incidents: Option<bool>,
15 #[serde(default, skip_serializing_if = "Option::is_none")]
17 pub provider_extra: Option<serde_json::Value>,
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
22pub enum IncidentSeverity {
23 Low,
24 Minor,
25 Major,
26 Critical,
27 Unknown,
28}
29
30#[derive(Debug, Clone, Serialize, Deserialize)]
32pub struct TrafficIncident {
33 pub id: Option<String>,
35 pub incident_type: Option<String>,
37 pub severity: Option<IncidentSeverity>,
39 pub description: Option<String>,
41 pub road_name: Option<String>,
43 pub start_time: Option<String>,
45 pub end_time: Option<String>,
47}
48
49#[derive(Debug, Clone, Serialize, Deserialize)]
51pub struct TrafficFlow {
52 pub speed: Option<f64>,
54 pub free_flow_speed: Option<f64>,
56 pub jam_factor: Option<f64>,
58 pub confidence: Option<f64>,
60 pub road_name: Option<String>,
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct TrafficResponse {
67 pub flows: Vec<TrafficFlow>,
69 pub incidents: Vec<TrafficIncident>,
71 #[serde(default, skip_serializing_if = "Option::is_none")]
73 pub raw: Option<serde_json::Value>,
74}
75
76#[async_trait]
77pub trait TrafficProvider: Send + Sync {
78 async fn get_traffic(
79 &self,
80 location: &Coordinate,
81 options: &TrafficOptions,
82 ) -> EveryMapResult<TrafficResponse>;
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88
89 #[test]
90 fn test_traffic_options_default() {
91 let options = TrafficOptions::default();
92 assert!(options.radius.is_none());
93 assert!(options.language.is_none());
94 assert!(options.include_incidents.is_none());
95 assert!(options.provider_extra.is_none());
96 }
97
98 #[test]
99 fn test_incident_severity_serialization() {
100 assert_eq!(
101 serde_json::to_string(&IncidentSeverity::Critical).unwrap(),
102 "\"Critical\""
103 );
104 let sev: IncidentSeverity = serde_json::from_str("\"Minor\"").unwrap();
105 assert_eq!(sev, IncidentSeverity::Minor);
106 }
107
108 #[test]
109 fn test_traffic_flow_construction() {
110 let flow = TrafficFlow {
111 speed: Some(80.0),
112 free_flow_speed: Some(100.0),
113 jam_factor: Some(3.5),
114 confidence: Some(0.9),
115 road_name: Some("A100".to_string()),
116 };
117 assert_eq!(flow.speed, Some(80.0));
118 assert_eq!(flow.jam_factor, Some(3.5));
119 assert_eq!(flow.road_name.as_deref(), Some("A100"));
120 }
121
122 #[test]
123 fn test_traffic_response_construction() {
124 let response = TrafficResponse {
125 flows: vec![TrafficFlow {
126 speed: Some(60.0),
127 free_flow_speed: Some(100.0),
128 jam_factor: Some(5.0),
129 confidence: Some(0.85),
130 road_name: None,
131 }],
132 incidents: vec![],
133 raw: None,
134 };
135 assert_eq!(response.flows.len(), 1);
136 assert!(response.incidents.is_empty());
137 }
138
139 #[test]
142 fn test_incident_severity_all_variants_serde() {
143 let variants = [
144 IncidentSeverity::Low,
145 IncidentSeverity::Minor,
146 IncidentSeverity::Major,
147 IncidentSeverity::Critical,
148 IncidentSeverity::Unknown,
149 ];
150 for v in &variants {
151 let json = serde_json::to_string(v).unwrap();
152 let back: IncidentSeverity = serde_json::from_str(&json).unwrap();
153 assert_eq!(*v, back, "Failed roundtrip for {:?}", v);
154 }
155 }
156
157 #[test]
158 fn test_incident_severity_all_variants_distinct() {
159 let variants = [
160 IncidentSeverity::Low,
161 IncidentSeverity::Minor,
162 IncidentSeverity::Major,
163 IncidentSeverity::Critical,
164 IncidentSeverity::Unknown,
165 ];
166 for i in 0..variants.len() {
167 for j in 0..variants.len() {
168 if i != j {
169 assert_ne!(variants[i], variants[j]);
170 }
171 }
172 }
173 }
174
175 #[test]
178 fn test_traffic_response_serde_roundtrip() {
179 let response = TrafficResponse {
180 flows: vec![TrafficFlow {
181 speed: Some(80.0),
182 free_flow_speed: Some(120.0),
183 jam_factor: Some(2.5),
184 confidence: Some(0.9),
185 road_name: Some("A9".to_string()),
186 }],
187 incidents: vec![TrafficIncident {
188 id: Some("inc-1".to_string()),
189 incident_type: Some("accident".to_string()),
190 severity: Some(IncidentSeverity::Major),
191 description: Some("Multi-vehicle accident".to_string()),
192 road_name: Some("A9".to_string()),
193 start_time: Some("2024-01-01T08:00:00".to_string()),
194 end_time: Some("2024-01-01T12:00:00".to_string()),
195 }],
196 raw: Some(serde_json::json!({"source": "here"})),
197 };
198 let json = serde_json::to_string(&response).unwrap();
199 let back: TrafficResponse = serde_json::from_str(&json).unwrap();
200 assert_eq!(back.flows.len(), 1);
201 assert_eq!(back.incidents.len(), 1);
202 assert_eq!(back.incidents[0].severity, Some(IncidentSeverity::Major));
203 assert!(back.raw.is_some());
204 }
205
206 #[test]
207 fn test_traffic_response_empty() {
208 let response = TrafficResponse {
209 flows: vec![],
210 incidents: vec![],
211 raw: None,
212 };
213 let json = serde_json::to_string(&response).unwrap();
214 let back: TrafficResponse = serde_json::from_str(&json).unwrap();
215 assert!(back.flows.is_empty());
216 assert!(back.incidents.is_empty());
217 }
218
219 #[test]
222 fn test_traffic_options_serde_roundtrip() {
223 let options = TrafficOptions {
224 radius: Some(5000.0),
225 language: Some("de".to_string()),
226 include_incidents: Some(true),
227 provider_extra: Some(serde_json::json!({"min_jam_factor": 4.0})),
228 };
229 let json = serde_json::to_string(&options).unwrap();
230 let back: TrafficOptions = serde_json::from_str(&json).unwrap();
231 assert_eq!(back.radius, Some(5000.0));
232 assert_eq!(back.include_incidents, Some(true));
233 assert!(back.provider_extra.is_some());
234 }
235
236 #[test]
239 fn test_traffic_incident_serde_roundtrip() {
240 let incident = TrafficIncident {
241 id: Some("inc-42".to_string()),
242 incident_type: Some("construction".to_string()),
243 severity: Some(IncidentSeverity::Minor),
244 description: Some("Road work, lane closed".to_string()),
245 road_name: Some("B96".to_string()),
246 start_time: Some("2024-03-01T06:00:00".to_string()),
247 end_time: Some("2024-09-01T18:00:00".to_string()),
248 };
249 let json = serde_json::to_string(&incident).unwrap();
250 let back: TrafficIncident = serde_json::from_str(&json).unwrap();
251 assert_eq!(back.id, incident.id);
252 assert_eq!(back.severity, incident.severity);
253 assert_eq!(back.description, incident.description);
254 }
255
256 #[test]
259 fn test_traffic_flow_serde_roundtrip() {
260 let flow = TrafficFlow {
261 speed: Some(0.0),
262 free_flow_speed: Some(100.0),
263 jam_factor: Some(10.0),
264 confidence: Some(0.5),
265 road_name: Some("A100".to_string()),
266 };
267 let json = serde_json::to_string(&flow).unwrap();
268 let back: TrafficFlow = serde_json::from_str(&json).unwrap();
269 assert_eq!(back.speed, Some(0.0));
270 assert_eq!(back.jam_factor, Some(10.0));
271 }
272
273 #[test]
276 fn test_traffic_flow_gridlock() {
277 let flow = TrafficFlow {
278 speed: Some(0.0),
279 free_flow_speed: Some(100.0),
280 jam_factor: Some(10.0),
281 confidence: Some(1.0),
282 road_name: None,
283 };
284 assert_eq!(flow.speed, Some(0.0));
285 assert_eq!(flow.jam_factor, Some(10.0));
286 }
287
288 #[test]
289 fn test_traffic_incident_all_none_optional_fields() {
290 let incident = TrafficIncident {
291 id: None,
292 incident_type: None,
293 severity: None,
294 description: None,
295 road_name: None,
296 start_time: None,
297 end_time: None,
298 };
299 assert!(incident.id.is_none());
300 assert!(incident.severity.is_none());
301 }
302}