error.rs 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. use std::io;
  2. use actix_web::{HttpResponse, ResponseError, http::StatusCode};
  3. use serde::Serialize;
  4. use thiserror::Error;
  5. pub type AppResult<T> = Result<T, AppError>;
  6. #[derive(Debug, Error)]
  7. pub enum AppError {
  8. #[error("I/O error: {0}")]
  9. Io(#[from] io::Error),
  10. #[error("database error: {0}")]
  11. Db(#[from] rusqlite::Error),
  12. #[error("database pool error: {0}")]
  13. Pool(String),
  14. #[error("configuration parse error: {0}")]
  15. Config(toml::de::Error),
  16. #[error("validation error: {0}")]
  17. Validation(String),
  18. #[error("conflict: {0}")]
  19. Conflict(String),
  20. #[error("not found: {0}")]
  21. NotFound(String),
  22. #[error("unauthorized: {0}")]
  23. Unauthorized(String),
  24. #[error("forbidden: {0}")]
  25. Forbidden(String),
  26. #[error("rate limited")]
  27. RateLimited,
  28. #[error("git error: {0}")]
  29. Git(String),
  30. }
  31. #[derive(Serialize)]
  32. struct ErrorBody {
  33. code: &'static str,
  34. message: String,
  35. status: u16,
  36. }
  37. impl ResponseError for AppError {
  38. fn status_code(&self) -> StatusCode {
  39. match self {
  40. AppError::Validation(_) => StatusCode::BAD_REQUEST,
  41. AppError::Conflict(_) => StatusCode::CONFLICT,
  42. AppError::NotFound(_) => StatusCode::NOT_FOUND,
  43. AppError::Unauthorized(_) => StatusCode::UNAUTHORIZED,
  44. AppError::Forbidden(_) => StatusCode::FORBIDDEN,
  45. AppError::RateLimited => StatusCode::TOO_MANY_REQUESTS,
  46. AppError::Io(_) | AppError::Db(_) | AppError::Pool(_) | AppError::Config(_) | AppError::Git(_) => {
  47. StatusCode::INTERNAL_SERVER_ERROR
  48. }
  49. }
  50. }
  51. fn error_response(&self) -> HttpResponse {
  52. HttpResponse::build(self.status_code()).json(ErrorBody {
  53. code: self.code(),
  54. message: self.public_message(),
  55. status: self.status_code().as_u16(),
  56. })
  57. }
  58. }
  59. impl AppError {
  60. fn code(&self) -> &'static str {
  61. match self {
  62. AppError::Validation(_) => "validation_error",
  63. AppError::Conflict(_) => "conflict",
  64. AppError::NotFound(_) => "not_found",
  65. AppError::Unauthorized(_) => "unauthorized",
  66. AppError::Forbidden(_) => "forbidden",
  67. AppError::RateLimited => "rate_limited",
  68. AppError::Io(_) | AppError::Db(_) | AppError::Pool(_) | AppError::Config(_) | AppError::Git(_) => {
  69. "internal_error"
  70. }
  71. }
  72. }
  73. fn public_message(&self) -> String {
  74. match self {
  75. AppError::Validation(message)
  76. | AppError::Conflict(message)
  77. | AppError::NotFound(message)
  78. | AppError::Unauthorized(message)
  79. | AppError::Forbidden(message) => message.clone(),
  80. AppError::RateLimited => "too many requests".to_string(),
  81. AppError::Io(_) | AppError::Db(_) | AppError::Pool(_) | AppError::Config(_) | AppError::Git(_) => {
  82. "internal server error".to_string()
  83. }
  84. }
  85. }
  86. }