use dbus::arg; use dbus_tree::MethodErr; use std::sync::Arc; use tokio::sync::{Mutex, MutexGuard}; use std::future::Future; use std::thread; use std::sync::mpsc; use futures_util::future::FutureExt; use crate::daemon::{Daemon, Event}; use crate::dbus::interface::NetBuzzertKordophoneRepository as DbusRepository; use crate::dbus::interface::NetBuzzertKordophoneSettings as DbusSettings; #[derive(Clone)] pub struct ServerImpl { daemon: Arc>, event_sender: mpsc::Sender, } impl ServerImpl { pub fn new(daemon: Arc>, event_sender: mpsc::Sender) -> Self { Self { daemon, event_sender } } pub async fn get_daemon(&self) -> MutexGuard<'_, Daemon> { self.daemon.lock().await // .map_err(|_| MethodErr::failed("Failed to lock daemon")) } pub fn daemon_then(&self, f: F) -> Result where F: FnOnce(MutexGuard<'_, Daemon>) -> T + Send, T: Send, { run_sync_future(self.get_daemon().then(|daemon| async move { f(daemon) })) } } impl DbusRepository for ServerImpl { fn get_version(&mut self) -> Result { self.daemon_then(|daemon| daemon.version.clone()) } fn get_conversations(&mut self) -> Result, dbus::MethodErr> { self.daemon_then(|mut daemon| { let conversations = daemon.get_conversations(); // Convert conversations to DBus property maps let result = conversations.into_iter().map(|conv| { let mut map = arg::PropMap::new(); map.insert("guid".into(), arg::Variant(Box::new(conv.guid))); map.insert("display_name".into(), arg::Variant(Box::new(conv.display_name.unwrap_or_default()))); map.insert("unread_count".into(), arg::Variant(Box::new(conv.unread_count as i32))); map }).collect(); Ok(result) })? } fn sync_all_conversations(&mut self) -> Result<(), dbus::MethodErr> { self.event_sender.send(Event::SyncAllConversations).unwrap_or_else(|e| { log::error!("Error sending sync event: {}", e); }); Ok(()) } } impl DbusSettings for ServerImpl { fn set_server(&mut self, url: String, user: String) -> Result<(), dbus::MethodErr> { todo!() } fn set_credential_item_(&mut self, item_path: dbus::Path<'static>) -> Result<(), dbus::MethodErr> { todo!() } fn server_url(&self) -> Result { todo!() } fn set_server_url(&self, value: String) -> Result<(), dbus::MethodErr> { todo!() } fn username(&self) -> Result { todo!() } fn set_username(&self, value: String) -> Result<(), dbus::MethodErr> { todo!() } fn credential_item(&self) -> Result, dbus::MethodErr> { todo!() } fn set_credential_item(&self, value: dbus::Path<'static>) -> Result<(), dbus::MethodErr> { todo!() } } fn run_sync_future(f: F) -> Result where T: Send, F: Future + Send, { // We use `scope` here to ensure that the thread is joined before the // function returns. This allows us to capture references of values that // have lifetimes shorter than 'static, which is what thread::spawn requires. thread::scope(move |s| { s.spawn(move || { let rt = tokio::runtime::Builder::new_current_thread() .enable_all() .build() .map_err(|_| MethodErr::failed("Unable to create tokio runtime"))?; let result = rt.block_on(f); Ok(result) }) .join() }) .expect("Error joining runtime thread") }