mkv

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

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 }