Skip to main content

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