Skip to content

Commit

Permalink
Simplify some error handling and make it more structured
Browse files Browse the repository at this point in the history
  • Loading branch information
MaxVerevkin committed Sep 20, 2023
1 parent 40bc766 commit 0f98a08
Show file tree
Hide file tree
Showing 12 changed files with 155 additions and 231 deletions.
9 changes: 5 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ signal-hook = "0.3"
signal-hook-tokio = { version = "0.3", features = ["futures-v0_3"] }
smart-default = "0.7"
swayipc-async = "2.0"
thiserror = "1.0"
toml = "0.8"
unicode-segmentation = "1.10.1"
wayrs-client = { version = "0.12", features = ["tokio"] }
Expand Down
35 changes: 20 additions & 15 deletions src/blocks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@

mod prelude;

use crate::BoxedFuture;
use futures::future::FutureExt;
use futures::stream::FuturesUnordered;
use serde::de::{self, Deserialize};
use tokio::sync::{mpsc, Notify};

Expand All @@ -41,7 +41,7 @@ use std::time::Duration;
use crate::click::MouseButton;
use crate::errors::*;
use crate::widget::Widget;
use crate::{Request, RequestCmd};
use crate::{BoxedFuture, Request, RequestCmd};

macro_rules! define_blocks {
{
Expand Down Expand Up @@ -74,29 +74,27 @@ macro_rules! define_blocks {
}
}

pub fn run(self, api: CommonApi) -> BlockFuture {
let id = api.id;
pub fn spawn(self, api: CommonApi, futures: &mut FuturesUnordered<BoxedFuture<()>>) {
match self {
$(
$(#[cfg(feature = $feat)])?
Self::$block(config) => async move {
Self::$block(config) => futures.push(async move {
while let Err(err) = $block::run(&config, &api).await {
api.set_error(err)?;
if api.set_error(err).is_err() {
return;
}
tokio::select! {
_ = tokio::time::sleep(api.error_interval) => (),
_ = api.wait_for_update_request() => (),
}
}
Ok(())
}.boxed_local(),
}.boxed_local()),
)*
Self::Err(name, err) => {
std::future::ready(Err(Error {
kind: ErrorKind::Config,
message: None,
Self::Err(_name, err) => {
let _ = api.set_error(Error {
message: Some("Configuration error".into()),
cause: Some(Arc::new(err)),
block: Some((name, id)),
})).boxed_local()
});
},
}
}
Expand Down Expand Up @@ -183,7 +181,14 @@ define_blocks!(
xrandr,
);

pub type BlockFuture = BoxedFuture<Result<()>>;
/// An error which originates from a block
#[derive(Debug, thiserror::Error)]
#[error("In block {}: {}", .block_name, .error)]
pub struct BlockError {
pub block_id: usize,
pub block_name: &'static str,
pub error: Error,
}

pub type BlockAction = Cow<'static, str>;

Expand Down
2 changes: 0 additions & 2 deletions src/blocks/backlight.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,10 +175,8 @@ pub async fn run(config: &Config, api: &CommonApi) -> Result<()> {
}
Some(e) => {
api.set_error(Error {
kind: ErrorKind::Other,
message: None,
cause: Some(Arc::new(e)),
block: None,
})?;
}
None => {
Expand Down
2 changes: 0 additions & 2 deletions src/blocks/weather.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,10 +292,8 @@ async fn find_ip_location(interval: Duration) -> Result<Coordinates> {

let location = if response.error {
return Err(Error {
kind: ErrorKind::Other,
message: Some("ipapi.co error".into()),
cause: Some(Arc::new(response.reason)),
block: None,
});
} else {
response
Expand Down
2 changes: 1 addition & 1 deletion src/click.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::fmt;
use serde::de::{self, Deserializer, Visitor};
use serde::Deserialize;

use crate::errors::{Result, ResultExt};
use crate::errors::{ErrorContext, Result};
use crate::protocol::i3bar_event::I3BarEvent;
use crate::subprocess::{spawn_shell, spawn_shell_sync};

Expand Down
124 changes: 7 additions & 117 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,172 +12,62 @@ type ErrorMsg = Cow<'static, str>;
/// Error type
#[derive(Debug, Clone)]
pub struct Error {
pub kind: ErrorKind,
pub message: Option<ErrorMsg>,
pub cause: Option<Arc<dyn StdError + Send + Sync + 'static>>,
pub block: Option<(&'static str, usize)>,
}

/// A set of errors that can occur during the runtime
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ErrorKind {
Config,
Format,
Other,
}

impl Error {
pub fn new<T: Into<ErrorMsg>>(message: T) -> Self {
Self {
kind: ErrorKind::Other,
message: Some(message.into()),
cause: None,
block: None,
}
}

pub fn new_format<T: Into<ErrorMsg>>(message: T) -> Self {
Self {
kind: ErrorKind::Format,
message: Some(message.into()),
cause: None,
block: None,
}
}
}

pub trait InBlock {
fn in_block(self, block: &'static str, block_id: usize) -> Self;
}

impl InBlock for Error {
fn in_block(mut self, block: &'static str, block_id: usize) -> Self {
self.block = Some((block, block_id));
self
}
}

impl<T> InBlock for Result<T> {
fn in_block(self, block: &'static str, block_id: usize) -> Self {
self.map_err(|e| e.in_block(block, block_id))
}
}

pub trait ResultExt<T> {
pub trait ErrorContext<T> {
fn error<M: Into<ErrorMsg>>(self, message: M) -> Result<T>;
fn or_error<M: Into<ErrorMsg>, F: FnOnce() -> M>(self, f: F) -> Result<T>;
fn config_error(self) -> Result<T>;
fn format_error<M: Into<ErrorMsg>>(self, message: M) -> Result<T>;
}

impl<T, E: StdError + Send + Sync + 'static> ResultExt<T> for Result<T, E> {
impl<T, E: StdError + Send + Sync + 'static> ErrorContext<T> for Result<T, E> {
fn error<M: Into<ErrorMsg>>(self, message: M) -> Result<T> {
self.map_err(|e| Error {
kind: ErrorKind::Other,
message: Some(message.into()),
cause: Some(Arc::new(e)),
block: None,
})
}

fn or_error<M: Into<ErrorMsg>, F: FnOnce() -> M>(self, f: F) -> Result<T> {
self.map_err(|e| Error {
kind: ErrorKind::Other,
message: Some(f().into()),
cause: Some(Arc::new(e)),
block: None,
})
}

fn config_error(self) -> Result<T> {
self.map_err(|e| Error {
kind: ErrorKind::Config,
message: None,
cause: Some(Arc::new(e)),
block: None,
})
}

fn format_error<M: Into<ErrorMsg>>(self, message: M) -> Result<T> {
self.map_err(|e| Error {
kind: ErrorKind::Format,
message: Some(message.into()),
cause: Some(Arc::new(e)),
block: None,
})
}
}

pub trait OptionExt<T> {
fn error<M: Into<ErrorMsg>>(self, message: M) -> Result<T>;
fn or_error<M: Into<ErrorMsg>, F: FnOnce() -> M>(self, f: F) -> Result<T>;
fn config_error(self) -> Result<T>;
fn or_format_error<M: Into<ErrorMsg>, F: FnOnce() -> M>(self, f: F) -> Result<T>;
}

impl<T> OptionExt<T> for Option<T> {
impl<T> ErrorContext<T> for Option<T> {
fn error<M: Into<ErrorMsg>>(self, message: M) -> Result<T> {
self.ok_or_else(|| Error {
kind: ErrorKind::Other,
message: Some(message.into()),
cause: None,
block: None,
})
}

fn or_error<M: Into<ErrorMsg>, F: FnOnce() -> M>(self, f: F) -> Result<T> {
self.ok_or_else(|| Error {
kind: ErrorKind::Other,
message: Some(f().into()),
cause: None,
block: None,
})
}

fn config_error(self) -> Result<T> {
self.ok_or(Error {
kind: ErrorKind::Config,
message: None,
cause: None,
block: None,
})
}

fn or_format_error<M: Into<ErrorMsg>, F: FnOnce() -> M>(self, f: F) -> Result<T> {
self.ok_or_else(|| Error {
kind: ErrorKind::Format,
message: Some(f().into()),
cause: None,
block: None,
})
}
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.block {
Some(block) => {
match self.kind {
ErrorKind::Config | ErrorKind::Format => f.write_str("Configuration error")?,
ErrorKind::Other => f.write_str("Error")?,
}

write!(f, " in {}", block.0)?;

if let Some(message) = &self.message {
write!(f, ": {message}")?;
}

if let Some(cause) = &self.cause {
write!(f, ". (Cause: {cause})")?;
}
}
None => {
f.write_str(self.message.as_deref().unwrap_or("Error"))?;
if let Some(cause) = &self.cause {
write!(f, ". (Cause: {cause})")?;
}
}
f.write_str(self.message.as_deref().unwrap_or("Error"))?;

if let Some(cause) = &self.cause {
write!(f, ". Cause: {cause}")?;
}

Ok(())
Expand Down
10 changes: 10 additions & 0 deletions src/formatting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,16 @@ use value::Value;

pub type Values = HashMap<Cow<'static, str>, Value>;

#[derive(Debug, thiserror::Error)]
pub enum FormatError {
#[error("Placeholder '{0}' not found")]
PlaceholderNotFound(String),
#[error("{} cannot be formatted with '{}' formatter", .ty, .fmt)]
IncompatibleFormatter { ty: &'static str, fmt: &'static str },
#[error(transparent)]
Other(#[from] Error),
}

#[derive(Debug, Clone)]
pub struct Format {
full: Arc<FormatTemplate>,
Expand Down
Loading

0 comments on commit 0f98a08

Please sign in to comment.