use std::convert::TryFrom; use hyper::{http, Body, Response, StatusCode}; use thiserror::Error; #[derive(Error, Debug)] pub enum ServerError { #[error("unauthorized access")] Unauthorized, #[error("object not found")] NotFound, #[error("invalid query")] InvalidQuery, #[error("endpoint gone")] Gone, #[error("internal database error")] Database(#[from] rusqlite::Error), #[error("internal WireGuard error")] WireGuard, #[error("internal I/O error")] Io(#[from] std::io::Error), #[error("JSON parsing/serialization error")] Json(#[from] serde_json::Error), #[error("Generic HTTP error")] Http(#[from] http::Error), #[error("Generic Hyper error")] Hyper(#[from] hyper::Error), } impl<'a> From<&'a ServerError> for StatusCode { fn from(error: &ServerError) -> StatusCode { use ServerError::*; match error { Unauthorized => StatusCode::UNAUTHORIZED, NotFound => StatusCode::NOT_FOUND, Gone => StatusCode::GONE, InvalidQuery | Json(_) => StatusCode::BAD_REQUEST, // Special-case the constraint violation situation. Database(rusqlite::Error::SqliteFailure(libsqlite3_sys::Error { code, .. }, ..)) if *code == libsqlite3_sys::ErrorCode::ConstraintViolation => { StatusCode::BAD_REQUEST }, Database(rusqlite::Error::QueryReturnedNoRows) => StatusCode::NOT_FOUND, WireGuard | Io(_) | Database(_) | Http(_) | Hyper(_) => { StatusCode::INTERNAL_SERVER_ERROR }, } } } impl TryFrom for Response { type Error = http::Error; fn try_from(e: ServerError) -> Result { Response::builder() .status(StatusCode::from(&e)) .body(Body::empty()) } }