waterkit-location 0.1.1

Cross-platform location and GPS services
Documentation
//! Apple platform (iOS/macOS) location implementation using swift-bridge.

use crate::{Location, LocationError, Timestamp};

#[swift_bridge::bridge]
mod ffi {
    // Shared struct for location data
    #[swift_bridge(swift_repr = "struct")]
    struct LocationData {
        latitude: f64,
        longitude: f64,
        altitude: f64,
        horizontal_accuracy: f64,
        vertical_accuracy: f64,
        timestamp_ms: i64,
    }

    // Result type for location requests
    enum LocationResult {
        Success(LocationData),
        PermissionDenied,
        ServiceDisabled,
        Timeout,
        NotAvailable,
    }

    extern "Swift" {
        fn get_current_location(callback: Box<dyn FnOnce(LocationResult) -> ()>);
    }
}

/// Get the current location on Apple platforms.
///
/// # Errors
/// Returns a `LocationError` if the location cannot be retrieved.
pub async fn get_location() -> Result<Location, LocationError> {
    let (sender, receiver) = futures::channel::oneshot::channel();
    ffi::get_current_location(Box::new(move |result| {
        let _ = sender.send(result);
    }));
    let result = receiver
        .await
        .map_err(|_| LocationError::Platform("location callback dropped".into()))?;
    match result {
        ffi::LocationResult::Success(data) => {
            let timestamp = Timestamp::from_millisecond(data.timestamp_ms)
                .map_err(|e| LocationError::Platform(e.to_string()))?;

            let mut location = Location::from_degrees(data.latitude, data.longitude, timestamp)?;

            // CoreLocation reports altitude validity through verticalAccuracy:
            // a negative value means the altitude is invalid. The altitude
            // itself is a plain double (0.0 is a legal reading), never NaN.
            if data.vertical_accuracy >= 0.0 {
                location = location
                    .with_altitude(data.altitude)
                    .with_vertical_accuracy(data.vertical_accuracy);
            }
            if data.horizontal_accuracy >= 0.0 {
                location = location.with_horizontal_accuracy(data.horizontal_accuracy);
            }

            Ok(location)
        }
        ffi::LocationResult::PermissionDenied => Err(LocationError::PermissionDenied),
        ffi::LocationResult::ServiceDisabled => Err(LocationError::ServiceDisabled),
        ffi::LocationResult::Timeout => Err(LocationError::Timeout),
        ffi::LocationResult::NotAvailable => Err(LocationError::NotAvailable),
    }
}