use std::collections::HashMap; use std::fmt::Debug; use std::fmt::Display; use std::fmt::format; use std::string::FromUtf8Error; use std::io; use colored::Colorize; use log::{trace, debug, info, warn, error}; use crate::proto::MizeMessage; pub type MizeResult = Result; pub trait MizeResultTrait { fn critical(self) -> T; } pub trait IntoMizeResult { fn mize_result(self) -> MizeResult; fn mize_result_msg(self, msg: S) -> MizeResult where S: std::fmt::Display ; } #[derive(Debug, Clone)] pub struct MizeError { pub category: Vec, pub code: u32, pub messages: Vec, pub caused_by_msg: Option, pub code_location: Option, } #[derive(Debug, Clone)] pub struct CausedByMessage { // peer: &Connection, // TODO: add later msg: Box, } #[derive(Debug, Clone)] pub struct MizeCodeLocation { file: String, line: u32, column: u32, } impl Display for MizeCodeLocation { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "file: {} line: {} column: {}", self.file, self.line, self.column) } } impl From<&std::panic::Location <'_>> for MizeCodeLocation { fn from(panic_location: &std::panic::Location) -> MizeCodeLocation { let file = panic_location.file().to_string(); let line = panic_location.line(); let column = panic_location.column(); MizeCodeLocation {file, line, column} } } impl MizeError { #[track_caller] pub fn code(code: u32) -> MizeError { let caller_location = std::panic::Location::caller(); get_error_by_code(code, caller_location) } pub fn set_code(mut self, code: u32) -> MizeError { self.code = code; return self; } #[track_caller] pub fn new() -> MizeError { let caller_location = std::panic::Location::caller(); return MizeError { category: Vec::new(), code: 0, messages: Vec::new(), caused_by_msg: None, code_location: Some(caller_location.into()), } } pub fn msg(mut self, msg: E) -> MizeError where E: std::fmt::Display { self.messages.push(format!("{}", msg)); return self; } pub fn category(mut self, category: T) -> MizeError where T: Into { self.category.push(category.into()); return self; } fn send(self) -> MizeError{ self } fn add_to_err_item(self) -> MizeError { self } pub fn log(self) -> MizeError { error!("MizeError envountered!"); if let Some(ref location) = self.code_location { error!("[ {} ] {}", "LOCATION".yellow(), location); }; for msg in &self.messages { error!("[ {} ] {}", "MSG".yellow(), msg); } self } pub fn handle(self) -> MizeError { return self.send().add_to_err_item().log(); } pub fn critical(self){ let mut msg_iter = self.messages.iter(); match msg_iter.next() { None => { error!("{} MizeError with code: {}", "CRITICAL".red(), self.code); }, Some(msg) => { error!("{} MizeError with code: {} - {}", "CRITICAL".red(), self.code, msg); }, } for msg in msg_iter { error!("{} {}", "MSG".red(), msg) } if let Some(location) = self.code_location { error!("{} {}", "LOCATION".red(), location) } if let Some(caused_by_msg) = self.caused_by_msg { error!("{} {:?}", "CAUSED_BY_MESSAGE".red(), caused_by_msg) } panic!(); } } /* pub trait MizeResultExtension { fn extra_msg(self, msg: E) -> Result where E: std::fmt::Display ; fn handle(self) -> Result; fn is_critical(self) -> T; } impl MizeResultExtension for Result { fn msg(mut self, msg: E) -> Result where E: std::fmt::Display { match self { Err(ref mut e) => { e.msg.push(format!("{}", msg)); return self; }, Ok(something) => Ok(something), } } fn handle(self) -> Result { match self { Err(e) => { Err(e.send().add_to_err_item().write_to_stderr()) }, Ok(something) => Ok(something) } } fn is_critical(self) -> T { self.expect("Aborting because of a critical Error") } } */ /* impl From for MizeError { #[track_caller] fn from(utf_err: FromUtf8Error) -> MizeError{ let caller_location = std::panic::Location::caller(); let mut err = MizeError::code(109).msg(format!("From std::string::FromUtf8Error: {}", utf_err)); err.code_location = Some(caller_location.into()); return err; } } */ impl From for MizeError where E: std::fmt::Display { fn from(e: E) -> MizeError { MizeError::new().msg(format!("From generic Rust error: {}", e)) } } impl MizeResultTrait for MizeResult { fn critical(self) -> T { match self { Ok(val) => val, Err(err) => { err.critical(); unreachable!() }, } } } impl IntoMizeResult for Result where E: std::fmt::Display { #[track_caller] fn mize_result(self) -> MizeResult { let caller_location = std::panic::Location::caller(); match self { Ok(val) => MizeResult::Ok(val), Err(err) => MizeResult::Err(MizeError::new() .category("misc") .msg(format!("From {}: {}", std::any::type_name_of_val(&err), err))), } } #[track_caller] fn mize_result_msg(self, msg: S) -> MizeResult where S: std::fmt::Display { let caller_location = std::panic::Location::caller(); match self { Ok(val) => MizeResult::Ok(val), Err(err) => MizeResult::Err(MizeError::new() .category("misc") .msg(format!("{}", msg)) .msg(format!("From {}: {}", std::any::type_name_of_val(&err), err))), } } } /* impl IntoMizeResult for io::Result { #[track_caller] fn mize_result(self) -> MizeResult { let caller_location = std::panic::Location::caller(); match self { Ok(val) => MizeResult::Ok(val), Err(io_err) => MizeResult::Err(MizeError::new().category("io").msg(format!("From an io::Error: {}", io_err))), } } #[track_caller] fn mize_result_msg(self, msg: dyn Into<&str>) -> MizeResult { let caller_location = std::panic::Location::caller(); match self { Ok(val) => MizeResult::Ok(val), Err(io_err) => MizeResult::Err(MizeError::new().category("io").msg(msg).msg(format!("From an io::Error: {}", io_err))), } } } */ fn get_error_by_code(code: u32, caller_location: &std::panic::Location) -> MizeError { let err = MizeError { category: Vec::new(), code: 0, messages: Vec::new(), caused_by_msg: None, code_location: Some(caller_location.into()), }; match code { 109 => err.set_code(109).category("decoding").msg("conversion from utf8 failed"), 108 => err.set_code(108).category("decoding").msg("serde deserialisation failed"), _ => err, } }