1use crate::httpx;
20use crate::tracingcomponent::MetricsName;
21use http::{Method, StatusCode};
22use std::error::Error as StdError;
23use std::fmt::{Display, Formatter};
24
25pub type Result<T> = std::result::Result<T, Error>;
26
27#[derive(Debug, PartialEq)]
28pub struct Error {
29 inner: Box<ErrorImpl>,
30}
31
32impl Display for Error {
33 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
34 write!(f, "{}", self.inner.kind)
35 }
36}
37
38impl StdError for Error {}
39
40impl Error {
41 pub(crate) fn new_message_error(msg: impl Into<String>) -> Self {
42 Self {
43 inner: Box::new(ErrorImpl {
44 kind: ErrorKind::Message(msg.into()),
45 }),
46 }
47 }
48
49 pub(crate) fn new_invalid_argument_error(
50 msg: impl Into<String>,
51 arg: impl Into<Option<String>>,
52 ) -> Self {
53 Self {
54 inner: Box::new(ErrorImpl {
55 kind: ErrorKind::InvalidArgument {
56 msg: msg.into(),
57 arg: arg.into(),
58 },
59 }),
60 }
61 }
62
63 pub fn kind(&self) -> &ErrorKind {
64 &self.inner.kind
65 }
66}
67
68#[derive(Debug)]
69pub struct ErrorImpl {
70 pub kind: ErrorKind,
71}
72
73impl PartialEq for ErrorImpl {
74 fn eq(&self, other: &Self) -> bool {
75 self.kind == other.kind
76 }
77}
78
79#[derive(Debug, Clone, PartialEq, Eq)]
80#[non_exhaustive]
81pub enum ErrorKind {
82 Server(ServerError),
83 Resource(ResourceError),
84 #[non_exhaustive]
85 InvalidArgument {
86 msg: String,
87 arg: Option<String>,
88 },
89 Message(String),
90 Http(httpx::error::Error),
91}
92
93impl Display for ErrorKind {
94 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95 match self {
96 ErrorKind::Server(e) => write!(f, "server error: {e}"),
97 ErrorKind::Resource(e) => write!(f, "resource error: {e}"),
98 ErrorKind::InvalidArgument { msg, arg } => {
99 if let Some(arg) = arg {
100 write!(f, "invalid argument: {msg}: {arg}")
101 } else {
102 write!(f, "invalid argument: {msg}")
103 }
104 }
105 ErrorKind::Message(msg) => write!(f, "{msg}"),
106 ErrorKind::Http(e) => write!(f, "http error: {e}"),
107 }
108 }
109}
110
111#[derive(Debug, Clone, PartialEq, Eq)]
112pub struct ServerError {
113 status_code: StatusCode,
114 url: String,
115 body: String,
116 method: Method,
117 path: String,
118 kind: ServerErrorKind,
119}
120
121impl StdError for ServerError {}
122
123impl Display for ServerError {
124 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
125 write!(
126 f,
127 "server error: method: {}, path: {} status code: {}, body: {}, kind: {}",
128 self.method, self.path, self.status_code, self.body, self.kind
129 )
130 }
131}
132
133impl ServerError {
134 pub(crate) fn new(
135 status_code: StatusCode,
136 url: String,
137 method: Method,
138 path: String,
139 body: String,
140 kind: ServerErrorKind,
141 ) -> Self {
142 Self {
143 status_code,
144 url,
145 method,
146 path,
147 body,
148 kind,
149 }
150 }
151
152 pub fn kind(&self) -> &ServerErrorKind {
153 &self.kind
154 }
155
156 pub fn status_code(&self) -> StatusCode {
157 self.status_code
158 }
159
160 pub fn body(&self) -> &str {
161 &self.body
162 }
163
164 pub fn path(&self) -> &str {
165 &self.path
166 }
167
168 pub fn method(&self) -> &Method {
169 &self.method
170 }
171
172 pub fn url(&self) -> &str {
173 &self.url
174 }
175}
176
177#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
178#[non_exhaustive]
179pub enum ServerErrorKind {
180 AccessDenied,
181 UnsupportedFeature { feature: String },
182 ScopeExists,
183 ScopeNotFound,
184 CollectionExists,
185 CollectionNotFound,
186 BucketExists,
187 BucketNotFound,
188 FlushDisabled,
189 ServerInvalidArg { arg: String, reason: String },
190 SampleAlreadyLoaded,
191 InvalidSampleBucket,
192 BucketUuidMismatch,
193 UserNotFound,
194 GroupNotFound,
195 OperationDelayed,
196 Unknown,
197}
198
199impl Display for ServerErrorKind {
200 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
201 match self {
202 ServerErrorKind::AccessDenied => write!(f, "access denied"),
203 ServerErrorKind::UnsupportedFeature { feature } => {
204 write!(f, "unsupported feature {feature}")
205 }
206 ServerErrorKind::ScopeExists => write!(f, "scope exists"),
207 ServerErrorKind::ScopeNotFound => write!(f, "scope not found"),
208 ServerErrorKind::CollectionExists => write!(f, "collection exists"),
209 ServerErrorKind::CollectionNotFound => write!(f, "collection not found"),
210 ServerErrorKind::BucketExists => write!(f, "bucket exists"),
211 ServerErrorKind::BucketNotFound => write!(f, "bucket not found"),
212 ServerErrorKind::FlushDisabled => write!(f, "flush disabled"),
213 ServerErrorKind::ServerInvalidArg { arg, reason } => {
214 write!(f, "server invalid argument: {arg} - {reason}")
215 }
216 ServerErrorKind::BucketUuidMismatch => write!(f, "bucket uuid mismatch"),
217 ServerErrorKind::UserNotFound => write!(f, "user not found"),
218 ServerErrorKind::GroupNotFound => write!(f, "group not found"),
219 ServerErrorKind::OperationDelayed => {
220 write!(f, "operation was delayed, but will continue")
221 }
222 ServerErrorKind::SampleAlreadyLoaded => write!(f, "sample already loaded"),
223 ServerErrorKind::InvalidSampleBucket => write!(f, "invalid sample bucket"),
224 ServerErrorKind::Unknown => write!(f, "unknown error"),
225 }
226 }
227}
228
229#[derive(Debug, Clone, PartialEq, Eq)]
230pub struct ResourceError {
231 cause: ServerError,
232 scope_name: String,
233 collection_name: String,
234 bucket_name: String,
235}
236
237impl StdError for ResourceError {}
238
239impl Display for ResourceError {
240 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
241 write!(
242 f,
243 "resource error: scope: {}, collection: {}, bucket: {}, cause: {}",
244 self.scope_name, self.collection_name, self.bucket_name, self.cause
245 )
246 }
247}
248
249impl ResourceError {
250 pub(crate) fn new(
251 cause: ServerError,
252 bucket_name: impl Into<String>,
253 scope_name: impl Into<String>,
254 collection_name: impl Into<String>,
255 ) -> Self {
256 Self {
257 cause,
258 bucket_name: bucket_name.into(),
259 scope_name: scope_name.into(),
260 collection_name: collection_name.into(),
261 }
262 }
263
264 pub fn cause(&self) -> &ServerError {
265 &self.cause
266 }
267
268 pub fn bucket_name(&self) -> &str {
269 &self.bucket_name
270 }
271
272 pub fn scope_name(&self) -> &str {
273 &self.scope_name
274 }
275
276 pub fn collection_name(&self) -> &str {
277 &self.collection_name
278 }
279}
280
281impl<E> From<E> for Error
282where
283 ErrorKind: From<E>,
284{
285 fn from(err: E) -> Self {
286 Self {
287 inner: Box::new(ErrorImpl {
288 kind: ErrorKind::from(err),
289 }),
290 }
291 }
292}
293
294impl From<ServerError> for Error {
295 fn from(value: ServerError) -> Self {
296 Self {
297 inner: Box::new(ErrorImpl {
298 kind: ErrorKind::Server(value),
299 }),
300 }
301 }
302}
303
304impl From<ResourceError> for Error {
305 fn from(value: ResourceError) -> Self {
306 Self {
307 inner: Box::new(ErrorImpl {
308 kind: ErrorKind::Resource(value),
309 }),
310 }
311 }
312}
313
314impl From<httpx::error::Error> for Error {
315 fn from(value: httpx::error::Error) -> Self {
316 Self {
317 inner: Box::new(ErrorImpl {
318 kind: ErrorKind::Http(value),
319 }),
320 }
321 }
322}
323
324impl MetricsName for Error {
325 fn metrics_name(&self) -> &'static str {
326 match self.kind() {
327 ErrorKind::Server(e) => e.kind().metrics_name(),
328 ErrorKind::Resource(e) => e.cause().kind().metrics_name(),
329 ErrorKind::InvalidArgument { .. } => "mgmtx.InvalidArgument",
330 ErrorKind::Message(_) => "mgmtx._OTHER",
331 ErrorKind::Http(e) => e.metrics_name(),
332 }
333 }
334}
335
336impl MetricsName for ServerErrorKind {
337 fn metrics_name(&self) -> &'static str {
338 match self {
339 ServerErrorKind::AccessDenied => "mgmtx.AccessDenied",
340 ServerErrorKind::UnsupportedFeature { .. } => "mgmtx.UnsupportedFeature",
341 ServerErrorKind::ScopeExists => "mgmtx.ScopeExists",
342 ServerErrorKind::ScopeNotFound => "mgmtx.ScopeNotFound",
343 ServerErrorKind::CollectionExists => "mgmtx.CollectionExists",
344 ServerErrorKind::CollectionNotFound => "mgmtx.CollectionNotFound",
345 ServerErrorKind::BucketExists => "mgmtx.BucketExists",
346 ServerErrorKind::BucketNotFound => "mgmtx.BucketNotFound",
347 ServerErrorKind::FlushDisabled => "mgmtx.FlushDisabled",
348 ServerErrorKind::ServerInvalidArg { .. } => "mgmtx.ServerInvalidArg",
349 ServerErrorKind::SampleAlreadyLoaded => "mgmtx.SampleAlreadyLoaded",
350 ServerErrorKind::InvalidSampleBucket => "mgmtx.InvalidSampleBucket",
351 ServerErrorKind::BucketUuidMismatch => "mgmtx.BucketUuidMismatch",
352 ServerErrorKind::UserNotFound => "mgmtx.UserNotFound",
353 ServerErrorKind::GroupNotFound => "mgmtx.GroupNotFound",
354 ServerErrorKind::OperationDelayed => "mgmtx.OperationDelayed",
355 ServerErrorKind::Unknown => "mgmtx._OTHER",
356 }
357 }
358}