1use std::fmt;
6
7pub type Result<T> = std::result::Result<T, Error>;
8
9#[derive(Debug)]
10pub enum Error {
11 BadRequest(String),
12 Unauthorized(String),
13 Forbidden(String),
14 NotFound(String),
15 MethodNotAllowed(String),
16 Conflict(String),
17 Internal(String),
18}
19
20impl Error {
21 pub fn status(&self) -> u16 {
22 match self {
23 Error::BadRequest(_) => 400,
24 Error::Unauthorized(_) => 401,
25 Error::Forbidden(_) => 403,
26 Error::NotFound(_) => 404,
27 Error::MethodNotAllowed(_) => 405,
28 Error::Conflict(_) => 409,
29 Error::Internal(_) => 500,
30 }
31 }
32
33 pub fn client_message(&self) -> &str {
36 match self {
37 Error::BadRequest(m)
38 | Error::Unauthorized(m)
39 | Error::Forbidden(m)
40 | Error::NotFound(m)
41 | Error::MethodNotAllowed(m)
42 | Error::Conflict(m) => m,
43 Error::Internal(_) => "Internal Server Error",
44 }
45 }
46}
47
48impl fmt::Display for Error {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 let label = match self {
51 Error::BadRequest(m) => format!("400 Bad Request: {m}"),
52 Error::Unauthorized(m) => format!("401 Unauthorized: {m}"),
53 Error::Forbidden(m) => format!("403 Forbidden: {m}"),
54 Error::NotFound(m) => format!("404 Not Found: {m}"),
55 Error::MethodNotAllowed(m) => format!("405 Method Not Allowed: {m}"),
56 Error::Conflict(m) => format!("409 Conflict: {m}"),
57 Error::Internal(m) => format!("500 Internal: {m}"),
58 };
59 f.write_str(&label)
60 }
61}
62
63impl std::error::Error for Error {}
64
65impl From<sqlx::Error> for Error {
66 fn from(e: sqlx::Error) -> Self {
67 match e {
68 sqlx::Error::RowNotFound => Error::NotFound("row not found".into()),
69 sqlx::Error::Database(db_err) if db_err.constraint().is_some() => {
74 let constraint = db_err.constraint().unwrap_or("?").to_string();
75 Error::Conflict(format!("constraint violation ({constraint}): {db_err}"))
76 }
77 other => Error::Internal(format!("db error: {other}")),
78 }
79 }
80}
81
82impl From<std::io::Error> for Error {
83 fn from(e: std::io::Error) -> Self {
84 Error::Internal(format!("io error: {e}"))
85 }
86}
87
88impl From<serde_json::Error> for Error {
89 fn from(e: serde_json::Error) -> Self {
90 Error::BadRequest(format!("json error: {e}"))
91 }
92}
93
94impl From<minijinja::Error> for Error {
95 fn from(e: minijinja::Error) -> Self {
96 Error::Internal(format!("template error: {e}"))
97 }
98}