use crate::{rendered, State}; use actix_web::{http::StatusCode, HttpResponse, ResponseError}; pub(crate) trait ResultExt { fn state(self, state: &State) -> Result; } pub(crate) trait OptionExt { fn req(self) -> Result; } #[derive(Debug, thiserror::Error)] #[error("{error}")] pub(crate) struct StateError { state: State, error: Error, } #[derive(Debug, thiserror::Error)] pub(crate) enum Error { #[error("{0}")] Accounts(#[from] hyaenidae_accounts::Error), #[error("{0}")] Profiles(#[from] hyaenidae_profiles::Error), #[error("{0}")] Render(std::io::Error), #[error("{0}")] Sled(#[from] sled::Error), #[error("{0}")] Minify(String), #[error("Required data was not present")] Required, } impl ResponseError for StateError { fn status_code(&self) -> StatusCode { match self.error { Error::Render(_) | Error::Minify(_) | Error::Accounts(_) | Error::Profiles(_) | Error::Sled(_) | Error::Required => StatusCode::INTERNAL_SERVER_ERROR, } } fn error_response(&self) -> HttpResponse { match rendered(HttpResponse::build(self.status_code()), |cursor| { crate::templates::error(cursor, self.error.to_string()) }) { Ok(res) => res, Err(_) => HttpResponse::build(self.status_code()) .content_type(mime::TEXT_PLAIN.essence_str()) .body(self.error.to_string()), } } } impl ResultExt for Result where Error: From, { fn state(self, state: &State) -> Result { self.map_err(|e| state.error(e)) } } impl OptionExt for Option { fn req(self) -> Result { self.ok_or_else(|| Error::Required) } } impl From for Error { fn from(e: minify_html::Error) -> Self { Error::Minify(format!("{:?}", e)) } } impl From for Error { fn from(e: hyaenidae_profiles::store::StoreError) -> Self { Error::Profiles(From::from(e)) } } impl From for Error { fn from(e: hyaenidae_profiles::apub::StoreError) -> Self { Error::Profiles(From::from(e)) } } impl State { pub(crate) fn error(&self, error: E) -> StateError where Error: From, { StateError { state: self.clone(), error: error.into(), } } }