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