use std::{error, fmt}; use axum::{ http::StatusCode, response::{IntoResponse, Response}, }; use crate::id::Id as BaseId; // I'm making an effort to avoid `anyhow` here, as that crate is _enormously_ // complex (though very usable). We don't need to be overly careful about // allocations on errors in this app, so this is fine for most "general // failure" cases. type BoxedError = Box; // Returns a 500 Internal Server Error to the client. Meant to be used via the // `?` operator; _does not_ return the originating error to the client. #[derive(Debug)] pub struct Internal(Id, BoxedError); impl From for Internal where E: Into, { fn from(error: E) -> Self { let id = Id::generate(); Self(id, error.into()) } } impl fmt::Display for Internal { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let Self(id, _) = self; writeln!(f, "internal server error")?; writeln!(f, "error id: {id}")?; Ok(()) } } impl IntoResponse for Internal { fn into_response(self) -> Response { let Self(id, error) = &self; eprintln!("hi: [{id}] {error}"); (StatusCode::INTERNAL_SERVER_ERROR, self.to_string()).into_response() } } // Transient identifier for an InternalError. Prefixed with `E`. #[derive(Debug)] pub struct Id(BaseId); impl From for Id { fn from(id: BaseId) -> Self { Self(id) } } impl Id { pub fn generate() -> Self { BaseId::generate("E") } } impl fmt::Display for Id { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) } } pub struct Unauthorized; impl IntoResponse for Unauthorized { fn into_response(self) -> Response { (StatusCode::UNAUTHORIZED, "unauthorized").into_response() } } pub struct NotFound(pub E); impl IntoResponse for NotFound where E: std::error::Error, { fn into_response(self) -> Response { let Self(response) = self; (StatusCode::NOT_FOUND, response.to_string()).into_response() } }