1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
use arangors_lite::ClientError;
use thiserror::Error;
pub use {
arango_error::ArangoError, arango_http_error::ArangoHttpError, database_error::DatabaseError,
};
mod arango_error;
mod arango_http_error;
mod database_error;
/// Error enum used for the Arango ORM mapped as potential Http errors
#[derive(Debug, Error)]
pub enum Error {
/// Unhandled error.
/// Can be interpreted as a HTTP code `500` internal error.
#[error("Internal Error")]
InternalError {
/// Optional message (will not be displayed)
message: Option<String>,
},
/// Validations failed (see model validation as implemented in [`Validate`].
/// Can be interpreted as a HTTP code `400` bad request.
///
/// [`Validate`]: crate::Validate
#[error("Validations failed: `{0}`")]
ValidationError(String),
/// An Item (document or collection) could not be found.
/// Can be interpreted as a HTTP code `404` not found.
#[error("{item} {id} not found")]
NotFound {
/// The missing item
item: String,
/// The missing item identifier
id: String,
/// Optional database source error
#[source]
source: Option<DatabaseError>,
},
/// An operation failed due to format or data issue.
///
/// Can be interpreted as a HTTP code `422` Unprocessable Entity.
#[error("Unprocessable Entity")]
UnprocessableEntity {
/// The source error
#[source]
source: Box<dyn std::error::Error + Send + Sync>,
},
/// The ArangoDb Error as returned by the database host
///
/// Can be interpreted as a HTTP code `500` Internal Error.
#[error("ArangoDB Error")]
ArangoError(#[source] DatabaseError),
/// A database conflict occured
///
/// Can be interpreted as a HTTP code `409` Conflict.
#[error("Conflict")]
Conflict(#[source] DatabaseError),
/// Failed to load config or initialize the app.
///
/// Can be interpreted as a HTTP code `500` Internal Error.
#[error("Failed to initialize `{item}`: `{message}`")]
InitError {
/// Item that failed to init
item: String,
/// Error message
message: String,
},
/// The operation is refused due to lack of authentication.
/// Can be interpreted as a HTTP code `401` unauthorized.
#[error("Unauthorized")]
Unauthorized(#[source] Option<DatabaseError>),
/// The operation is refused and authentication cannot resolve it.
/// Can be interpreted as a HTTP code `403` forbidden.
#[error("Forbidden")]
Forbidden(#[source] Option<DatabaseError>),
}
impl Error {
/// get the matching http code
#[allow(dead_code)]
#[must_use]
#[inline]
pub const fn http_code(&self) -> u16 {
match self {
Self::ValidationError(_str) => 400,
Self::UnprocessableEntity { .. } => 422,
Self::NotFound { .. } => 404,
Self::Forbidden(_) => 403,
Self::Unauthorized(_) => 401,
Self::ArangoError(_) | Self::InitError { .. } | Self::InternalError { .. } => 500,
Self::Conflict(_) => 409,
}
}
}
impl From<ClientError> for Error {
fn from(error: ClientError) -> Self {
log::debug!("Client Error: {}", error);
match error {
ClientError::Arango(arango_error) => {
let arango_error = DatabaseError::from(arango_error);
match arango_error.http_error {
ArangoHttpError::Unauthorized => Self::Unauthorized(Some(arango_error)),
ArangoHttpError::Forbidden => Self::Forbidden(Some(arango_error)),
ArangoHttpError::Conflict => Self::Conflict(arango_error),
_ => Self::ArangoError(arango_error),
}
}
ClientError::Serde(serde_error) => Self::UnprocessableEntity {
source: Box::new(serde_error),
},
ClientError::InvalidServer(server) => Self::InitError {
item: server,
message: String::from("Is not ArangoDB"),
},
ClientError::InsufficientPermission {
permission,
operation,
} => Self::InitError {
item: operation.clone(),
message: format!(
"Insufficent permission for {} : {:?}",
operation, permission
),
},
ClientError::HttpClient(error) => Self::InitError {
item: "Http Client".to_string(),
message: error.to_string(),
},
}
}
}
impl From<serde_json::Error> for Error {
fn from(err: serde_json::Error) -> Self {
Self::UnprocessableEntity {
source: Box::new(err),
}
}
}
impl Default for Error {
fn default() -> Self {
Self::InternalError { message: None }
}
}