use lingxia_platform::traits::location::{Location as PlatformLocation, LocationRequestConfig};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LocationOptions {
#[serde(default)]
pub high_accuracy: bool,
}
impl LocationOptions {
pub fn new() -> Self {
Self::default()
}
pub fn high_accuracy() -> Self {
Self {
high_accuracy: true,
}
}
}
#[derive(Debug, Clone, Serialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Location {
pub latitude: f64,
pub longitude: f64,
#[serde(default)]
pub accuracy: f64,
#[serde(default)]
pub altitude: f64,
#[serde(default)]
pub speed: f64,
#[serde(default)]
pub vertical_accuracy: f64,
#[serde(default)]
pub horizontal_accuracy: f64,
}
#[derive(Deserialize)]
struct RawLocation {
latitude: f64,
longitude: f64,
#[serde(default)]
accuracy: f64,
#[serde(default)]
altitude: f64,
#[serde(default)]
speed: f64,
#[serde(default)]
vertical_accuracy: f64,
#[serde(default)]
horizontal_accuracy: f64,
}
impl From<RawLocation> for Location {
fn from(raw: RawLocation) -> Self {
Self {
latitude: raw.latitude,
longitude: raw.longitude,
accuracy: raw.accuracy,
altitude: raw.altitude,
speed: raw.speed,
vertical_accuracy: raw.vertical_accuracy,
horizontal_accuracy: raw.horizontal_accuracy,
}
}
}
pub async fn request(options: LocationOptions) -> crate::Result<Location> {
let config = LocationRequestConfig {
is_high_accuracy: options.high_accuracy,
high_accuracy_expire_time: None,
include_altitude: options.high_accuracy,
};
let raw = crate::runtime::platform()?
.request_location(config)
.await
.map_err(crate::Error::from)?;
let parsed: RawLocation = serde_json::from_str(&raw)
.map_err(|err| crate::Error::platform(format!("parse location: {err} (raw: {raw})")))?;
Ok(parsed.into())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_platform_location_payload() {
let raw = r#"{"latitude":37.33,"longitude":-122.03,"speed":1.5,"accuracy":5.0,"altitude":12.0,"vertical_accuracy":3.0,"horizontal_accuracy":5.0}"#;
let parsed: RawLocation = serde_json::from_str(raw).unwrap();
let dto: Location = parsed.into();
assert_eq!(dto.latitude, 37.33);
assert_eq!(dto.longitude, -122.03);
assert_eq!(dto.accuracy, 5.0);
assert_eq!(dto.altitude, 12.0);
}
#[test]
fn tolerates_minimal_payload() {
let raw = r#"{"latitude":1.0,"longitude":2.0}"#;
let parsed: RawLocation = serde_json::from_str(raw).unwrap();
let dto: Location = parsed.into();
assert_eq!(dto.accuracy, 0.0);
assert_eq!(dto.speed, 0.0);
}
}