Skip to main content

couchbase_core/queryx/
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;
20use crate::tracingcomponent::MetricsName;
21use http::StatusCode;
22use serde_json::Value;
23use std::collections::HashMap;
24use std::error::Error as StdError;
25use std::fmt::{Display, Formatter};
26
27pub type Result<T> = std::result::Result<T, Error>;
28
29#[derive(Debug, Clone, PartialEq)]
30pub struct Error {
31    inner: ErrorImpl,
32}
33
34impl Display for Error {
35    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
36        write!(f, "{}", self.inner.kind)
37    }
38}
39
40impl StdError for Error {}
41
42impl Error {
43    pub(crate) fn new_server_error(e: ServerError) -> Error {
44        Self {
45            inner: ErrorImpl {
46                kind: Box::new(ErrorKind::Server(e)),
47            },
48        }
49    }
50
51    pub(crate) fn new_resource_error(e: ResourceError) -> Error {
52        Self {
53            inner: ErrorImpl {
54                kind: Box::new(ErrorKind::Resource(e)),
55            },
56        }
57    }
58
59    pub(crate) fn new_message_error(
60        msg: impl Into<String>,
61        endpoint: impl Into<Option<String>>,
62        statement: impl Into<Option<String>>,
63        client_context_id: impl Into<Option<String>>,
64    ) -> Error {
65        Self {
66            inner: ErrorImpl {
67                kind: Box::new(ErrorKind::Message {
68                    msg: msg.into(),
69                    endpoint: endpoint.into(),
70                    statement: statement.into(),
71                    client_context_id: client_context_id.into(),
72                }),
73            },
74        }
75    }
76
77    pub(crate) fn new_encoding_error(msg: impl Into<String>) -> Error {
78        Self {
79            inner: ErrorImpl {
80                kind: Box::new(ErrorKind::Encoding { msg: msg.into() }),
81            },
82        }
83    }
84
85    pub(crate) fn new_invalid_argument_error(
86        msg: impl Into<String>,
87        arg: impl Into<Option<String>>,
88    ) -> Self {
89        Self {
90            inner: ErrorImpl {
91                kind: Box::new(ErrorKind::InvalidArgument {
92                    msg: msg.into(),
93                    arg: arg.into(),
94                }),
95            },
96        }
97    }
98
99    pub(crate) fn new_http_error(
100        error: httpx::error::Error,
101        endpoint: impl Into<String>,
102        statement: impl Into<Option<String>>,
103        client_context_id: impl Into<Option<String>>,
104    ) -> Self {
105        Self {
106            inner: ErrorImpl {
107                kind: Box::new(ErrorKind::Http {
108                    error,
109                    endpoint: endpoint.into(),
110                    statement: statement.into(),
111                    client_context_id: client_context_id.into(),
112                }),
113            },
114        }
115    }
116
117    pub fn kind(&self) -> &ErrorKind {
118        &self.inner.kind
119    }
120}
121
122#[derive(Debug, Clone)]
123struct ErrorImpl {
124    kind: Box<ErrorKind>,
125}
126
127impl PartialEq for ErrorImpl {
128    fn eq(&self, other: &Self) -> bool {
129        self.kind == other.kind
130    }
131}
132
133#[derive(Clone, Debug, PartialEq, Eq)]
134#[non_exhaustive]
135pub enum ErrorKind {
136    Server(ServerError),
137    #[non_exhaustive]
138    Http {
139        error: httpx::error::Error,
140        endpoint: String,
141        statement: Option<String>,
142        client_context_id: Option<String>,
143    },
144    Resource(ResourceError),
145    #[non_exhaustive]
146    Message {
147        msg: String,
148        endpoint: Option<String>,
149        statement: Option<String>,
150        client_context_id: Option<String>,
151    },
152    #[non_exhaustive]
153    InvalidArgument {
154        msg: String,
155        arg: Option<String>,
156    },
157    #[non_exhaustive]
158    Encoding {
159        msg: String,
160    },
161}
162
163impl Display for ErrorKind {
164    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
165        match self {
166            ErrorKind::Server(e) => write!(f, "{e}"),
167            ErrorKind::Resource(e) => write!(f, "{e}"),
168            ErrorKind::InvalidArgument { msg, arg } => {
169                let base_msg = format!("invalid argument: {msg}");
170                if let Some(arg) = arg {
171                    write!(f, "{base_msg}, arg: {arg}")
172                } else {
173                    write!(f, "{base_msg}")
174                }
175            }
176            ErrorKind::Encoding { msg } => write!(f, "encoding error: {msg}"),
177            ErrorKind::Http {
178                error,
179                endpoint,
180                statement,
181                client_context_id,
182            } => {
183                write!(f, "http error {error}: endpoint: {endpoint}")?;
184                if let Some(statement) = statement {
185                    write!(f, ", statement: {statement}")?;
186                }
187                if let Some(client_context_id) = client_context_id {
188                    write!(f, ", client context id: {client_context_id}")?;
189                }
190                Ok(())
191            }
192            ErrorKind::Message {
193                msg,
194                endpoint,
195                statement,
196                client_context_id,
197            } => {
198                write!(f, "{msg}")?;
199                if let Some(endpoint) = endpoint {
200                    write!(f, ", endpoint: {endpoint}")?;
201                }
202                if let Some(statement) = statement {
203                    write!(f, ", statement: {statement}")?;
204                }
205                if let Some(client_context_id) = client_context_id {
206                    write!(f, ", client context id: {client_context_id}")?;
207                }
208                Ok(())
209            }
210        }
211    }
212}
213
214#[derive(Clone, Debug, PartialEq, Eq)]
215pub struct ServerError {
216    kind: ServerErrorKind,
217
218    endpoint: String,
219    status_code: StatusCode,
220    code: u32,
221    msg: String,
222    retry: bool,
223
224    statement: Option<String>,
225    client_context_id: Option<String>,
226
227    all_error_descs: Vec<ErrorDesc>,
228}
229
230impl ServerError {
231    pub(crate) fn new(
232        kind: ServerErrorKind,
233        endpoint: impl Into<String>,
234        status_code: StatusCode,
235        code: u32,
236        retry: bool,
237        msg: impl Into<String>,
238    ) -> Self {
239        Self {
240            kind,
241            endpoint: endpoint.into(),
242            status_code,
243            code,
244            msg: msg.into(),
245            retry,
246            statement: None,
247            client_context_id: None,
248            all_error_descs: vec![],
249        }
250    }
251
252    pub fn kind(&self) -> &ServerErrorKind {
253        &self.kind
254    }
255
256    pub fn endpoint(&self) -> &str {
257        &self.endpoint
258    }
259
260    pub fn statement(&self) -> Option<&str> {
261        self.statement.as_deref()
262    }
263
264    pub fn status_code(&self) -> StatusCode {
265        self.status_code
266    }
267
268    pub fn client_context_id(&self) -> Option<&str> {
269        self.client_context_id.as_deref()
270    }
271
272    pub fn code(&self) -> u32 {
273        self.code
274    }
275
276    pub fn msg(&self) -> &str {
277        &self.msg
278    }
279
280    pub fn retry(&self) -> bool {
281        self.retry
282    }
283
284    pub fn all_error_descs(&self) -> &[ErrorDesc] {
285        &self.all_error_descs
286    }
287
288    pub(crate) fn with_statement(mut self, statement: impl Into<String>) -> Self {
289        self.statement = Some(statement.into());
290        self
291    }
292
293    pub(crate) fn with_client_context_id(mut self, client_context_id: impl Into<String>) -> Self {
294        self.client_context_id = Some(client_context_id.into());
295        self
296    }
297
298    pub(crate) fn with_error_descs(mut self, error_descs: Vec<ErrorDesc>) -> Self {
299        self.all_error_descs = error_descs;
300        self
301    }
302}
303
304impl Display for ServerError {
305    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
306        write!(
307            f,
308            "server error of kind: {} code: {}, msg: {}",
309            self.kind, self.code, self.msg
310        )?;
311
312        if let Some(client_context_id) = &self.client_context_id {
313            write!(f, ", client context id: {client_context_id}")?;
314        }
315        if let Some(statement) = &self.statement {
316            write!(f, ", statement: {statement}")?;
317        }
318
319        write!(
320            f,
321            ", endpoint: {},  status code: {}",
322            self.endpoint, self.status_code
323        )?;
324
325        if !self.all_error_descs.is_empty() {
326            write!(f, ", all error descriptions: {:?}", self.all_error_descs)?;
327        }
328
329        Ok(())
330    }
331}
332
333#[derive(Clone, Debug, PartialEq, Eq)]
334#[non_exhaustive]
335pub struct ResourceError {
336    cause: ServerError,
337    bucket_name: Option<String>,
338    scope_name: Option<String>,
339    collection_name: Option<String>,
340    index_name: Option<String>,
341}
342
343impl ResourceError {
344    pub(crate) fn new(cause: ServerError) -> Self {
345        match cause.kind {
346            ServerErrorKind::CollectionNotFound => Self::parse_resource_not_found(cause),
347            ServerErrorKind::ScopeNotFound => Self::parse_resource_not_found(cause),
348            ServerErrorKind::AuthenticationFailure => Self::parse_auth_failure(cause),
349            ServerErrorKind::IndexNotFound => Self::parse_index_not_found_or_exists(cause),
350            ServerErrorKind::IndexExists => Self::parse_index_not_found_or_exists(cause),
351            _ => Self {
352                cause,
353                bucket_name: None,
354                scope_name: None,
355                collection_name: None,
356                index_name: None,
357            },
358        }
359    }
360
361    pub fn cause(&self) -> &ServerError {
362        &self.cause
363    }
364
365    pub fn bucket_name(&self) -> Option<&str> {
366        self.bucket_name.as_deref()
367    }
368
369    pub fn scope_name(&self) -> Option<&str> {
370        self.scope_name.as_deref()
371    }
372
373    pub fn collection_name(&self) -> Option<&str> {
374        self.collection_name.as_deref()
375    }
376
377    pub fn index_name(&self) -> Option<&str> {
378        self.index_name.as_deref()
379    }
380
381    fn parse_index_not_found_or_exists(cause: ServerError) -> ResourceError {
382        let msg = cause.msg.clone();
383        let mut fields = msg.split_whitespace();
384
385        // msg for not found is of the form - "Index Not Found - cause: GSI index testingIndex not found."
386        // msg for index exists is of the form - "The index NewIndex already exists."
387        while let Some(field) = fields.next() {
388            if field == "index" {
389                return ResourceError {
390                    cause,
391                    bucket_name: None,
392                    scope_name: None,
393                    collection_name: None,
394                    index_name: Some(fields.next().unwrap().to_string()),
395                };
396            }
397        }
398
399        ResourceError {
400            cause,
401            bucket_name: None,
402            scope_name: None,
403            collection_name: None,
404            index_name: None,
405        }
406    }
407
408    fn parse_resource_not_found(cause: ServerError) -> ResourceError {
409        let msg = cause.msg.clone();
410        let mut fields = msg.split_whitespace();
411        // Resource path is of the form bucket:bucket.scope.collection
412        let path = fields.find(|f| f.contains('.') && f.contains(':'));
413
414        if let Some(p) = path {
415            if let Some(trimmed_path) = p.split(':').nth(1) {
416                let fields: Vec<&str> = trimmed_path.split('.').collect();
417
418                if cause.kind == ServerErrorKind::ScopeNotFound {
419                    // Bucket names are the only one that can contain `.`, which is why we need to reconstruct the name if split
420                    let bucket_name = fields[0..fields.len() - 1].join(".");
421                    let scope_name = fields[fields.len() - 1];
422
423                    return ResourceError {
424                        cause,
425                        bucket_name: Some(bucket_name),
426                        scope_name: Some(scope_name.to_string()),
427                        collection_name: None,
428                        index_name: None,
429                    };
430                } else if cause.kind == ServerErrorKind::CollectionNotFound {
431                    // Bucket names are the only one that can contain `.`, which is why we need to reconstruct the name if split
432                    let bucket_name = fields[0..fields.len() - 2].join(".");
433                    let scope_name = fields[fields.len() - 2];
434                    let collection_name = fields[fields.len() - 1];
435
436                    return ResourceError {
437                        cause,
438                        bucket_name: Some(bucket_name),
439                        scope_name: Some(scope_name.to_string()),
440                        collection_name: Some(collection_name.to_string()),
441                        index_name: None,
442                    };
443                }
444            }
445        }
446
447        ResourceError {
448            cause,
449            bucket_name: None,
450            scope_name: None,
451            collection_name: None,
452            index_name: None,
453        }
454    }
455
456    fn parse_auth_failure(cause: ServerError) -> Self {
457        let msg = &cause.msg;
458        let mut fields = msg.split_whitespace();
459        let path = fields.find(|f| f.contains(':'));
460
461        if let Some(p) = path {
462            if let Some(trimmed_path) = p.split(':').nth(1) {
463                let (bucket_name, scope_name, collection_name) = if trimmed_path.contains('`') {
464                    // trimmedPath will have the form "`bucket.name`" or "`bucket.name`.scope.collection" so the first element of fields
465                    // will be the empty string
466                    let fields: Vec<&str> = trimmed_path.split('`').collect();
467
468                    let bucket_name = fields[1];
469
470                    let (scope_name, collection_name) = if fields[2].is_empty() {
471                        (None, None)
472                    } else {
473                        // scope_and_col is of the form ".scope.collection" meaning fields[1] is empty and the names are in fields[1] and
474                        // fields[2]
475                        let scope_and_col = fields[2];
476                        let fields: Vec<&str> = scope_and_col.split('.').collect();
477                        let (scope, collection) = if fields.len() >= 3 {
478                            (Some(fields[1].to_string()), Some(fields[2].to_string()))
479                        } else {
480                            (None, None)
481                        };
482
483                        (scope, collection)
484                    };
485
486                    (Some(bucket_name.to_string()), scope_name, collection_name)
487                } else {
488                    let fields: Vec<&str> = trimmed_path.split('.').collect();
489
490                    let bucket_name = fields[0];
491
492                    let (scope_name, collection_name) = if fields.len() >= 3 {
493                        (Some(fields[1].to_string()), Some(fields[2].to_string()))
494                    } else {
495                        (None, None)
496                    };
497
498                    (Some(bucket_name.to_string()), scope_name, collection_name)
499                };
500
501                return ResourceError {
502                    cause,
503                    bucket_name,
504                    scope_name,
505                    collection_name,
506                    index_name: None,
507                };
508            }
509        }
510
511        ResourceError {
512            cause,
513            bucket_name: None,
514            scope_name: None,
515            collection_name: None,
516            index_name: None,
517        }
518    }
519}
520
521impl Display for ResourceError {
522    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
523        write!(f, "resource error caused by: {}", self.cause)?;
524
525        if let Some(bucket_name) = &self.bucket_name {
526            write!(f, ", bucket: {bucket_name}")?;
527        }
528        if let Some(scope_name) = &self.scope_name {
529            write!(f, ", scope: {scope_name}")?;
530        }
531        if let Some(collection_name) = &self.collection_name {
532            write!(f, ", collection: {collection_name}")?;
533        }
534        if let Some(index_name) = &self.index_name {
535            write!(f, ", index: {index_name}")?;
536        }
537
538        Ok(())
539    }
540}
541
542impl StdError for ResourceError {}
543
544#[derive(Clone, Debug, PartialEq, Eq)]
545#[non_exhaustive]
546pub struct ErrorDesc {
547    kind: ServerErrorKind,
548
549    code: u32,
550    message: String,
551    retry: bool,
552    reason: HashMap<String, Value>,
553}
554
555impl ErrorDesc {
556    pub fn new(
557        kind: ServerErrorKind,
558        code: u32,
559        message: String,
560        retry: bool,
561        reason: HashMap<String, Value>,
562    ) -> Self {
563        Self {
564            kind,
565            code,
566            message,
567            retry,
568            reason,
569        }
570    }
571
572    pub fn kind(&self) -> &ServerErrorKind {
573        &self.kind
574    }
575
576    pub fn code(&self) -> u32 {
577        self.code
578    }
579
580    pub fn message(&self) -> &str {
581        &self.message
582    }
583
584    pub fn retry(&self) -> bool {
585        self.retry
586    }
587
588    pub fn reason(&self) -> &HashMap<String, Value> {
589        &self.reason
590    }
591}
592
593impl Display for ErrorDesc {
594    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
595        write!(
596            f,
597            "error description of kind: {}, code: {}, message: {}, retry: {}, reason: {:?}",
598            self.kind, self.code, self.message, self.retry, self.reason
599        )
600    }
601}
602
603#[derive(Clone, Debug, PartialEq, Eq)]
604#[non_exhaustive]
605pub enum ServerErrorKind {
606    ParsingFailure,
607    Internal,
608    AuthenticationFailure,
609    CasMismatch,
610    DocNotFound,
611    DocExists,
612    PlanningFailure,
613    IndexFailure,
614    PreparedStatementFailure,
615    DMLFailure,
616    Timeout,
617    IndexExists,
618    IndexNotFound,
619    WriteInReadOnlyMode,
620    ScopeNotFound,
621    CollectionNotFound,
622    InvalidArgument { argument: String, reason: String },
623    BuildAlreadyInProgress,
624    Unknown,
625}
626
627impl Display for ServerErrorKind {
628    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
629        match self {
630            ServerErrorKind::ParsingFailure => write!(f, "parsing failure"),
631            ServerErrorKind::Internal => write!(f, "internal server error"),
632            ServerErrorKind::AuthenticationFailure => write!(f, "authentication failure"),
633            ServerErrorKind::CasMismatch => write!(f, "cas mismatch"),
634            ServerErrorKind::DocNotFound => write!(f, "doc not found"),
635            ServerErrorKind::DocExists => write!(f, "doc exists"),
636            ServerErrorKind::PlanningFailure => write!(f, "planning failure"),
637            ServerErrorKind::IndexFailure => write!(f, "index failure"),
638            ServerErrorKind::PreparedStatementFailure => write!(f, "prepared statement failure"),
639            ServerErrorKind::DMLFailure => write!(
640                f,
641                "data service returned an error during execution of DML statement"
642            ),
643            ServerErrorKind::Timeout => write!(f, "server timeout"),
644            ServerErrorKind::IndexExists => write!(f, "index exists"),
645            ServerErrorKind::IndexNotFound => write!(f, "index not found"),
646            ServerErrorKind::WriteInReadOnlyMode => {
647                write!(f, "write statement used in a read-only query")
648            }
649            ServerErrorKind::ScopeNotFound => write!(f, "scope not found"),
650            ServerErrorKind::CollectionNotFound => write!(f, "collection not found"),
651            ServerErrorKind::InvalidArgument { argument, reason } => write!(
652                f,
653                "server invalid argument: (argument: {argument}, reason: {reason})"
654            ),
655            ServerErrorKind::BuildAlreadyInProgress => write!(f, "build already in progress"),
656            ServerErrorKind::Unknown => write!(f, "unknown query error"),
657        }
658    }
659}
660
661impl Error {
662    pub fn is_parsing_failure(&self) -> bool {
663        matches!(
664            self.kind(),
665            ErrorKind::Server(ServerError {
666                kind: ServerErrorKind::ParsingFailure,
667                ..
668            })
669        )
670    }
671
672    pub fn is_internal(&self) -> bool {
673        matches!(
674            self.kind(),
675            ErrorKind::Server(ServerError {
676                kind: ServerErrorKind::Internal,
677                ..
678            })
679        )
680    }
681
682    pub fn is_authentication_failure(&self) -> bool {
683        matches!(
684            self.kind(),
685            ErrorKind::Server(ServerError {
686                kind: ServerErrorKind::AuthenticationFailure,
687                ..
688            })
689        )
690    }
691
692    pub fn is_cas_mismatch(&self) -> bool {
693        matches!(
694            self.kind(),
695            ErrorKind::Server(ServerError {
696                kind: ServerErrorKind::CasMismatch,
697                ..
698            })
699        )
700    }
701
702    pub fn is_doc_not_found(&self) -> bool {
703        matches!(
704            self.kind(),
705            ErrorKind::Server(ServerError {
706                kind: ServerErrorKind::DocNotFound,
707                ..
708            })
709        )
710    }
711
712    pub fn is_doc_exists(&self) -> bool {
713        matches!(
714            self.kind(),
715            ErrorKind::Server(ServerError {
716                kind: ServerErrorKind::DocExists,
717                ..
718            })
719        )
720    }
721
722    pub fn is_planning_failure(&self) -> bool {
723        matches!(
724            self.kind(),
725            ErrorKind::Server(ServerError {
726                kind: ServerErrorKind::PlanningFailure,
727                ..
728            })
729        )
730    }
731
732    pub fn is_index_failure(&self) -> bool {
733        matches!(
734            self.kind(),
735            ErrorKind::Server(ServerError {
736                kind: ServerErrorKind::IndexFailure,
737                ..
738            })
739        )
740    }
741
742    pub fn is_prepared_statement_failure(&self) -> bool {
743        matches!(
744            self.kind(),
745            ErrorKind::Server(ServerError {
746                kind: ServerErrorKind::PreparedStatementFailure,
747                ..
748            })
749        )
750    }
751
752    pub fn is_dml_failure(&self) -> bool {
753        matches!(
754            self.kind(),
755            ErrorKind::Server(ServerError {
756                kind: ServerErrorKind::DMLFailure,
757                ..
758            })
759        )
760    }
761
762    pub fn is_server_timeout(&self) -> bool {
763        matches!(
764            self.kind(),
765            ErrorKind::Server(ServerError {
766                kind: ServerErrorKind::Timeout,
767                ..
768            })
769        )
770    }
771
772    pub fn is_write_in_read_only_mode(&self) -> bool {
773        matches!(
774            self.kind(),
775            ErrorKind::Server(ServerError {
776                kind: ServerErrorKind::WriteInReadOnlyMode,
777                ..
778            })
779        )
780    }
781
782    pub fn is_invalid_argument(&self) -> bool {
783        matches!(
784            self.kind(),
785            ErrorKind::Server(ServerError {
786                kind: ServerErrorKind::InvalidArgument { .. },
787                ..
788            })
789        )
790    }
791
792    pub fn is_build_already_in_progress(&self) -> bool {
793        matches!(
794            self.kind(),
795            ErrorKind::Server(ServerError {
796                kind: ServerErrorKind::BuildAlreadyInProgress,
797                ..
798            })
799        )
800    }
801
802    pub fn is_scope_not_found(&self) -> bool {
803        matches!(
804            self.kind(),
805            ErrorKind::Resource(ResourceError {
806                cause: ServerError {
807                    kind: ServerErrorKind::ScopeNotFound,
808                    ..
809                },
810                ..
811            })
812        ) || matches!(
813            self.kind(),
814            ErrorKind::Server(ServerError {
815                kind: ServerErrorKind::ScopeNotFound,
816                ..
817            })
818        )
819    }
820
821    pub fn is_collection_not_found(&self) -> bool {
822        matches!(
823            self.kind(),
824            ErrorKind::Resource(ResourceError {
825                cause: ServerError {
826                    kind: ServerErrorKind::CollectionNotFound,
827                    ..
828                },
829                ..
830            })
831        ) || matches!(
832            self.kind(),
833            ErrorKind::Server(ServerError {
834                kind: ServerErrorKind::CollectionNotFound,
835                ..
836            })
837        )
838    }
839
840    pub fn is_index_not_found(&self) -> bool {
841        matches!(
842            self.kind(),
843            ErrorKind::Resource(ResourceError {
844                cause: ServerError {
845                    kind: ServerErrorKind::IndexNotFound,
846                    ..
847                },
848                ..
849            })
850        ) || matches!(
851            self.kind(),
852            ErrorKind::Server(ServerError {
853                kind: ServerErrorKind::IndexNotFound,
854                ..
855            })
856        )
857    }
858
859    pub fn is_index_exists(&self) -> bool {
860        matches!(
861            self.kind(),
862            ErrorKind::Resource(ResourceError {
863                cause: ServerError {
864                    kind: ServerErrorKind::IndexExists,
865                    ..
866                },
867                ..
868            })
869        ) || matches!(
870            self.kind(),
871            ErrorKind::Server(ServerError {
872                kind: ServerErrorKind::IndexExists,
873                ..
874            })
875        )
876    }
877}
878
879impl MetricsName for Error {
880    fn metrics_name(&self) -> &'static str {
881        match self.kind() {
882            ErrorKind::Server(e) => e.kind().metrics_name(),
883            ErrorKind::Resource(e) => e.cause().kind().metrics_name(),
884            ErrorKind::Http { error, .. } => error.metrics_name(),
885            ErrorKind::Message { .. } => "queryx._OTHER",
886            ErrorKind::InvalidArgument { .. } => "queryx.InvalidArgument",
887            ErrorKind::Encoding { .. } => "queryx.Encoding",
888        }
889    }
890}
891
892impl MetricsName for ServerErrorKind {
893    fn metrics_name(&self) -> &'static str {
894        match self {
895            ServerErrorKind::ParsingFailure => "queryx.ParsingFailure",
896            ServerErrorKind::Internal => "queryx.Internal",
897            ServerErrorKind::AuthenticationFailure => "queryx.AuthenticationFailure",
898            ServerErrorKind::CasMismatch => "queryx.CasMismatch",
899            ServerErrorKind::DocNotFound => "queryx.DocNotFound",
900            ServerErrorKind::DocExists => "queryx.DocExists",
901            ServerErrorKind::PlanningFailure => "queryx.PlanningFailure",
902            ServerErrorKind::IndexFailure => "queryx.IndexFailure",
903            ServerErrorKind::PreparedStatementFailure => "queryx.PreparedStatementFailure",
904            ServerErrorKind::DMLFailure => "queryx.DMLFailure",
905            ServerErrorKind::Timeout => "queryx.Timeout",
906            ServerErrorKind::IndexExists => "queryx.IndexExists",
907            ServerErrorKind::IndexNotFound => "queryx.IndexNotFound",
908            ServerErrorKind::WriteInReadOnlyMode => "queryx.WriteInReadOnlyMode",
909            ServerErrorKind::ScopeNotFound => "queryx.ScopeNotFound",
910            ServerErrorKind::CollectionNotFound => "queryx.CollectionNotFound",
911            ServerErrorKind::InvalidArgument { .. } => "queryx.InvalidArgument",
912            ServerErrorKind::BuildAlreadyInProgress => "queryx.BuildAlreadyInProgress",
913            ServerErrorKind::Unknown => "queryx._OTHER",
914        }
915    }
916}