couchbase_core/memdx/
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 serde::Deserialize;
20use std::error::Error as StdError;
21use std::fmt::{Display, Formatter, Pointer};
22use std::io;
23
24use crate::memdx::opcode::OpCode;
25use crate::memdx::status::Status;
26
27pub type Result<T> = std::result::Result<T, Error>;
28
29#[derive(Debug, PartialEq)]
30pub struct Error {
31    inner: ErrorImpl,
32}
33
34impl Error {
35    pub(crate) fn new_protocol_error(msg: impl Into<String>) -> Self {
36        Self {
37            inner: ErrorImpl {
38                kind: Box::new(ErrorKind::Protocol { msg: msg.into() }),
39                source: None,
40            },
41        }
42    }
43
44    pub(crate) fn new_decompression_error() -> Self {
45        Self {
46            inner: ErrorImpl {
47                kind: Box::new(ErrorKind::Decompression {}),
48                source: None,
49            },
50        }
51    }
52
53    pub(crate) fn new_message_error(msg: impl Into<String>) -> Self {
54        Self {
55            inner: ErrorImpl {
56                kind: Box::new(ErrorKind::Message(msg.into())),
57                source: None,
58            },
59        }
60    }
61
62    pub(crate) fn new_cancelled_error(cancellation_kind: CancellationErrorKind) -> Self {
63        Self {
64            inner: ErrorImpl {
65                kind: Box::new(ErrorKind::Cancelled(cancellation_kind)),
66                source: None,
67            },
68        }
69    }
70
71    pub(crate) fn new_invalid_argument_error(
72        msg: impl Into<String>,
73        arg: impl Into<Option<String>>,
74    ) -> Self {
75        Self {
76            inner: ErrorImpl {
77                kind: Box::new(ErrorKind::InvalidArgument {
78                    msg: msg.into(),
79                    arg: arg.into(),
80                }),
81                source: None,
82            },
83        }
84    }
85
86    pub(crate) fn new_connection_failed_error(
87        reason: impl Into<String>,
88        source: Box<io::Error>,
89    ) -> Self {
90        Self {
91            inner: ErrorImpl {
92                kind: Box::new(ErrorKind::ConnectionFailed { msg: reason.into() }),
93                source: Some(source),
94            },
95        }
96    }
97
98    pub(crate) fn new_dispatch_error(opaque: u32, op_code: OpCode, source: Box<Error>) -> Self {
99        Self {
100            inner: ErrorImpl {
101                kind: Box::new(ErrorKind::Dispatch { opaque, op_code }),
102                source: Some(source),
103            },
104        }
105    }
106
107    pub(crate) fn new_close_error(msg: String, source: Box<Error>) -> Self {
108        Self {
109            inner: ErrorImpl {
110                kind: Box::new(ErrorKind::Close { msg }),
111                source: Some(source),
112            },
113        }
114    }
115
116    pub fn has_server_config(&self) -> Option<&Vec<u8>> {
117        if let ErrorKind::Server(ServerError { config, .. }) = self.inner.kind.as_ref() {
118            config.as_ref()
119        } else {
120            None
121        }
122    }
123
124    pub fn has_server_error_context(&self) -> Option<&Vec<u8>> {
125        if let ErrorKind::Server(ServerError { context, .. }) = self.inner.kind.as_ref() {
126            context.as_ref()
127        } else if let ErrorKind::Resource(ResourceError { cause, .. }) = self.inner.kind.as_ref() {
128            cause.context.as_ref()
129        } else {
130            None
131        }
132    }
133
134    pub fn has_opaque(&self) -> Option<u32> {
135        let inner_kind = self.inner.kind.as_ref();
136        if let ErrorKind::Server(ServerError { opaque, .. }) = inner_kind {
137            Some(*opaque)
138        } else if let ErrorKind::Resource(e) = inner_kind {
139            Some(e.cause.opaque)
140        } else if let ErrorKind::Dispatch { opaque, .. } = inner_kind {
141            Some(*opaque)
142        } else {
143            None
144        }
145    }
146
147    pub fn is_cancellation_error(&self) -> bool {
148        matches!(self.inner.kind.as_ref(), ErrorKind::Cancelled { .. })
149    }
150
151    pub fn is_dispatch_error(&self) -> bool {
152        matches!(self.inner.kind.as_ref(), ErrorKind::Dispatch { .. })
153    }
154
155    pub fn is_server_error_kind(&self, kind: ServerErrorKind) -> bool {
156        match self.inner.kind.as_ref() {
157            ErrorKind::Server(e) => e.kind == kind,
158            ErrorKind::Resource(e) => e.cause.kind == kind,
159            _ => false,
160        }
161    }
162
163    pub fn kind(&self) -> &ErrorKind {
164        &self.inner.kind
165    }
166
167    pub(crate) fn with<C: Into<Source>>(mut self, source: C) -> Error {
168        self.inner.source = Some(source.into());
169        self
170    }
171}
172
173type Source = Box<dyn StdError + Send + Sync>;
174
175#[derive(Debug)]
176struct ErrorImpl {
177    kind: Box<ErrorKind>,
178    source: Option<Source>,
179}
180
181impl PartialEq for ErrorImpl {
182    fn eq(&self, other: &Self) -> bool {
183        self.kind == other.kind
184    }
185}
186
187impl Display for Error {
188    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
189        write!(f, "{}", self.inner.kind)
190    }
191}
192
193impl StdError for Error {
194    fn source(&self) -> Option<&(dyn StdError + 'static)> {
195        self.inner
196            .source
197            .as_ref()
198            .map(|cause| &**cause as &(dyn StdError + 'static))
199    }
200}
201
202#[derive(Debug, Clone, PartialEq)]
203#[non_exhaustive]
204pub enum ErrorKind {
205    Server(ServerError),
206    Resource(ResourceError),
207    #[non_exhaustive]
208    Dispatch {
209        opaque: u32,
210        op_code: OpCode,
211    },
212    #[non_exhaustive]
213    Close {
214        msg: String,
215    },
216    #[non_exhaustive]
217    Protocol {
218        msg: String,
219    },
220    Cancelled(CancellationErrorKind),
221    #[non_exhaustive]
222    ConnectionFailed {
223        msg: String,
224    },
225    Io,
226    #[non_exhaustive]
227    InvalidArgument {
228        msg: String,
229        arg: Option<String>,
230    },
231    Decompression,
232    Message(String),
233}
234
235impl Display for ErrorKind {
236    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
237        match self {
238            ErrorKind::Server(e) => write!(f, "{e}"),
239            ErrorKind::Resource(e) => write!(f, "{e}"),
240            ErrorKind::Dispatch { opaque, op_code } => {
241                write!(f, "dispatch failed: opaque: {opaque}, op_code: {op_code}")
242            }
243            ErrorKind::Close { msg } => {
244                write!(f, "close error {msg}")
245            }
246            ErrorKind::Protocol { msg } => {
247                write!(f, "{msg}")
248            }
249            ErrorKind::Cancelled(kind) => {
250                write!(f, "request cancelled: {kind}")
251            }
252            ErrorKind::ConnectionFailed { msg } => {
253                write!(f, "connection failed {msg}")
254            }
255            ErrorKind::Io => {
256                write!(f, "connection error")
257            }
258            ErrorKind::InvalidArgument { msg, arg } => {
259                let base_msg = format!("invalid argument: {msg}");
260                if let Some(arg) = arg {
261                    write!(f, "{base_msg}, arg: {arg}")
262                } else {
263                    write!(f, "{base_msg}")
264                }
265            }
266            ErrorKind::Decompression => write!(f, "decompression error"),
267            ErrorKind::Message(msg) => write!(f, "{msg}"),
268        }
269    }
270}
271
272#[derive(Clone, Debug, PartialEq, Eq)]
273#[non_exhaustive]
274pub struct ResourceError {
275    cause: ServerError,
276    scope_name: String,
277    collection_name: String,
278}
279
280impl StdError for ResourceError {}
281
282impl ResourceError {
283    pub(crate) fn new(
284        cause: ServerError,
285        scope_name: impl Into<String>,
286        collection_name: impl Into<String>,
287    ) -> Self {
288        Self {
289            cause,
290            scope_name: scope_name.into(),
291            collection_name: collection_name.into(),
292        }
293    }
294
295    pub fn cause(&self) -> &ServerError {
296        &self.cause
297    }
298
299    pub fn scope_name(&self) -> &str {
300        &self.scope_name
301    }
302
303    pub fn collection_name(&self) -> &str {
304        &self.collection_name
305    }
306}
307
308impl Display for ResourceError {
309    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
310        write!(
311            f,
312            "Resource error: {}, scope: {}, collection: {}",
313            self.cause, self.scope_name, self.collection_name
314        )
315    }
316}
317
318#[derive(Clone, Debug, PartialEq, Eq)]
319#[non_exhaustive]
320pub struct ServerError {
321    kind: ServerErrorKind,
322    config: Option<Vec<u8>>,
323    context: Option<Vec<u8>>,
324    op_code: OpCode,
325    status: Status,
326    opaque: u32,
327}
328
329impl StdError for ServerError {}
330
331impl Display for ServerError {
332    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
333        let mut base_msg = format!(
334            "Server error: {}, status: 0x{:02x}, opcode: {}, opaque: {}",
335            self.kind,
336            u16::from(self.status),
337            self.op_code,
338            self.opaque
339        );
340
341        if let Some(context) = &self.context {
342            if let Some(parsed) = Self::parse_context(context) {
343                base_msg.push_str(" (");
344                if let Some(text) = &parsed.text {
345                    base_msg.push_str(&format!("context: {text}, "));
346                }
347
348                if let Some(error_ref) = &parsed.error_ref {
349                    base_msg.push_str(&format!("error_ref: {error_ref}"));
350                }
351                base_msg.push(')');
352            }
353        }
354
355        write!(f, "{base_msg}")
356    }
357}
358
359impl ServerError {
360    pub(crate) fn new(kind: ServerErrorKind, op_code: OpCode, status: Status, opaque: u32) -> Self {
361        Self {
362            kind,
363            config: None,
364            context: None,
365            op_code,
366            status,
367            opaque,
368        }
369    }
370
371    pub(crate) fn with_context(mut self, context: Vec<u8>) -> Self {
372        self.context = Some(context);
373        self
374    }
375
376    pub(crate) fn with_config(mut self, config: Vec<u8>) -> Self {
377        self.config = Some(config);
378        self
379    }
380
381    pub fn kind(&self) -> &ServerErrorKind {
382        &self.kind
383    }
384
385    pub fn config(&self) -> Option<&Vec<u8>> {
386        self.config.as_ref()
387    }
388
389    pub fn context(&self) -> Option<&Vec<u8>> {
390        self.context.as_ref()
391    }
392
393    pub fn op_code(&self) -> OpCode {
394        self.op_code
395    }
396
397    pub fn status(&self) -> Status {
398        self.status
399    }
400
401    pub fn opaque(&self) -> u32 {
402        self.opaque
403    }
404
405    pub fn parse_context(context: &[u8]) -> Option<ServerErrorContext> {
406        if context.is_empty() {
407            return None;
408        }
409
410        let context_json: ServerErrorContextJson = match serde_json::from_slice(context) {
411            Ok(c) => c,
412            Err(_) => {
413                return None;
414            }
415        };
416
417        let text = context_json.error.context;
418
419        let error_ref = context_json.error.error_ref;
420
421        let manifest_rev = context_json
422            .manifest_rev
423            .map(|manifest_rev| u64::from_str_radix(&manifest_rev, 16).unwrap_or_default());
424
425        Some(ServerErrorContext {
426            text,
427            error_ref,
428            manifest_rev,
429        })
430    }
431}
432
433#[derive(Clone, Debug, PartialEq, Eq)]
434#[non_exhaustive]
435pub struct ServerErrorContext {
436    pub text: Option<String>,
437    pub error_ref: Option<String>,
438    pub manifest_rev: Option<u64>,
439}
440
441#[derive(Clone, Debug, Eq, Hash, PartialEq)]
442#[non_exhaustive]
443pub enum ServerErrorKind {
444    KeyNotFound,
445    KeyExists,
446    TooBig,
447    NotStored,
448    BadDelta,
449    NotMyVbucket,
450    NoBucket,
451    Locked,
452    NotLocked,
453    Auth { msg: String },
454    RangeError,
455    Access,
456    RateLimitedNetworkIngress,
457    RateLimitedNetworkEgress,
458    RateLimitedMaxConnections,
459    RateLimitedMaxCommands,
460    RateLimitedScopeSizeLimitExceeded,
461    UnknownCommand,
462    NotSupported,
463    InternalError,
464    Busy,
465    TmpFail,
466    UnknownCollectionID,
467    UnknownScopeName,
468    UnknownCollectionName,
469    DurabilityInvalid,
470    DurabilityImpossible,
471    SyncWriteInProgress,
472    SyncWriteAmbiguous,
473    SyncWriteRecommitInProgress,
474    RangeScanCancelled,
475    RangeScanVBUUIDNotEqual,
476    InvalidArgs,
477
478    ConfigNotSet,
479    UnknownBucketName,
480    CasMismatch,
481
482    Subdoc { error: SubdocError },
483
484    UnknownStatus { status: Status },
485}
486
487impl Display for ServerErrorKind {
488    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
489        match self {
490            ServerErrorKind::NotMyVbucket => write!(f, "not my vbucket"),
491            ServerErrorKind::KeyExists => write!(f, "key exists"),
492            ServerErrorKind::NotStored => write!(f, "key not stored"),
493            ServerErrorKind::KeyNotFound => write!(f, "key not found"),
494            ServerErrorKind::TmpFail => write!(f, "temporary failure"),
495            ServerErrorKind::CasMismatch => write!(f, "cas mismatch"),
496            ServerErrorKind::Locked => write!(f, "locked"),
497            ServerErrorKind::NotLocked => write!(f, "not locked"),
498            ServerErrorKind::TooBig => write!(f, "too big"),
499            ServerErrorKind::UnknownCollectionID => write!(f, "unknown collection id"),
500            ServerErrorKind::NoBucket => write!(f, "no bucket selected"),
501            ServerErrorKind::UnknownBucketName => write!(f, "unknown bucket name"),
502            ServerErrorKind::Access => write!(f, "access error"),
503            ServerErrorKind::Auth { msg } => write!(f, "auth error {msg}"),
504            ServerErrorKind::ConfigNotSet => write!(f, "config not set"),
505            ServerErrorKind::UnknownScopeName => write!(f, "scope name unknown"),
506            ServerErrorKind::UnknownCollectionName => write!(f, "collection name unknown"),
507            ServerErrorKind::Subdoc { error } => write!(f, "{error}"),
508            ServerErrorKind::UnknownStatus { status } => {
509                write!(f, "server status unexpected for operation: {status}")
510            }
511            ServerErrorKind::BadDelta => write!(f, "bad delta"),
512            ServerErrorKind::UnknownCommand => write!(f, "unknown command"),
513            ServerErrorKind::RangeError => write!(f, "range error"),
514            ServerErrorKind::RateLimitedNetworkIngress => {
515                write!(f, "rate limited: network ingress")
516            }
517            ServerErrorKind::RateLimitedNetworkEgress => write!(f, "rate limited: network egress"),
518            ServerErrorKind::RateLimitedMaxConnections => {
519                write!(f, "rate limited: max connections")
520            }
521            ServerErrorKind::RateLimitedMaxCommands => write!(f, "rate limited: max commands"),
522            ServerErrorKind::RateLimitedScopeSizeLimitExceeded => {
523                write!(f, "rate limited: scope size limit exceeded")
524            }
525            ServerErrorKind::NotSupported => write!(f, "not supported"),
526            ServerErrorKind::InternalError => write!(f, "internal error"),
527            ServerErrorKind::Busy => write!(f, "busy"),
528            ServerErrorKind::DurabilityInvalid => write!(f, "durability invalid"),
529            ServerErrorKind::DurabilityImpossible => write!(f, "durability impossible"),
530            ServerErrorKind::SyncWriteInProgress => write!(f, "sync write in progress"),
531            ServerErrorKind::SyncWriteAmbiguous => write!(f, "sync write ambiguous"),
532            ServerErrorKind::SyncWriteRecommitInProgress => {
533                write!(f, "sync write recommit in progress")
534            }
535            ServerErrorKind::RangeScanCancelled => write!(f, "range scan cancelled"),
536            ServerErrorKind::RangeScanVBUUIDNotEqual => write!(f, "range scan vbUUID not equal"),
537            ServerErrorKind::InvalidArgs => write!(f, "invalid args"),
538        }
539    }
540}
541
542#[derive(Clone, Debug, PartialEq, Eq, Hash)]
543#[non_exhaustive]
544pub struct SubdocError {
545    kind: SubdocErrorKind,
546    op_index: Option<u8>,
547}
548
549impl StdError for SubdocError {}
550
551impl SubdocError {
552    pub(crate) fn new(kind: SubdocErrorKind, op_index: impl Into<Option<u8>>) -> Self {
553        Self {
554            kind,
555            op_index: op_index.into(),
556        }
557    }
558
559    pub fn is_error_kind(&self, kind: SubdocErrorKind) -> bool {
560        self.kind == kind
561    }
562
563    pub fn kind(&self) -> &SubdocErrorKind {
564        &self.kind
565    }
566
567    pub fn op_index(&self) -> Option<u8> {
568        self.op_index
569    }
570}
571
572impl Display for SubdocError {
573    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
574        if let Some(op_index) = self.op_index {
575            let base_msg = format!("Subdoc error: {}, op_index: {}", self.kind, op_index);
576            write!(f, "{base_msg}")
577        } else {
578            let base_msg = format!("Subdoc error: {}", self.kind);
579            write!(f, "{base_msg}")
580        }
581    }
582}
583
584#[derive(Clone, Debug, Eq, Hash, PartialEq)]
585#[non_exhaustive]
586pub enum SubdocErrorKind {
587    PathNotFound,
588    PathMismatch,
589    PathInvalid,
590    PathTooBig,
591    DocTooDeep,
592    CantInsert,
593    NotJSON,
594    BadRange,
595    BadDelta,
596    PathExists,
597    ValueTooDeep,
598    InvalidCombo,
599    XattrInvalidFlagCombo,
600    XattrInvalidKeyCombo,
601    XattrUnknownMacro,
602    XattrUnknownVAttr,
603    XattrCannotModifyVAttr,
604    InvalidXattrOrder,
605    XattrUnknownVattrMacro,
606    CanOnlyReviveDeletedDocuments,
607    DeletedDocumentCantHaveValue,
608    UnknownStatus { status: Status },
609}
610
611impl Display for SubdocErrorKind {
612    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
613        match self {
614            SubdocErrorKind::PathNotFound => write!(f, "subdoc path not found"),
615            SubdocErrorKind::PathMismatch => write!(f, "subdoc path mismatch"),
616            SubdocErrorKind::PathInvalid => write!(f, "subdoc path invalid"),
617            SubdocErrorKind::PathTooBig => write!(f, "subdoc path too big"),
618            SubdocErrorKind::DocTooDeep => write!(f, "subdoc doc too deep"),
619            SubdocErrorKind::CantInsert => write!(f, "subdoc can't insert"),
620            SubdocErrorKind::NotJSON => write!(f, "subdoc not JSON"),
621            SubdocErrorKind::BadRange => write!(f, "subdoc bad range"),
622            SubdocErrorKind::BadDelta => write!(f, "subdoc bad delta"),
623            SubdocErrorKind::PathExists => write!(f, "subdoc path exists"),
624            SubdocErrorKind::ValueTooDeep => write!(f, "subdoc value too deep"),
625            SubdocErrorKind::InvalidCombo => write!(f, "subdoc invalid combo"),
626            SubdocErrorKind::XattrInvalidFlagCombo => write!(f, "subdoc xattr invalid flag combo"),
627            SubdocErrorKind::XattrInvalidKeyCombo => write!(f, "subdoc xattr invalid key combo"),
628            SubdocErrorKind::XattrUnknownMacro => write!(f, "subdoc xattr unknown macro"),
629            SubdocErrorKind::XattrUnknownVAttr => write!(f, "subdoc xattr unknown vattr"),
630            SubdocErrorKind::XattrCannotModifyVAttr => {
631                write!(f, "subdoc xattr cannot modify vattr")
632            }
633            SubdocErrorKind::InvalidXattrOrder => write!(f, "subdoc invalid xattr order"),
634            SubdocErrorKind::XattrUnknownVattrMacro => {
635                write!(f, "subdoc xattr unknown vattr macro")
636            }
637            SubdocErrorKind::CanOnlyReviveDeletedDocuments => {
638                write!(f, "subdoc can only revive deleted documents")
639            }
640            SubdocErrorKind::DeletedDocumentCantHaveValue => {
641                write!(f, "subdoc deleted document can't have value")
642            }
643            SubdocErrorKind::UnknownStatus { status } => write!(
644                f,
645                "subdoc unknown status unexpected for operation: {status}"
646            ),
647        }
648    }
649}
650
651#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
652#[non_exhaustive]
653pub enum CancellationErrorKind {
654    Timeout,
655    RequestCancelled,
656    ClosedInFlight,
657}
658
659impl Display for CancellationErrorKind {
660    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
661        let txt = match self {
662            CancellationErrorKind::Timeout => "Timeout",
663            CancellationErrorKind::RequestCancelled => "Request cancelled",
664            CancellationErrorKind::ClosedInFlight => "Closed in flight",
665        };
666
667        write!(f, "{txt}")
668    }
669}
670
671impl<E> From<E> for Error
672where
673    ErrorKind: From<E>,
674{
675    fn from(err: E) -> Self {
676        Self {
677            inner: ErrorImpl {
678                kind: Box::new(ErrorKind::from(err)),
679                source: None,
680            },
681        }
682    }
683}
684
685impl From<ServerError> for Error {
686    fn from(value: ServerError) -> Self {
687        Self {
688            inner: ErrorImpl {
689                kind: Box::new(ErrorKind::Server(value)),
690                source: None,
691            },
692        }
693    }
694}
695
696impl From<ResourceError> for Error {
697    fn from(value: ResourceError) -> Self {
698        Self {
699            inner: ErrorImpl {
700                kind: Box::new(ErrorKind::Resource(value)),
701                source: None,
702            },
703        }
704    }
705}
706
707impl From<io::Error> for Error {
708    fn from(value: io::Error) -> Self {
709        Self {
710            inner: ErrorImpl {
711                kind: Box::new(ErrorKind::Io),
712                source: Some(Box::new(value)),
713            },
714        }
715    }
716}
717
718#[derive(Deserialize, Clone, Debug, PartialEq, Eq, Default)]
719struct ServerErrorContextJsonContext {
720    #[serde(alias = "context")]
721    context: Option<String>,
722    #[serde(alias = "ref")]
723    pub error_ref: Option<String>,
724}
725
726#[derive(Deserialize, Clone, Debug, PartialEq, Eq)]
727struct ServerErrorContextJson {
728    #[serde(alias = "error", default)]
729    error: ServerErrorContextJsonContext,
730    #[serde(alias = "manifest_uid")]
731    pub manifest_rev: Option<String>,
732}