use crate::RecvError;
use mesh_protobuf::EncodeAs;
use mesh_protobuf::Protobuf;
use std::fmt;
use std::fmt::Display;
use thiserror::Error;
#[derive(Protobuf)]
pub struct RemoteError(EncodeAs<BoxedError, EncodedError>);
type BoxedError = Box<dyn std::error::Error + Send + Sync>;
impl RemoteError {
pub fn new<T: Into<BoxedError>>(error: T) -> Self {
Self(error.into().into())
}
}
impl Display for RemoteError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&self.0, f)
}
}
impl fmt::Debug for RemoteError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}
impl std::error::Error for RemoteError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.0.source()
}
}
#[derive(Protobuf)]
struct EncodedError {
errors: Vec<String>,
}
impl From<EncodedError> for BoxedError {
fn from(error: EncodedError) -> Self {
Box::new(DecodedError::from(error))
}
}
impl From<BoxedError> for EncodedError {
fn from(error: BoxedError) -> Self {
let mut errors = Vec::new();
let mut error = error.as_ref() as &dyn std::error::Error;
loop {
errors.push(error.to_string());
if let Some(source) = error.source() {
error = source;
} else {
break;
}
}
Self { errors }
}
}
#[derive(Debug)]
struct DecodedError {
source: Option<Box<DecodedError>>,
error: String,
}
impl From<EncodedError> for DecodedError {
fn from(value: EncodedError) -> Self {
let mut errors = value.errors;
let last_error = errors.pop().unwrap_or("no error information".to_string());
let mut decoded = DecodedError {
source: None,
error: last_error,
};
for error in errors.into_iter().rev() {
decoded = DecodedError {
source: Some(Box::new(decoded)),
error,
};
}
decoded
}
}
impl Display for DecodedError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.error)
}
}
impl std::error::Error for DecodedError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.source.as_ref().map(|s| s as _)
}
}
pub type RemoteResult<T> = Result<T, RemoteError>;
#[derive(Debug, Error)]
pub enum RpcError<E = RemoteError> {
#[error(transparent)]
Call(E),
#[error(transparent)]
Channel(RecvError),
}
pub trait RemoteResultExt {
type Flattened;
fn flatten(self) -> Self::Flattened;
}
impl<T, E> RemoteResultExt for Result<Result<T, E>, RecvError> {
type Flattened = Result<T, RpcError<E>>;
fn flatten(self) -> Self::Flattened {
match self {
Ok(Ok(t)) => Ok(t),
Ok(Err(e)) => Err(RpcError::Call(e)),
Err(e) => Err(RpcError::Channel(e)),
}
}
}