1#[cfg(feature = "sqlx-dep")]
2pub use sqlx::error::Error as SqlxError;
3
4#[cfg(feature = "sqlx-mysql")]
5pub use sqlx::mysql::MySqlDatabaseError as SqlxMySqlError;
6
7#[cfg(feature = "sqlx-postgres")]
8pub use sqlx::postgres::PgDatabaseError as SqlxPostgresError;
9
10#[cfg(feature = "sqlx-sqlite")]
11pub use sqlx::sqlite::SqliteError as SqlxSqliteError;
12
13use std::sync::Arc;
14use thiserror::Error;
15
16#[derive(Error, Debug, Clone)]
18pub enum DbErr {
19 #[error("Failed to acquire connection from pool: {0}")]
21 ConnectionAcquire(#[source] ConnAcquireErr),
22 #[error("Error converting `{from}` into `{into}`: {source}")]
24 TryIntoErr {
25 from: &'static str,
27 into: &'static str,
29 source: Arc<dyn std::error::Error + Send + Sync>,
31 },
32 #[error("Connection Error: {0}")]
34 Conn(#[source] RuntimeErr),
35 #[error("Execution Error: {0}")]
37 Exec(#[source] RuntimeErr),
38 #[error("Query Error: {0}")]
40 Query(#[source] RuntimeErr),
41 #[error("Type '{0}' cannot be converted from u64")]
43 ConvertFromU64(&'static str),
44 #[error("Failed to unpack last_insert_id")]
46 UnpackInsertId,
47 #[error("Failed to get primary key from model")]
50 UpdateGetPrimaryKey,
51 #[error("RecordNotFound Error: {0}")]
53 RecordNotFound(String),
54 #[error("Attribute {0} is NotSet")]
56 AttrNotSet(String),
57 #[error("Custom Error: {0}")]
59 Custom(String),
60 #[error("Type Error: {0}")]
62 Type(String),
63 #[error("Json Error: {0}")]
65 Json(String),
66 #[error("Migration Error: {0}")]
68 Migration(String),
69 #[error("None of the records are inserted")]
72 RecordNotInserted,
73 #[error("None of the records are updated")]
76 RecordNotUpdated,
77 #[error("Operation not supported by backend {db}: {ctx}")]
79 BackendNotSupported {
80 db: &'static str,
82 ctx: &'static str,
84 },
85 #[error("Key arity mismatch: expected {expected}, received {received}")]
87 KeyArityMismatch {
88 expected: u8,
90 received: u8,
92 },
93 #[error("Primay key not set for {ctx}")]
95 PrimaryKeyNotSet {
96 ctx: &'static str,
98 },
99 #[error("RBAC error: {0}")]
101 RbacError(String),
102 #[error("Access denied: cannot perform `{permission}` on `{resource}`")]
104 AccessDenied {
105 permission: String,
107 resource: String,
109 },
110}
111
112#[derive(Debug)]
114pub enum TryGetError {
115 DbErr(DbErr),
117 Null(String),
119}
120
121#[derive(Error, Debug, PartialEq, Eq, Copy, Clone)]
123pub enum ConnAcquireErr {
124 #[error("Connection pool timed out")]
126 Timeout,
127 #[error("Connection closed")]
129 ConnectionClosed,
130}
131
132#[derive(Error, Debug, Clone)]
134pub enum RuntimeErr {
135 #[cfg(feature = "sqlx-dep")]
137 #[error("{0}")]
138 SqlxError(Arc<sqlx::error::Error>),
139 #[error("{0}")]
141 Internal(String),
142}
143
144impl PartialEq for DbErr {
145 fn eq(&self, other: &Self) -> bool {
146 self.to_string() == other.to_string()
147 }
148}
149
150impl Eq for DbErr {}
151
152#[derive(Error, Debug)]
154#[error("Failed to match \"{0}\" as Column")]
155pub struct ColumnFromStrErr(pub String);
156
157#[allow(dead_code)]
158pub(crate) fn conn_err<T>(s: T) -> DbErr
159where
160 T: ToString,
161{
162 DbErr::Conn(RuntimeErr::Internal(s.to_string()))
163}
164
165#[allow(dead_code)]
166pub(crate) fn exec_err<T>(s: T) -> DbErr
167where
168 T: ToString,
169{
170 DbErr::Exec(RuntimeErr::Internal(s.to_string()))
171}
172
173#[allow(dead_code)]
174pub(crate) fn query_err<T>(s: T) -> DbErr
175where
176 T: ToString,
177{
178 DbErr::Query(RuntimeErr::Internal(s.to_string()))
179}
180
181#[allow(dead_code)]
182pub(crate) fn type_err<T>(s: T) -> DbErr
183where
184 T: ToString,
185{
186 DbErr::Type(s.to_string())
187}
188
189#[allow(dead_code)]
190pub(crate) fn json_err<T>(s: T) -> DbErr
191where
192 T: ToString,
193{
194 DbErr::Json(s.to_string())
195}
196
197#[derive(Error, Debug, Clone, PartialEq, Eq)]
199#[non_exhaustive]
200pub enum SqlErr {
201 #[error("Unique Constraint Violated: {0}")]
203 UniqueConstraintViolation(String),
204 #[error("Foreign Key Constraint Violated: {0}")]
206 ForeignKeyConstraintViolation(String),
207}
208
209#[allow(dead_code)]
210impl DbErr {
211 pub fn sql_err(&self) -> Option<SqlErr> {
213 #[cfg(any(
214 feature = "sqlx-mysql",
215 feature = "sqlx-postgres",
216 feature = "sqlx-sqlite"
217 ))]
218 {
219 use std::ops::Deref;
220 if let DbErr::Exec(RuntimeErr::SqlxError(e)) | DbErr::Query(RuntimeErr::SqlxError(e)) =
221 self
222 {
223 if let sqlx::Error::Database(e) = e.deref() {
224 let error_code = e.code().unwrap_or_default();
225 let _error_code_expanded = error_code.deref();
226 #[cfg(feature = "sqlx-mysql")]
227 if e.try_downcast_ref::<sqlx::mysql::MySqlDatabaseError>()
228 .is_some()
229 {
230 let error_number = e
231 .try_downcast_ref::<sqlx::mysql::MySqlDatabaseError>()?
232 .number();
233 match error_number {
234 1022 | 1062 | 1169 | 1586 => {
239 return Some(SqlErr::UniqueConstraintViolation(e.message().into()));
240 }
241 1216 | 1217 | 1451 | 1452 | 1557 | 1761 | 1762 => {
249 return Some(SqlErr::ForeignKeyConstraintViolation(
250 e.message().into(),
251 ));
252 }
253 _ => return None,
254 }
255 }
256 #[cfg(feature = "sqlx-postgres")]
257 if e.try_downcast_ref::<sqlx::postgres::PgDatabaseError>()
258 .is_some()
259 {
260 match _error_code_expanded {
261 "23505" => {
262 return Some(SqlErr::UniqueConstraintViolation(e.message().into()));
263 }
264 "23503" => {
265 return Some(SqlErr::ForeignKeyConstraintViolation(
266 e.message().into(),
267 ));
268 }
269 _ => return None,
270 }
271 }
272 #[cfg(feature = "sqlx-sqlite")]
273 if e.try_downcast_ref::<sqlx::sqlite::SqliteError>().is_some() {
274 match _error_code_expanded {
275 "1555" | "2067" => {
278 return Some(SqlErr::UniqueConstraintViolation(e.message().into()));
279 }
280 "787" => {
281 return Some(SqlErr::ForeignKeyConstraintViolation(
282 e.message().into(),
283 ));
284 }
285 _ => return None,
286 }
287 }
288 }
289 }
290 }
291 None
292 }
293}