everymap_core/domains/
positioning.rs1use crate::error::EveryMapResult;
2use crate::types::Coordinate;
3use async_trait::async_trait;
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Default, Serialize, Deserialize)]
12pub struct PositioningOptions {
13 #[serde(default, skip_serializing_if = "Option::is_none")]
15 pub provider_extra: Option<serde_json::Value>,
16}
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct PositioningResponse {
21 pub coordinate: Coordinate,
23 pub accuracy: Option<f64>,
25 pub altitude: Option<f64>,
27 pub altitude_accuracy: Option<f64>,
29 #[serde(default, skip_serializing_if = "Option::is_none")]
31 pub raw: Option<serde_json::Value>,
32}
33
34#[async_trait]
35pub trait NetworkPositioner: Send + Sync {
36 async fn get_position(
37 &self,
38 options: &PositioningOptions,
39 ) -> EveryMapResult<PositioningResponse>;
40}
41
42#[cfg(test)]
43mod tests {
44 use super::*;
45
46 #[test]
47 fn test_positioning_options_default() {
48 let options = PositioningOptions::default();
49 assert!(options.provider_extra.is_none());
50 }
51
52 #[test]
53 fn test_positioning_options_with_provider_extra() {
54 let options = PositioningOptions {
55 provider_extra: Some(serde_json::json!({"wlan": [{"mac": "aa:bb:cc:dd:ee:ff"}]})),
56 };
57 assert!(options.provider_extra.is_some());
58 }
59
60 #[test]
61 fn test_positioning_response_construction() {
62 let coordinate = Coordinate::new(52.5, 13.4).unwrap();
63 let response = PositioningResponse {
64 coordinate,
65 accuracy: Some(50.0),
66 altitude: Some(34.0),
67 altitude_accuracy: Some(10.0),
68 raw: None,
69 };
70 assert_eq!(response.coordinate.lat, 52.5);
71 assert_eq!(response.accuracy, Some(50.0));
72 assert_eq!(response.altitude, Some(34.0));
73 }
74
75 #[test]
78 fn test_positioning_response_serde_roundtrip() {
79 let coordinate = Coordinate::new(52.5, 13.4).unwrap();
80 let response = PositioningResponse {
81 coordinate,
82 accuracy: Some(50.0),
83 altitude: Some(34.0),
84 altitude_accuracy: Some(10.0),
85 raw: Some(serde_json::json!({"wlan_count": 5})),
86 };
87 let json = serde_json::to_string(&response).unwrap();
88 let back: PositioningResponse = serde_json::from_str(&json).unwrap();
89 assert_eq!(back.coordinate.lat, 52.5);
90 assert_eq!(back.accuracy, Some(50.0));
91 assert_eq!(back.altitude, Some(34.0));
92 assert!(back.raw.is_some());
93 }
94
95 #[test]
96 fn test_positioning_response_minimal_serde_roundtrip() {
97 let coordinate = Coordinate::ORIGIN;
98 let response = PositioningResponse {
99 coordinate,
100 accuracy: None,
101 altitude: None,
102 altitude_accuracy: None,
103 raw: None,
104 };
105 let json = serde_json::to_string(&response).unwrap();
106 let back: PositioningResponse = serde_json::from_str(&json).unwrap();
107 assert_eq!(back.coordinate, coordinate);
108 assert!(back.accuracy.is_none());
109 }
110
111 #[test]
114 fn test_positioning_options_serde_roundtrip() {
115 let options = PositioningOptions {
116 provider_extra: Some(serde_json::json!({
117 "wlan": [{"mac": "aa:bb:cc:dd:ee:ff", "signalStrength": -70}],
118 "cell": [{"mcc": 262, "mnc": 1, "cid": 12345}]
119 })),
120 };
121 let json = serde_json::to_string(&options).unwrap();
122 let back: PositioningOptions = serde_json::from_str(&json).unwrap();
123 assert!(back.provider_extra.is_some());
124 }
125
126 #[test]
129 fn test_positioning_response_boundary_coordinates() {
130 let coordinate = Coordinate::new(90.0, 180.0).unwrap();
131 let response = PositioningResponse {
132 coordinate,
133 accuracy: Some(0.0),
134 altitude: Some(0.0),
135 altitude_accuracy: Some(0.0),
136 raw: None,
137 };
138 assert_eq!(response.coordinate.lat, 90.0);
139 assert_eq!(response.accuracy, Some(0.0));
140 }
141
142 #[test]
143 fn test_positioning_response_negative_altitude() {
144 let coordinate = Coordinate::new(-33.8688, 151.2093).unwrap();
145 let response = PositioningResponse {
146 coordinate,
147 accuracy: Some(15.0),
148 altitude: Some(-11.0),
149 altitude_accuracy: Some(5.0),
150 raw: None,
151 };
152 assert_eq!(response.altitude, Some(-11.0));
153 }
154}