use dbus::arg; use dbus_tree::MethodErr; use std::sync::{Arc, Mutex, MutexGuard}; use std::future::Future; use std::thread; use crate::daemon::Daemon; use crate::dbus::interface::NetBuzzertKordophoneRepository as DbusRepository; use crate::dbus::interface::NetBuzzertKordophoneSettings as DbusSettings; #[derive(Clone)] pub struct ServerImpl { daemon: Arc>, } impl ServerImpl { pub fn new(daemon: Arc>) -> Self { Self { daemon } } pub fn get_daemon(&self) -> Result, MethodErr> { self.daemon.lock().map_err(|_| MethodErr::failed("Failed to lock daemon")) } } impl DbusRepository for ServerImpl { fn get_version(&mut self) -> Result { let daemon = self.get_daemon()?; Ok(daemon.version.clone()) } fn get_conversations(&mut self) -> Result, dbus::MethodErr> { // Get a repository instance and use it to fetch conversations let mut daemon = self.get_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 { let mut daemon = self.get_daemon()?; // TODO: We don't actually probably want to block here. run_sync_future(daemon.sync_all_conversations()) .unwrap() .map_err(|e| { log::error!("Failed to sync conversations: {}", e); MethodErr::failed(&format!("Failed to sync conversations: {}", e)) })?; Ok(true) } } 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") }