1
0
Fork 0
mirror of https://github.com/Zedfrigg/ironbar.git synced 2025-07-01 10:41:03 +02:00

refactor: better error handling for client initialization

This commit is contained in:
Jake Stanger 2024-04-01 16:34:25 +01:00
parent 1b35354272
commit 9245188af7
No known key found for this signature in database
GPG key ID: C51FC8F9CB0BEA61
10 changed files with 97 additions and 49 deletions

View file

@ -1,4 +1,4 @@
use crate::{await_sync, register_client}; use crate::{await_sync, register_fallible_client};
use cfg_if::cfg_if; use cfg_if::cfg_if;
use color_eyre::{Help, Report, Result}; use color_eyre::{Help, Report, Result};
use std::fmt::{Debug, Display, Formatter}; use std::fmt::{Debug, Display, Formatter};
@ -141,4 +141,4 @@ pub trait WorkspaceClient: Debug + Send + Sync {
fn subscribe_workspace_change(&self) -> broadcast::Receiver<WorkspaceUpdate>; fn subscribe_workspace_change(&self) -> broadcast::Receiver<WorkspaceUpdate>;
} }
register_client!(dyn WorkspaceClient, workspaces); register_fallible_client!(dyn WorkspaceClient, workspaces);

View file

@ -1,4 +1,5 @@
use crate::Ironbar; use crate::{await_sync, Ironbar};
use color_eyre::Result;
use std::sync::Arc; use std::sync::Arc;
#[cfg(feature = "clipboard")] #[cfg(feature = "clipboard")]
@ -38,6 +39,8 @@ pub struct Clients {
volume: Option<Arc<volume::Client>>, volume: Option<Arc<volume::Client>>,
} }
pub type ClientResult<T> = Result<Arc<T>>;
impl Clients { impl Clients {
pub(crate) fn new() -> Self { pub(crate) fn new() -> Self {
Self::default() Self::default()
@ -59,13 +62,17 @@ impl Clients {
} }
#[cfg(feature = "workspaces")] #[cfg(feature = "workspaces")]
pub fn workspaces(&mut self) -> Arc<dyn compositor::WorkspaceClient> { pub fn workspaces(&mut self) -> ClientResult<dyn compositor::WorkspaceClient> {
// TODO: Error handling here isn't great - should throw a user-friendly error & exit let client = match &self.workspaces {
self.workspaces Some(workspaces) => workspaces.clone(),
.get_or_insert_with(|| { None => {
compositor::Compositor::create_workspace_client().expect("to be valid compositor") let client = compositor::Compositor::create_workspace_client()?;
}) self.workspaces.replace(client.clone());
.clone() client
}
};
Ok(client)
} }
#[cfg(feature = "music")] #[cfg(feature = "music")]
@ -77,29 +84,35 @@ impl Clients {
} }
#[cfg(feature = "notifications")] #[cfg(feature = "notifications")]
pub fn notifications(&mut self) -> Arc<swaync::Client> { pub fn notifications(&mut self) -> ClientResult<swaync::Client> {
self.notifications let client = match &self.notifications {
.get_or_insert_with(|| { Some(client) => client.clone(),
Arc::new(crate::await_sync(async { swaync::Client::new().await })) None => {
}) let client = await_sync(async { swaync::Client::new().await })?;
.clone() let client = Arc::new(client);
self.notifications.replace(client.clone());
client
}
};
Ok(client)
} }
#[cfg(feature = "tray")] #[cfg(feature = "tray")]
pub fn tray(&mut self) -> Arc<tray::Client> { pub fn tray(&mut self) -> ClientResult<tray::Client> {
// TODO: Error handling here isn't great - should throw a user-friendly error let client = match &self.tray {
self.tray Some(client) => client.clone(),
.get_or_insert_with(|| { None => {
Arc::new(crate::await_sync(async { let service_name = format!("{}-{}", env!("CARGO_CRATE_NAME"), Ironbar::unique_id());
let service_name =
format!("{}-{}", env!("CARGO_CRATE_NAME"), Ironbar::unique_id());
tray::Client::new(&service_name) let client = await_sync(async { tray::Client::new(&service_name).await })?;
.await let client = Arc::new(client);
.expect("to be able to start client") self.tray.replace(client.clone());
})) client
}) }
.clone() };
Ok(client)
} }
#[cfg(feature = "upower")] #[cfg(feature = "upower")]
@ -126,6 +139,14 @@ pub trait ProvidesClient<T: ?Sized> {
fn provide(&self) -> Arc<T>; fn provide(&self) -> Arc<T>;
} }
/// Types implementing this trait
/// indicate that they provide a singleton client instance of type `T`,
/// which may fail to be created.
pub trait ProvidesFallibleClient<T: ?Sized> {
/// Returns a singleton client instance of type `T`.
fn try_provide(&self) -> ClientResult<T>;
}
/// Generates a `ProvidesClient` impl block on `WidgetContext` /// Generates a `ProvidesClient` impl block on `WidgetContext`
/// for the provided `$ty` (first argument) client type. /// for the provided `$ty` (first argument) client type.
/// ///
@ -148,3 +169,26 @@ macro_rules! register_client {
} }
}; };
} }
/// Generates a `ProvidesClient` impl block on `WidgetContext`
/// for the provided `$ty` (first argument) client type.
///
/// The implementation calls `$method` (second argument)
/// on the `Clients` struct to obtain the client instance.
///
/// # Example
/// `register_client!(Client, clipboard);`
#[macro_export]
macro_rules! register_fallible_client {
($ty:ty, $method:ident) => {
impl<TSend, TReceive> $crate::clients::ProvidesFallibleClient<$ty>
for $crate::modules::WidgetContext<TSend, TReceive>
where
TSend: Clone,
{
fn try_provide(&self) -> color_eyre::Result<std::sync::Arc<$ty>> {
self.ironbar.clients.borrow_mut().$method()
}
}
};
}

View file

@ -1,6 +1,6 @@
mod dbus; mod dbus;
use crate::{register_client, send, spawn}; use crate::{register_fallible_client, send, spawn};
use color_eyre::{Report, Result}; use color_eyre::{Report, Result};
use dbus::SwayNcProxy; use dbus::SwayNcProxy;
use serde::Deserialize; use serde::Deserialize;
@ -24,9 +24,9 @@ type GetSubscribeData = (bool, bool, u32, bool);
impl From<GetSubscribeData> for Event { impl From<GetSubscribeData> for Event {
fn from((dnd, cc_open, count, inhibited): (bool, bool, u32, bool)) -> Self { fn from((dnd, cc_open, count, inhibited): (bool, bool, u32, bool)) -> Self {
Self { Self {
count,
dnd, dnd,
cc_open, cc_open,
count,
inhibited, inhibited,
} }
} }
@ -40,15 +40,13 @@ pub struct Client {
} }
impl Client { impl Client {
pub async fn new() -> Self { pub async fn new() -> Result<Self> {
let dbus = Box::pin(zbus::Connection::session()) let dbus = Box::pin(zbus::Connection::session()).await?;
.await
.expect("failed to create connection to system bus");
let proxy = SwayNcProxy::new(&dbus).await.unwrap(); let proxy = SwayNcProxy::new(&dbus).await?;
let (tx, rx) = broadcast::channel(8); let (tx, rx) = broadcast::channel(8);
let mut stream = proxy.receive_subscribe_v2().await.unwrap(); let mut stream = proxy.receive_subscribe_v2().await?;
{ {
let tx = tx.clone(); let tx = tx.clone();
@ -62,7 +60,7 @@ impl Client {
}); });
} }
Self { proxy, tx, _rx: rx } Ok(Self { proxy, tx, _rx: rx })
} }
pub fn subscribe(&self) -> broadcast::Receiver<Event> { pub fn subscribe(&self) -> broadcast::Receiver<Event> {
@ -85,4 +83,4 @@ impl Client {
} }
} }
register_client!(Client, notifications); register_fallible_client!(Client, notifications);

View file

@ -1,4 +1,4 @@
use crate::register_client; use crate::register_fallible_client;
pub use system_tray::client::Client; pub use system_tray::client::Client;
register_client!(Client, tray); register_fallible_client!(Client, tray);

View file

@ -129,7 +129,7 @@ impl Ipc {
ironbar.reload_config(); ironbar.reload_config();
for output in outputs { for output in outputs {
match crate::load_output_bars(ironbar, application, output) { match crate::load_output_bars(ironbar, application, &output) {
Ok(mut bars) => ironbar.bars.borrow_mut().append(&mut bars), Ok(mut bars) => ironbar.bars.borrow_mut().append(&mut bars),
Err(err) => error!("{err:?}"), Err(err) => error!("{err:?}"),
} }

View file

@ -13,7 +13,6 @@ use gtk::prelude::*;
use gtk::{Button, EventBox, Image, Label, Orientation, RadioButton, Widget}; use gtk::{Button, EventBox, Image, Label, Orientation, RadioButton, Widget};
use serde::Deserialize; use serde::Deserialize;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::{broadcast, mpsc}; use tokio::sync::{broadcast, mpsc};
use tracing::{debug, error}; use tracing::{debug, error};
@ -76,7 +75,7 @@ impl Module<Button> for ClipboardModule {
let max_items = self.max_items; let max_items = self.max_items;
let tx = context.tx.clone(); let tx = context.tx.clone();
let client: Arc<clipboard::Client> = context.client(); let client = context.client::<clipboard::Client>();
// listen to clipboard events // listen to clipboard events
spawn(async move { spawn(async move {

View file

@ -10,7 +10,7 @@ use gtk::{Application, Button, EventBox, IconTheme, Orientation, Revealer, Widge
use tokio::sync::{broadcast, mpsc}; use tokio::sync::{broadcast, mpsc};
use tracing::debug; use tracing::debug;
use crate::clients::ProvidesClient; use crate::clients::{ClientResult, ProvidesClient, ProvidesFallibleClient};
use crate::config::{BarPosition, CommonConfig, TransitionType}; use crate::config::{BarPosition, CommonConfig, TransitionType};
use crate::gtk_helpers::{IronbarGtkExt, WidgetGeometry}; use crate::gtk_helpers::{IronbarGtkExt, WidgetGeometry};
use crate::popup::Popup; use crate::popup::Popup;
@ -115,6 +115,13 @@ where
ProvidesClient::provide(self) ProvidesClient::provide(self)
} }
pub fn try_client<T: ?Sized>(&self) -> ClientResult<T>
where
WidgetContext<TSend, TReceive>: ProvidesFallibleClient<T>,
{
ProvidesFallibleClient::try_provide(self)
}
/// Subscribes to events sent from this widget. /// Subscribes to events sent from this widget.
pub fn subscribe(&self) -> broadcast::Receiver<TSend> { pub fn subscribe(&self) -> broadcast::Receiver<TSend> {
self.update_tx.subscribe() self.update_tx.subscribe()

View file

@ -108,7 +108,7 @@ impl Module<Overlay> for NotificationsModule {
where where
<Self as Module<Overlay>>::SendMessage: Clone, <Self as Module<Overlay>>::SendMessage: Clone,
{ {
let client = context.client::<swaync::Client>(); let client = context.try_client::<swaync::Client>()?;
{ {
let client = client.clone(); let client = client.clone();

View file

@ -67,7 +67,7 @@ impl Module<MenuBar> for TrayModule {
) -> Result<()> { ) -> Result<()> {
let tx = context.tx.clone(); let tx = context.tx.clone();
let client = context.client::<tray::Client>(); let client = context.try_client::<tray::Client>()?;
let mut tray_rx = client.subscribe(); let mut tray_rx = client.subscribe();
let initial_items = lock!(client.items()).clone(); let initial_items = lock!(client.items()).clone();

View file

@ -153,7 +153,7 @@ impl Module<gtk::Box> for WorkspacesModule {
mut rx: Receiver<Self::ReceiveMessage>, mut rx: Receiver<Self::ReceiveMessage>,
) -> Result<()> { ) -> Result<()> {
let tx = context.tx.clone(); let tx = context.tx.clone();
let client = context.ironbar.clients.borrow_mut().workspaces(); let client = context.ironbar.clients.borrow_mut().workspaces()?;
// Subscribe & send events // Subscribe & send events
spawn(async move { spawn(async move {
let mut srx = client.subscribe_workspace_change(); let mut srx = client.subscribe_workspace_change();
@ -166,7 +166,7 @@ impl Module<gtk::Box> for WorkspacesModule {
} }
}); });
let client = context.client::<dyn WorkspaceClient>(); let client = context.try_client::<dyn WorkspaceClient>()?;
// Change workspace focus // Change workspace focus
spawn(async move { spawn(async move {