1use std::fmt;
4
5pub type Result<T> = std::result::Result<T, Error>;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct FieldError {
10 pub field: String,
11 pub message: String,
12}
13
14#[derive(Debug)]
16#[non_exhaustive]
17pub enum Error {
18 BadRequest(String),
20 Unauthorized(String),
22 Forbidden(String),
24 NotFound(String),
26 Internal(String),
28 Validation(Vec<FieldError>),
32 Custom(u16, String),
34}
35
36impl Error {
37 pub fn status_code(&self) -> u16 {
38 match self {
39 Error::BadRequest(_) => 400,
40 Error::Unauthorized(_) => 401,
41 Error::Forbidden(_) => 403,
42 Error::NotFound(_) => 404,
43 Error::Internal(_) => 500,
44 Error::Validation(_) => 422,
45 Error::Custom(code, _) => *code,
46 }
47 }
48
49 pub fn status(&self) -> http::StatusCode {
51 http::StatusCode::from_u16(self.status_code())
52 .unwrap_or(http::StatusCode::INTERNAL_SERVER_ERROR)
53 }
54
55 pub fn message(&self) -> &str {
56 match self {
57 Error::Validation(_) => "Validation failed",
58 Error::BadRequest(msg)
59 | Error::Unauthorized(msg)
60 | Error::Forbidden(msg)
61 | Error::NotFound(msg)
62 | Error::Internal(msg)
63 | Error::Custom(_, msg) => msg,
64 }
65 }
66}
67
68impl fmt::Display for Error {
69 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70 write!(f, "{}", self.message())
71 }
72}
73
74impl std::error::Error for Error {}
75
76impl Error {
78 pub fn bad_request(msg: impl Into<String>) -> Self {
79 Error::BadRequest(msg.into())
80 }
81
82 pub fn unauthorized(msg: impl Into<String>) -> Self {
83 Error::Unauthorized(msg.into())
84 }
85
86 pub fn forbidden(msg: impl Into<String>) -> Self {
87 Error::Forbidden(msg.into())
88 }
89
90 pub fn not_found(msg: impl Into<String>) -> Self {
91 Error::NotFound(msg.into())
92 }
93
94 pub fn internal(msg: impl Into<String>) -> Self {
95 Error::Internal(msg.into())
96 }
97
98 pub fn validation(fields: Vec<FieldError>) -> Self {
100 Error::Validation(fields)
101 }
102
103 pub fn fields(&self) -> &[FieldError] {
105 match self {
106 Error::Validation(fields) => fields,
107 _ => &[],
108 }
109 }
110
111 pub fn custom(status: u16, msg: impl Into<String>) -> Self {
112 Error::Custom(status, msg.into())
113 }
114}