error.rs (2639B)
1 use axum::http::StatusCode; 2 use axum::response::{IntoResponse, Response}; 3 4 /// Errors from individual volume HTTP requests — used for logging, not HTTP responses. 5 #[derive(Debug)] 6 pub enum VolumeError { 7 Request { 8 url: String, 9 source: reqwest::Error, 10 }, 11 BadStatus { 12 url: String, 13 status: reqwest::StatusCode, 14 }, 15 } 16 17 impl std::fmt::Display for VolumeError { 18 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 19 match self { 20 VolumeError::Request { url, source } => { 21 write!(f, "volume request to {url} failed: {source}") 22 } 23 VolumeError::BadStatus { url, status } => { 24 write!(f, "volume {url} returned status {status}") 25 } 26 } 27 } 28 } 29 30 /// Application-level errors that map to HTTP responses. 31 #[derive(Debug)] 32 pub enum AppError { 33 NotFound, 34 CorruptRecord { key: String }, 35 Db(rusqlite::Error), 36 InsufficientVolumes { need: usize, have: usize }, 37 PartialWrite, 38 AllVolumesUnreachable, 39 } 40 41 impl From<rusqlite::Error> for AppError { 42 fn from(e: rusqlite::Error) -> Self { 43 match e { 44 rusqlite::Error::QueryReturnedNoRows => AppError::NotFound, 45 other => AppError::Db(other), 46 } 47 } 48 } 49 50 impl std::fmt::Display for AppError { 51 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 52 match self { 53 AppError::NotFound => write!(f, "not found"), 54 AppError::CorruptRecord { key } => { 55 write!(f, "corrupt record for key {key}: no volumes") 56 } 57 AppError::Db(e) => write!(f, "database error: {e}"), 58 AppError::InsufficientVolumes { need, have } => { 59 write!(f, "need {need} volumes but only {have} available") 60 } 61 AppError::PartialWrite => write!(f, "not all volume writes succeeded"), 62 AppError::AllVolumesUnreachable => write!(f, "all volume replicas are unreachable"), 63 } 64 } 65 } 66 67 impl IntoResponse for AppError { 68 fn into_response(self) -> Response { 69 let status = match &self { 70 AppError::NotFound => StatusCode::NOT_FOUND, 71 AppError::CorruptRecord { .. } => StatusCode::INTERNAL_SERVER_ERROR, 72 AppError::Db(_) => StatusCode::INTERNAL_SERVER_ERROR, 73 AppError::InsufficientVolumes { .. } => StatusCode::SERVICE_UNAVAILABLE, 74 AppError::PartialWrite => StatusCode::BAD_GATEWAY, 75 AppError::AllVolumesUnreachable => StatusCode::BAD_GATEWAY, 76 }; 77 (status, self.to_string()).into_response() 78 } 79 }