wayk_proto 0.2.6

WaykNow packet encoder-decoder and sequencing utilities
Documentation
use crate::{
    message::{ChannelName, MessageType},
    sharee::ShareeState,
    sm::ConnectionState,
};
use core::{fmt, num::TryFromIntError};

macro_rules! error_chain {
    ($error_ty:ident, $error_kind_ty:ident) => {
        #[derive(Debug)]
        pub struct $error_ty {
            pub kind: $error_kind_ty,
            pub description: Option<std::borrow::Cow<'static, str>>,
            pub source: Option<Box<$error_ty>>,
        }

        impl $error_ty {
            pub fn new<T>(kind: $error_kind_ty) -> core::result::Result<T, $error_ty> {
                Err(Self::from(kind))
            }

            pub fn into_source(self) -> Option<Self> {
                self.source.map(|boxed| *boxed)
            }

            pub fn print_trace(&self) {
                print!("–– Error trace: ");
                self.__print_trace();
                println!("");
            }

            fn __print_trace(&self) {
                print!("{}", self.kind);

                if let Some(desc) = &self.description {
                    print!(" [description: {}]", desc);
                }

                if let Some(source) = &self.source {
                    print!("\n\t↳ source: ");
                    source.__print_trace();
                }
            }
        }

        impl core::fmt::Display for $error_ty {
            fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
                write!(f, "{}", self.kind)?;

                if let Some(desc) = &self.description {
                    write!(f, " [description: {}]", desc)?;
                }

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

                Ok(())
            }
        }

        paste::item! {
            pub trait [<$error_ty ResultExt>]<T>
            where
                Self: core::marker::Sized,
            {
                #[allow(unused_variables)]
                fn or_else_desc<F, S>(self, f: F) -> core::result::Result<T, $error_ty>
                where
                    F: FnOnce() -> S,
                    S: Into<std::borrow::Cow<'static, str>>,
                {
                    unimplemented!()
                }

                #[allow(unused_variables)]
                fn or_desc<S>(self, desc: S) -> core::result::Result<T, $error_ty>
                where
                    S: Into<std::borrow::Cow<'static, str>>,
                {
                    unimplemented!()
                }

                #[allow(unused_variables)]
                fn source(self, src: $error_ty) -> core::result::Result<T, $error_ty> {
                    unimplemented!()
                }

                fn chain(self, kind: $error_kind_ty) -> core::result::Result<T, $error_ty>;
            }

            impl<T> [<$error_ty ResultExt>]<T> for core::result::Result<T, $error_ty> {
                fn or_else_desc<F, S>(self, f: F) -> core::result::Result<T, $error_ty>
                where
                    F: FnOnce() -> S,
                    S: Into<std::borrow::Cow<'static, str>>,
                {
                    match self {
                        Err(_) => self.or_desc(f()),
                        Ok(_) => self,
                    }
                }

                fn or_desc<S>(self, desc: S) -> core::result::Result<T, $error_ty>
                where
                    S: Into<std::borrow::Cow<'static, str>>,
                {
                    self.map_err(|err| {
                        let new_desc = if let Some(current_desc) = err.description {
                            format!("{} [{}]", desc.into(), current_desc).into()
                        } else {
                            desc.into()
                        };

                        $error_ty {
                            description: Some(new_desc),
                            ..err
                        }
                    })
                }

                fn source(self, src: $error_ty) -> core::result::Result<T, $error_ty> {
                    self.map_err(|err| $error_ty {
                        source: Some(Box::new(src)),
                        ..err
                    })
                }

                fn chain(self, kind: $error_kind_ty) -> core::result::Result<T, $error_ty> {
                    self.map_err(|err| $error_ty {
                        kind,
                        description: None,
                        source: Some(Box::new(err)),
                    })
                }
            }

            impl<T> [<$error_ty ResultExt>]<T> for Option<T> {
                fn chain(self, kind: $error_kind_ty) -> core::result::Result<T, $error_ty> {
                    self.ok_or_else(|| $error_ty::from(kind))
                }
            }
        }

        impl From<$error_kind_ty> for $error_ty {
            fn from(kind: $error_kind_ty) -> Self {
                Self {
                    kind,
                    description: None,
                    source: None,
                }
            }
        }
    };
}

pub type Result<T> = std::result::Result<T, ProtoError>;

error_chain! { ProtoError, ProtoErrorKind }

sa::assert_impl_all!(ProtoError: Sync, Send);

impl From<std::io::Error> for ProtoError {
    fn from(e: std::io::Error) -> Self {
        Self::from(ProtoErrorKind::Io(e))
    }
}

impl From<std::string::FromUtf8Error> for ProtoError {
    fn from(e: std::string::FromUtf8Error) -> Self {
        Self::from(ProtoErrorKind::FromUtf8(e))
    }
}

impl From<std::num::TryFromIntError> for ProtoError {
    fn from(e: std::num::TryFromIntError) -> Self {
        Self::from(ProtoErrorKind::IntConversion(e))
    }
}

#[derive(Debug)]
pub enum ProtoErrorKind {
    Decoding(&'static str),
    Encoding(&'static str),
    ConnectionSequence(ConnectionState),
    VirtualChannel(ChannelName),
    ChannelsManager,
    UnexpectedMessage(MessageType),
    Sharee(ShareeState),
    Io(std::io::Error),
    FromUtf8(std::string::FromUtf8Error),
    IntConversion(TryFromIntError),
}

impl fmt::Display for ProtoErrorKind {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            ProtoErrorKind::Decoding(desc) => write!(f, "couldn't decode {}", desc),
            ProtoErrorKind::Encoding(desc) => write!(f, "couldn't encode {}", desc),
            ProtoErrorKind::ConnectionSequence(state) => write!(f, "connection sequence failed at state {:?}", state),
            ProtoErrorKind::VirtualChannel(name) => write!(f, "virtual channel {:?} failed", name),
            ProtoErrorKind::ChannelsManager => write!(f, "virtual channels manager failed"),
            ProtoErrorKind::UnexpectedMessage(packet) => write!(f, "unexpected {:?} message", packet),
            ProtoErrorKind::Sharee(state) => write!(f, "sharee error in state {:?}", state),
            ProtoErrorKind::Io(e) => write!(f, "io error: {}", e),
            ProtoErrorKind::FromUtf8(e) => write!(f, "couldn't parse utf8 string: {}", e),
            ProtoErrorKind::IntConversion(e) => write!(f, "integer conversion failed: {}", e),
        }
    }
}