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 }