blatherskite

a toy discord-like chat app backend written for a swe class
Log | Files | Refs | README

error.rs (4266B)


      1 use poem::http::StatusCode;
      2 use diesel::result::{QueryResult, Error};
      3 
      4 #[derive(Debug, Clone)]
      5 pub struct UserFacingPropagatedError {
      6 	code: StatusCode,
      7 	backtrace: Option<String>,
      8 	error_kind: String,
      9 	error: String,
     10 	context: Option<String>,
     11 }
     12 
     13 impl std::fmt::Display for UserFacingPropagatedError {
     14 	fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
     15 		writeln!(f, "<h1>{}</h1>", self.code)?;
     16 		writeln!(f, "Received <code>{}: {}</code>", self.error_kind, self.error)?;
     17 		if let Some(ctx) = &self.context {
     18 			writeln!(f, " while {}.", ctx)?;
     19 		}
     20 		if let Some(trace) = &self.backtrace {
     21 			writeln!(f, "<pre>{}</pre>", html_escape::encode_text(&trace))?;
     22 		}
     23 		Ok(())
     24 	}
     25 }
     26 
     27 impl std::error::Error for UserFacingPropagatedError {}
     28 
     29 #[derive(Debug, Clone)]
     30 pub struct UserFacingError {
     31 	code: StatusCode,
     32 	reason: Option<String>,
     33 }
     34 
     35 impl UserFacingError {
     36 	pub fn new(code: StatusCode, reason: &str) -> Self {
     37 		Self {
     38 			code,
     39 			reason: Some(String::from(reason))
     40 		}
     41 	}
     42 	pub fn terse(code: StatusCode) -> Self {
     43 		Self {
     44 			code,
     45 			reason: None,
     46 		}
     47 	}
     48 }
     49 
     50 impl Into<poem::Error> for UserFacingError {
     51 	fn into(self) -> poem::Error {
     52 		let code = self.code;
     53 		poem::Error::new(self, code)
     54 	}
     55 }
     56 
     57 impl std::fmt::Display for UserFacingError {
     58 	fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
     59 		write!(f, "<h1>{}</h1>", self.code)?;
     60 		if let Some(reason) = &self.reason {
     61 			write!(f, "{}", reason)?;
     62 		}
     63 		writeln!(f, "")
     64 	}
     65 }
     66 
     67 impl std::error::Error for UserFacingError {}
     68 
     69 #[derive(Debug)]
     70 pub struct WithBacktrace<E>
     71 where E: std::error::Error
     72 {
     73 	backtrace: std::backtrace::Backtrace,
     74 	pub err: E,
     75 	pub context: Option<String>,
     76 }
     77 
     78 impl<E> std::fmt::Display for WithBacktrace<E>
     79 	where E: std::error::Error
     80 {
     81 	fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
     82 		write!(f, "{}", self.err)
     83 	}
     84 }
     85 
     86 impl<E> std::error::Error for WithBacktrace<E>
     87 where E: std::error::Error  {}
     88 
     89 pub trait WithBacktraceExt<E> where E: std::error::Error  {
     90 	type Success;
     91 	fn with_backtrace(self) -> Result<Self::Success, WithBacktrace<E>>;
     92 }
     93 
     94 impl<T,E> WithBacktraceExt<E> for Result<T, E>
     95 where E: std::error::Error
     96 {
     97 	type Success = T;
     98 	fn with_backtrace(self) -> Result<Self::Success, WithBacktrace<E>> {
     99 		self.map_err(|err| WithBacktrace {
    100 			err,
    101 			backtrace: std::backtrace::Backtrace::capture(),
    102 			context: None
    103 		})
    104 	}
    105 }
    106 
    107 pub trait InternalResultExt {
    108 	type Success;
    109 	fn poemify(self, ctx: &str) -> poem::Result<Self::Success>;
    110 }
    111 
    112 
    113 #[derive(Debug)]
    114 pub struct StdAnyhowError(pub anyhow::Error);
    115 
    116 impl std::fmt::Display for StdAnyhowError {
    117 	fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
    118 		std::fmt::Display::fmt(&self.0, f)
    119 	}
    120 }
    121 
    122 impl std::error::Error for StdAnyhowError {
    123 	fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
    124 		Some(self.0.as_ref())
    125 	}
    126 }
    127 
    128 impl From<anyhow::Error> for StdAnyhowError {
    129 	fn from(err: anyhow::Error) -> Self {
    130 		Self(err)
    131 	}
    132 }
    133 
    134 impl<T> InternalResultExt for QueryResult<T> {
    135 	type Success = T;
    136 
    137 	fn poemify(self, ctx: &str) -> poem::Result<Self::Success> {
    138 		self.map_err(|err| {
    139 			log::error!("{:#?}", err);
    140 			let code = match err {
    141 				diesel::result::Error::NotFound => StatusCode::NOT_FOUND,
    142 				_ => StatusCode::INTERNAL_SERVER_ERROR,
    143 			};
    144 			poem::error::Error::new(UserFacingPropagatedError {
    145 				code,
    146 				backtrace: None,
    147 				context: Some(String::from(ctx)),
    148 				error_kind: format!("{:?}", err),
    149 				error: err.to_string(),
    150 			}, code)
    151 		})
    152 	}
    153 }
    154 
    155 
    156 impl<T> InternalResultExt for anyhow::Result<T> {
    157 	type Success = T;
    158 
    159 	fn poemify(self, ctx: &str) -> poem::Result<Self::Success> {
    160 		self.map_err(|err| {
    161 			log::error!("{:#?}", err);
    162 			poem::error::InternalServerError(StdAnyhowError::from(err))
    163 		})
    164 	}
    165 
    166 }
    167 
    168 impl<T, E> InternalResultExt for std::result::Result<T, WithBacktrace<E>>
    169 	where E: std::error::Error
    170 {
    171 	type Success = T;
    172 
    173 	fn poemify(self, ctx: &str) -> poem::Result<Self::Success> {
    174 		self.map_err(|err| {
    175 			log::error!("{:#?}", err.err);
    176 			poem::error::InternalServerError( UserFacingPropagatedError {
    177 				code: StatusCode::INTERNAL_SERVER_ERROR,
    178 				backtrace: Some(err.backtrace.to_string()),
    179 				context: Some(String::from(ctx)),
    180 				error_kind: String::from(std::any::type_name::<E>()),
    181 				error: err.to_string(),
    182 			})
    183 		})
    184 	}
    185 
    186 }