1use axum::{
4 extract::rejection::JsonRejection,
5 http::{
6 header::{InvalidHeaderName, InvalidHeaderValue},
7 method::InvalidMethod,
8 StatusCode,
9 },
10};
11use lettre::{address::AddressError, transport::smtp};
12
13use crate::{controller::ErrorDetail, depcheck, validation::ModelValidationErrors};
14
15impl From<serde_json::Error> for Error {
25 fn from(val: serde_json::Error) -> Self {
26 Self::JSON(val).bt()
27 }
28}
29
30#[derive(thiserror::Error, Debug)]
51#[non_exhaustive]
52pub enum Error {
53 #[error("{0}")]
55 Message(String),
56
57 #[error("{0}")]
59 Unauthorized(String),
60
61 #[error("not found")]
63 NotFound,
64
65 #[error("{0}")]
66 BadRequest(String),
67
68 #[error("")]
69 CustomError(StatusCode, ErrorDetail),
70
71 #[error("internal server error")]
72 InternalServerError,
73
74 #[error(transparent)]
75 JsonRejection(#[from] JsonRejection),
76
77 #[error(transparent)]
78 AxumFormRejection(#[from] axum::extract::rejection::FormRejection),
79
80 #[error(transparent)]
81 Validation(#[from] ModelValidationErrors),
82
83 #[cfg(feature = "with-db")]
84 #[error(transparent)]
86 Model(#[from] crate::model::ModelError),
87
88 #[error("{inner}\n{backtrace}")]
90 WithBacktrace {
91 inner: Box<Self>,
92 backtrace: Box<std::backtrace::Backtrace>,
93 },
94
95 #[error(
96 "error while running worker: no queue provider populated in context. Did you configure \
97 BackgroundQueue and connection details in `queue` in your config file?"
98 )]
99 QueueProviderMissing,
100
101 #[error("task not found: '{0}'")]
102 TaskNotFound(String),
103
104 #[error(transparent)]
105 Scheduler(#[from] crate::scheduler::Error),
106
107 #[error(transparent)]
108 Axum(#[from] axum::http::Error),
109
110 #[error(transparent)]
111 Tera(#[from] tera::Error),
112
113 #[error(transparent)]
114 JSON(serde_json::Error),
115
116 #[error("cannot parse `{1}`: {0}")]
117 YAMLFile(#[source] serde_yaml::Error, String),
118
119 #[error(transparent)]
120 YAML(#[from] serde_yaml::Error),
121
122 #[error("Error sending email: '{0}'")]
123 EmailSender(#[from] lettre::error::Error),
124
125 #[error("Error sending email (smtp): '{0}'")]
126 Smtp(#[from] smtp::Error),
127
128 #[error("Worker error: {0}")]
129 Worker(String),
130
131 #[error(transparent)]
132 IO(#[from] std::io::Error),
133
134 #[cfg(feature = "with-db")]
135 #[error(transparent)]
136 DB(#[from] sea_orm::DbErr),
137
138 #[error(transparent)]
139 ParseAddress(#[from] AddressError),
140
141 #[error(transparent)]
142 InvalidHeaderValue(#[from] InvalidHeaderValue),
143
144 #[error(transparent)]
145 InvalidHeaderName(#[from] InvalidHeaderName),
146
147 #[error(transparent)]
148 InvalidMethod(#[from] InvalidMethod),
149
150 #[cfg(feature = "worker_redis")]
151 #[error(transparent)]
152 Redis(#[from] redis::RedisError),
153
154 #[cfg(feature = "worker")]
155 #[error(transparent)]
156 Sqlx(#[from] sqlx::Error),
157
158 #[error(transparent)]
159 Storage(#[from] crate::storage::StorageError),
160
161 #[error(transparent)]
162 Cache(#[from] crate::cache::CacheError),
163
164 #[cfg(debug_assertions)]
165 #[error(transparent)]
166 Generators(#[from] loco_gen::Error),
167
168 #[error(transparent)]
169 VersionCheck(#[from] depcheck::VersionCheckError),
170
171 #[error(transparent)]
172 Any(#[from] Box<dyn std::error::Error + Send + Sync>),
173}
174
175impl Error {
176 pub fn wrap(err: impl std::error::Error + Send + Sync + 'static) -> Self {
177 Self::Any(Box::new(err)) }
179
180 pub fn msg(err: impl std::error::Error + Send + Sync + 'static) -> Self {
181 Self::Message(err.to_string()) }
183 #[must_use]
184 pub fn string(s: &str) -> Self {
185 Self::Message(s.to_string())
186 }
187 #[must_use]
188 pub fn bt(self) -> Self {
189 let backtrace = std::backtrace::Backtrace::capture();
190 match backtrace.status() {
191 std::backtrace::BacktraceStatus::Disabled
192 | std::backtrace::BacktraceStatus::Unsupported => self,
193 _ => Self::WithBacktrace {
194 inner: Box::new(self),
195 backtrace: Box::new(backtrace),
196 },
197 }
198 }
199}