couchbase_core/mgmtx/
error.rs

1/*
2 *
3 *  * Copyright (c) 2025 Couchbase, Inc.
4 *  *
5 *  * Licensed under the Apache License, Version 2.0 (the "License");
6 *  * you may not use this file except in compliance with the License.
7 *  * You may obtain a copy of the License at
8 *  *
9 *  *    http://www.apache.org/licenses/LICENSE-2.0
10 *  *
11 *  * Unless required by applicable law or agreed to in writing, software
12 *  * distributed under the License is distributed on an "AS IS" BASIS,
13 *  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  * See the License for the specific language governing permissions and
15 *  * limitations under the License.
16 *
17 */
18
19use http::{Method, StatusCode};
20use std::error::Error as StdError;
21use std::fmt::{Display, Formatter};
22
23pub type Result<T> = std::result::Result<T, Error>;
24
25#[derive(Debug, PartialEq)]
26pub struct Error {
27    inner: Box<ErrorImpl>,
28}
29
30impl Display for Error {
31    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
32        write!(f, "{}", self.inner.kind)
33    }
34}
35
36impl StdError for Error {}
37
38impl Error {
39    pub(crate) fn new_message_error(msg: impl Into<String>) -> Self {
40        Self {
41            inner: Box::new(ErrorImpl {
42                kind: ErrorKind::Message(msg.into()),
43            }),
44        }
45    }
46
47    pub(crate) fn new_invalid_argument_error(
48        msg: impl Into<String>,
49        arg: impl Into<Option<String>>,
50    ) -> Self {
51        Self {
52            inner: Box::new(ErrorImpl {
53                kind: ErrorKind::InvalidArgument {
54                    msg: msg.into(),
55                    arg: arg.into(),
56                },
57            }),
58        }
59    }
60
61    pub fn kind(&self) -> &ErrorKind {
62        &self.inner.kind
63    }
64}
65
66#[derive(Debug)]
67pub struct ErrorImpl {
68    pub kind: ErrorKind,
69}
70
71impl PartialEq for ErrorImpl {
72    fn eq(&self, other: &Self) -> bool {
73        self.kind == other.kind
74    }
75}
76
77#[derive(Debug, Clone, PartialEq, Eq)]
78#[non_exhaustive]
79pub enum ErrorKind {
80    Server(ServerError),
81    Resource(ResourceError),
82    #[non_exhaustive]
83    InvalidArgument {
84        msg: String,
85        arg: Option<String>,
86    },
87    Message(String),
88}
89
90impl Display for ErrorKind {
91    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92        match self {
93            ErrorKind::Server(e) => write!(f, "server error: {e}"),
94            ErrorKind::Resource(e) => write!(f, "resource error: {e}"),
95            ErrorKind::InvalidArgument { msg, arg } => {
96                if let Some(arg) = arg {
97                    write!(f, "invalid argument: {msg}: {arg}")
98                } else {
99                    write!(f, "invalid argument: {msg}")
100                }
101            }
102            ErrorKind::Message(msg) => write!(f, "{msg}"),
103        }
104    }
105}
106
107#[derive(Debug, Clone, PartialEq, Eq)]
108pub struct ServerError {
109    status_code: StatusCode,
110    url: String,
111    body: String,
112    method: Method,
113    path: String,
114    kind: ServerErrorKind,
115}
116
117impl StdError for ServerError {}
118
119impl Display for ServerError {
120    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121        write!(
122            f,
123            "server error: method: {}, path: {} status code: {}, body: {}, kind: {}",
124            self.method, self.path, self.status_code, self.body, self.kind
125        )
126    }
127}
128
129impl ServerError {
130    pub(crate) fn new(
131        status_code: StatusCode,
132        url: String,
133        method: Method,
134        path: String,
135        body: String,
136        kind: ServerErrorKind,
137    ) -> Self {
138        Self {
139            status_code,
140            url,
141            method,
142            path,
143            body,
144            kind,
145        }
146    }
147
148    pub fn kind(&self) -> &ServerErrorKind {
149        &self.kind
150    }
151
152    pub fn status_code(&self) -> StatusCode {
153        self.status_code
154    }
155
156    pub fn body(&self) -> &str {
157        &self.body
158    }
159
160    pub fn path(&self) -> &str {
161        &self.path
162    }
163
164    pub fn method(&self) -> &Method {
165        &self.method
166    }
167
168    pub fn url(&self) -> &str {
169        &self.url
170    }
171}
172
173#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
174#[non_exhaustive]
175pub enum ServerErrorKind {
176    AccessDenied,
177    UnsupportedFeature { feature: String },
178    ScopeExists,
179    ScopeNotFound,
180    CollectionExists,
181    CollectionNotFound,
182    BucketExists,
183    BucketNotFound,
184    FlushDisabled,
185    ServerInvalidArg { arg: String, reason: String },
186    BucketUuidMismatch,
187    UserNotFound,
188    GroupNotFound,
189    OperationDelayed,
190    Unknown,
191}
192
193impl Display for ServerErrorKind {
194    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
195        match self {
196            ServerErrorKind::AccessDenied => write!(f, "access denied"),
197            ServerErrorKind::UnsupportedFeature { feature } => {
198                write!(f, "unsupported feature {feature}")
199            }
200            ServerErrorKind::ScopeExists => write!(f, "scope exists"),
201            ServerErrorKind::ScopeNotFound => write!(f, "scope not found"),
202            ServerErrorKind::CollectionExists => write!(f, "collection exists"),
203            ServerErrorKind::CollectionNotFound => write!(f, "collection not found"),
204            ServerErrorKind::BucketExists => write!(f, "bucket exists"),
205            ServerErrorKind::BucketNotFound => write!(f, "bucket not found"),
206            ServerErrorKind::FlushDisabled => write!(f, "flush disabled"),
207            ServerErrorKind::ServerInvalidArg { arg, reason } => {
208                write!(f, "server invalid argument: {arg} - {reason}")
209            }
210            ServerErrorKind::BucketUuidMismatch => write!(f, "bucket uuid mismatch"),
211            ServerErrorKind::UserNotFound => write!(f, "user not found"),
212            ServerErrorKind::GroupNotFound => write!(f, "group not found"),
213            ServerErrorKind::OperationDelayed => {
214                write!(f, "operation was delayed, but will continue")
215            }
216            ServerErrorKind::Unknown => write!(f, "unknown error"),
217        }
218    }
219}
220
221#[derive(Debug, Clone, PartialEq, Eq)]
222pub struct ResourceError {
223    cause: ServerError,
224    scope_name: String,
225    collection_name: String,
226    bucket_name: String,
227}
228
229impl StdError for ResourceError {}
230
231impl Display for ResourceError {
232    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
233        write!(
234            f,
235            "resource error: scope: {}, collection: {}, bucket: {}, cause: {}",
236            self.scope_name, self.collection_name, self.bucket_name, self.cause
237        )
238    }
239}
240
241impl ResourceError {
242    pub(crate) fn new(
243        cause: ServerError,
244        bucket_name: impl Into<String>,
245        scope_name: impl Into<String>,
246        collection_name: impl Into<String>,
247    ) -> Self {
248        Self {
249            cause,
250            bucket_name: bucket_name.into(),
251            scope_name: scope_name.into(),
252            collection_name: collection_name.into(),
253        }
254    }
255
256    pub fn cause(&self) -> &ServerError {
257        &self.cause
258    }
259
260    pub fn bucket_name(&self) -> &str {
261        &self.bucket_name
262    }
263
264    pub fn scope_name(&self) -> &str {
265        &self.scope_name
266    }
267
268    pub fn collection_name(&self) -> &str {
269        &self.collection_name
270    }
271}
272
273impl<E> From<E> for Error
274where
275    ErrorKind: From<E>,
276{
277    fn from(err: E) -> Self {
278        Self {
279            inner: Box::new(ErrorImpl {
280                kind: ErrorKind::from(err),
281            }),
282        }
283    }
284}
285
286impl From<ServerError> for Error {
287    fn from(value: ServerError) -> Self {
288        Self {
289            inner: Box::new(ErrorImpl {
290                kind: ErrorKind::Server(value),
291            }),
292        }
293    }
294}
295
296impl From<ResourceError> for Error {
297    fn from(value: ResourceError) -> Self {
298        Self {
299            inner: Box::new(ErrorImpl {
300                kind: ErrorKind::Resource(value),
301            }),
302        }
303    }
304}