couchbase_core/
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 crate::httpx::error::Error as HttpError;
20use crate::memdx;
21use crate::mgmtx::error::Error as MgmtError;
22use crate::queryx::error::Error as QueryError;
23use crate::retry::RetryInfo;
24use crate::searchx::error::Error as SearchError;
25use crate::service_type::ServiceType;
26use std::error::Error as StdError;
27use std::fmt::{Display, Formatter};
28use std::ops::Deref;
29use std::sync::Arc;
30
31pub type Result<T> = std::result::Result<T, Error>;
32
33#[derive(Debug, Clone)]
34pub struct Error {
35    kind: Arc<ErrorKind>,
36    retry_info: Option<RetryInfo>,
37}
38
39impl Display for Error {
40    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
41        if let Some(retry_info) = &self.retry_info {
42            return write!(f, "{}, {}", self.kind, retry_info);
43        }
44        write!(f, "{}", self.kind)
45    }
46}
47
48impl StdError for Error {
49    fn source(&self) -> Option<&(dyn StdError + 'static)> {
50        match self.kind.as_ref() {
51            ErrorKind::Memdx(err) => err.inner.source.source(),
52            ErrorKind::Query(err) => err.source(),
53            ErrorKind::Search(err) => err.source(),
54            ErrorKind::Http(err) => err.source(),
55            ErrorKind::Mgmt(err) => err.source(),
56            _ => None,
57        }
58    }
59}
60
61impl Error {
62    pub(crate) fn new(kind: ErrorKind) -> Self {
63        Self {
64            kind: Arc::new(kind),
65            retry_info: None,
66        }
67    }
68
69    pub(crate) fn new_contextual_memdx_error(e: MemdxError) -> Self {
70        Self::new(ErrorKind::Memdx(e))
71    }
72
73    pub(crate) fn new_message_error(msg: impl Into<String>) -> Self {
74        Self::new(ErrorKind::Message { msg: msg.into() })
75    }
76
77    pub(crate) fn new_invalid_argument_error(msg: impl Into<String>, arg: Option<String>) -> Self {
78        Self::new(ErrorKind::InvalidArgument {
79            msg: msg.into(),
80            arg,
81        })
82    }
83
84    pub(crate) fn new_feature_not_available_error(
85        feature: impl Into<String>,
86        msg: impl Into<String>,
87    ) -> Self {
88        Self::new(ErrorKind::FeatureNotAvailable {
89            feature: feature.into(),
90            msg: msg.into(),
91        })
92    }
93
94    pub fn kind(&self) -> &ErrorKind {
95        &self.kind
96    }
97
98    pub(crate) fn is_memdx_error(&self) -> Option<&memdx::error::Error> {
99        match self.kind.as_ref() {
100            ErrorKind::Memdx(err) => Some(err),
101            _ => None,
102        }
103    }
104
105    pub(crate) fn set_retry_info(&mut self, retry_info: RetryInfo) {
106        self.retry_info = Some(retry_info);
107    }
108
109    pub fn retry_info(&self) -> Option<&RetryInfo> {
110        self.retry_info.as_ref()
111    }
112}
113
114#[derive(Debug, PartialEq)]
115#[non_exhaustive]
116pub enum ErrorKind {
117    Memdx(MemdxError),
118    Query(QueryError),
119    Search(SearchError),
120    Http(HttpError),
121    Mgmt(MgmtError),
122    VbucketMapOutdated,
123    #[non_exhaustive]
124    InvalidArgument {
125        msg: String,
126        arg: Option<String>,
127    },
128    #[non_exhaustive]
129    EndpointNotKnown {
130        endpoint: String,
131    },
132    InvalidVbucket {
133        requested_vb_id: u16,
134        num_vbuckets: usize,
135    },
136    InvalidReplica {
137        requested_replica: u32,
138        num_servers: usize,
139    },
140    NoEndpointsAvailable,
141    Shutdown,
142    NoBucket,
143    IllegalState {
144        msg: String,
145    },
146    NoVbucketMap,
147    #[non_exhaustive]
148    NoServerAssigned {
149        requested_vb_id: u16,
150    },
151    #[non_exhaustive]
152    CollectionManifestOutdated {
153        manifest_uid: u64,
154        server_manifest_uid: u64,
155    },
156    #[non_exhaustive]
157    Message {
158        msg: String,
159    },
160    #[non_exhaustive]
161    ServiceNotAvailable {
162        service: ServiceType,
163    },
164    #[non_exhaustive]
165    FeatureNotAvailable {
166        feature: String,
167        msg: String,
168    },
169    #[non_exhaustive]
170    Compression {
171        msg: String,
172    },
173    #[non_exhaustive]
174    Internal {
175        msg: String,
176    },
177}
178
179impl Display for ErrorKind {
180    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
181        match self {
182            ErrorKind::VbucketMapOutdated => write!(f, "vbucket map outdated"),
183            ErrorKind::InvalidArgument { msg, arg } => {
184                if let Some(arg) = arg {
185                    write!(f, "invalid argument {arg}: {msg}")
186                } else {
187                    write!(f, "invalid argument: {msg}")
188                }
189            }
190            ErrorKind::Memdx(err) => write!(f, "{err}"),
191            ErrorKind::Query(err) => write!(f, "{err}"),
192            ErrorKind::Search(err) => write!(f, "{err}"),
193            ErrorKind::Http(err) => write!(f, "{err}"),
194            ErrorKind::Mgmt(err) => write!(f, "{err}"),
195            ErrorKind::EndpointNotKnown { endpoint } => {
196                write!(f, "endpoint not known: {endpoint}")
197            }
198            ErrorKind::NoEndpointsAvailable => write!(f, "no endpoints available"),
199            ErrorKind::Shutdown => write!(f, "shutdown"),
200            ErrorKind::NoBucket => write!(f, "no bucket selected"),
201            ErrorKind::IllegalState { msg } => write!(f, "illegal state: {msg}"),
202            ErrorKind::NoVbucketMap => write!(f, "invalid vbucket map"),
203            ErrorKind::CollectionManifestOutdated {
204                manifest_uid,
205                server_manifest_uid,
206            } => {
207                write!(
208                    f,
209                    "collection manifest outdated: our manifest uid: {manifest_uid}, server manifest uid: {server_manifest_uid}"
210                )
211            }
212            ErrorKind::Message { msg } => write!(f, "{msg}"),
213            ErrorKind::ServiceNotAvailable { service } => {
214                write!(f, "service not available: {service}")
215            }
216            ErrorKind::FeatureNotAvailable { feature, msg } => {
217                write!(f, "feature not available: {feature}, {msg}")
218            }
219            ErrorKind::Internal { msg } => write!(f, "internal error: {msg}"),
220            ErrorKind::NoServerAssigned { requested_vb_id } => {
221                write!(f, "no server assigned for vbucket id: {requested_vb_id}")
222            }
223            ErrorKind::InvalidVbucket {
224                requested_vb_id,
225                num_vbuckets,
226            } => write!(
227                f,
228                "invalid vbucket id: {requested_vb_id}, num vbuckets: {num_vbuckets}"
229            ),
230            ErrorKind::InvalidReplica {
231                requested_replica,
232                num_servers,
233            } => write!(
234                f,
235                "invalid replica: {requested_replica}, num servers: {num_servers}"
236            ),
237            ErrorKind::Compression { msg } => write!(f, "compression error: {msg}"),
238        }
239    }
240}
241
242#[derive(Debug, PartialEq)]
243pub struct MemdxError {
244    inner: Box<InnerMemdxError>,
245}
246
247#[derive(Debug, PartialEq)]
248pub struct InnerMemdxError {
249    source: memdx::error::Error,
250    dispatched_to: Option<String>,
251    dispatched_from: Option<String>,
252    doc_id: Option<Vec<u8>>,
253    bucket_name: Option<String>,
254    scope_name: Option<String>,
255    collection_name: Option<String>,
256}
257
258impl Deref for MemdxError {
259    type Target = memdx::error::Error;
260
261    fn deref(&self) -> &Self::Target {
262        &self.inner.source
263    }
264}
265
266impl MemdxError {
267    pub(crate) fn new(source: memdx::error::Error) -> Self {
268        Self {
269            inner: Box::new(InnerMemdxError {
270                source,
271                dispatched_to: None,
272                dispatched_from: None,
273                doc_id: None,
274                bucket_name: None,
275                scope_name: None,
276                collection_name: None,
277            }),
278        }
279    }
280
281    pub(crate) fn with_dispatched_to(mut self, dispatched_to: impl Into<String>) -> Self {
282        self.inner.dispatched_to = Some(dispatched_to.into());
283        self
284    }
285
286    pub(crate) fn with_dispatched_from(mut self, dispatched_from: impl Into<String>) -> Self {
287        self.inner.dispatched_from = Some(dispatched_from.into());
288        self
289    }
290
291    pub fn dispatched_to(&self) -> Option<&String> {
292        self.inner.dispatched_to.as_ref()
293    }
294
295    pub fn dispatched_from(&self) -> Option<&String> {
296        self.inner.dispatched_from.as_ref()
297    }
298
299    pub fn doc_id(&self) -> Option<&[u8]> {
300        self.inner.doc_id.as_deref()
301    }
302
303    pub fn bucket_name(&self) -> Option<&String> {
304        self.inner.bucket_name.as_ref()
305    }
306
307    pub fn scope_name(&self) -> Option<&String> {
308        self.inner.scope_name.as_ref()
309    }
310
311    pub fn collection_name(&self) -> Option<&String> {
312        self.inner.collection_name.as_ref()
313    }
314
315    pub(crate) fn set_doc_id(mut self, doc_id: Vec<u8>) -> Self {
316        self.inner.doc_id = Some(doc_id);
317        self
318    }
319
320    pub(crate) fn set_bucket_name(mut self, bucket_name: String) -> Self {
321        self.inner.bucket_name = Some(bucket_name);
322        self
323    }
324
325    pub(crate) fn set_scope_name(mut self, scope_name: String) -> Self {
326        self.inner.scope_name = Some(scope_name);
327        self
328    }
329
330    pub(crate) fn set_collection_name(mut self, collection_name: String) -> Self {
331        self.inner.collection_name = Some(collection_name);
332        self
333    }
334}
335
336impl Display for MemdxError {
337    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
338        write!(f, "{}", self.inner.source)?;
339        if let Some(ref dispatched_to) = self.inner.dispatched_to {
340            write!(f, ", dispatched to: {dispatched_to}")?;
341        }
342        if let Some(ref dispatched_from) = self.inner.dispatched_from {
343            write!(f, ", dispatched from: {dispatched_from}")?;
344        }
345        Ok(())
346    }
347}
348
349impl<E> From<E> for Error
350where
351    ErrorKind: From<E>,
352{
353    fn from(err: E) -> Self {
354        Self {
355            kind: Arc::new(err.into()),
356            retry_info: None,
357        }
358    }
359}
360
361impl From<QueryError> for Error {
362    fn from(value: QueryError) -> Self {
363        Self::new(ErrorKind::Query(value))
364    }
365}
366
367impl From<HttpError> for Error {
368    fn from(value: HttpError) -> Self {
369        Self::new(ErrorKind::Http(value))
370    }
371}
372
373impl From<SearchError> for Error {
374    fn from(value: SearchError) -> Self {
375        Self::new(ErrorKind::Search(value))
376    }
377}
378
379impl From<MgmtError> for Error {
380    fn from(value: MgmtError) -> Self {
381        Self::new(ErrorKind::Mgmt(value))
382    }
383}