use std::fmt::Debug;
use futures_util::Stream;
use serde::{Deserialize, Serialize};
use serde_repr::Serialize_repr;
use zbus::zvariant::{
ObjectPath, Optional, OwnedObjectPath, Type,
as_value::{self, optional},
};
use super::{HandleToken, Request, Session, session::SessionPortal};
use crate::{Error, WindowIdentifier, proxy::Proxy};
#[cfg_attr(feature = "glib", derive(glib::Enum))]
#[cfg_attr(feature = "glib", enum_type(name = "AshpdLocationAccuracy"))]
#[derive(Serialize_repr, PartialEq, Eq, Clone, Copy, Debug, Type)]
#[doc(alias = "XdpLocationAccuracy")]
#[repr(u32)]
pub enum Accuracy {
#[doc(alias = "XDP_LOCATION_ACCURACY_NONE")]
None = 0,
#[doc(alias = "XDP_LOCATION_ACCURACY_COUNTRY")]
Country = 1,
#[doc(alias = "XDP_LOCATION_ACCURACY_CITY")]
City = 2,
#[doc(alias = "XDP_LOCATION_ACCURACY_NEIGHBORHOOD")]
Neighborhood = 3,
#[doc(alias = "XDP_LOCATION_ACCURACY_STREET")]
Street = 4,
#[doc(alias = "XDP_LOCATION_ACCURACY_EXACT")]
Exact = 5,
}
#[derive(Serialize, Type, Debug, Default)]
#[zvariant(signature = "dict")]
pub struct CreateSessionOptions {
#[serde(with = "as_value")]
session_handle_token: HandleToken,
#[serde(
rename = "distance-threshold",
with = "optional",
skip_serializing_if = "Option::is_none"
)]
distance_threshold: Option<u32>,
#[serde(
rename = "time-threshold",
with = "optional",
skip_serializing_if = "Option::is_none"
)]
time_threshold: Option<u32>,
#[serde(with = "optional", skip_serializing_if = "Option::is_none")]
accuracy: Option<Accuracy>,
}
impl CreateSessionOptions {
pub fn set_distance_threshold(mut self, distance_threshold: impl Into<Option<u32>>) -> Self {
self.distance_threshold = distance_threshold.into();
self
}
pub fn set_time_threshold(mut self, time_threshold: impl Into<Option<u32>>) -> Self {
self.time_threshold = time_threshold.into();
self
}
pub fn set_accuracy(mut self, accuracy: impl Into<Option<Accuracy>>) -> Self {
self.accuracy = accuracy.into();
self
}
}
#[derive(Serialize, Type, Debug, Default)]
#[zvariant(signature = "dict")]
pub struct StartOptions {
#[serde(with = "as_value")]
handle_token: HandleToken,
}
#[derive(Deserialize, Type)]
pub struct Location(OwnedObjectPath, LocationInner);
impl Location {
pub fn session_handle(&self) -> ObjectPath<'_> {
self.0.as_ref()
}
pub fn accuracy(&self) -> f64 {
self.1.accuracy
}
pub fn altitude(&self) -> Option<f64> {
if self.1.altitude == -f64::MAX {
None
} else {
Some(self.1.altitude)
}
}
pub fn speed(&self) -> Option<f64> {
if self.1.speed == -1f64 {
None
} else {
Some(self.1.speed)
}
}
pub fn heading(&self) -> Option<f64> {
if self.1.heading == -1f64 {
None
} else {
Some(self.1.heading)
}
}
pub fn description(&self) -> Option<&str> {
if self.1.description.is_empty() {
None
} else {
Some(&self.1.description)
}
}
pub fn latitude(&self) -> f64 {
self.1.latitude
}
pub fn longitude(&self) -> f64 {
self.1.longitude
}
pub fn timestamp(&self) -> std::time::Duration {
std::time::Duration::from_secs(self.1.timestamp.0)
}
}
impl Debug for Location {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Location")
.field("accuracy", &self.accuracy())
.field("altitude", &self.altitude())
.field("speed", &self.speed())
.field("heading", &self.heading())
.field("description", &self.description())
.field("latitude", &self.latitude())
.field("longitude", &self.longitude())
.field("timestamp", &self.timestamp())
.finish()
}
}
#[derive(Debug, Serialize, Deserialize, Type)]
#[zvariant(signature = "dict")]
#[serde(rename_all = "PascalCase")]
struct LocationInner {
#[serde(with = "as_value")]
accuracy: f64,
#[serde(with = "as_value")]
altitude: f64,
#[serde(with = "as_value")]
speed: f64,
#[serde(with = "as_value")]
heading: f64,
#[serde(with = "as_value")]
description: String,
#[serde(with = "as_value")]
latitude: f64,
#[serde(with = "as_value")]
longitude: f64,
#[serde(with = "as_value")]
timestamp: (u64, u64),
}
#[derive(Debug, Clone)]
#[doc(alias = "org.freedesktop.portal.Location")]
pub struct LocationProxy(Proxy<'static>);
impl LocationProxy {
pub async fn new() -> Result<Self, Error> {
let proxy = Proxy::new_desktop("org.freedesktop.portal.Location").await?;
Ok(Self(proxy))
}
pub async fn with_connection(connection: zbus::Connection) -> Result<Self, Error> {
let proxy =
Proxy::new_desktop_with_connection(connection, "org.freedesktop.portal.Location")
.await?;
Ok(Self(proxy))
}
pub fn version(&self) -> u32 {
self.0.version()
}
#[doc(alias = "LocationUpdated")]
#[doc(alias = "XdpPortal::location-updated")]
pub async fn receive_location_updated(
&self,
) -> Result<impl Stream<Item = Location> + use<>, Error> {
self.0.signal("LocationUpdated").await
}
#[doc(alias = "CreateSession")]
pub async fn create_session(
&self,
options: CreateSessionOptions,
) -> Result<Session<Self>, Error> {
let (path, proxy) = futures_util::try_join!(
self.0.call::<OwnedObjectPath>("CreateSession", &(options)),
Session::from_unique_name(self.0.connection().clone(), &options.session_handle_token),
)?;
assert_eq!(proxy.path(), &path.into_inner());
Ok(proxy)
}
#[doc(alias = "Start")]
#[doc(alias = "xdp_portal_location_monitor_start")]
pub async fn start(
&self,
session: &Session<Self>,
identifier: Option<&WindowIdentifier>,
options: StartOptions,
) -> Result<Request<()>, Error> {
let identifier = Optional::from(identifier);
self.0
.empty_request(
&options.handle_token,
"Start",
&(session, identifier, &options),
)
.await
}
}
impl crate::Sealed for LocationProxy {}
impl SessionPortal for LocationProxy {}
impl std::ops::Deref for LocationProxy {
type Target = zbus::Proxy<'static>;
fn deref(&self) -> &Self::Target {
&self.0
}
}