Skip to main content

couchbase_core/searchx/
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 std::error::Error as StdError;
23use std::fmt::{Display, Formatter};
24
25pub type Result<T> = std::result::Result<T, Error>;
26
27#[derive(Debug, Clone, PartialEq)]
28pub struct Error {
29    inner: ErrorImpl,
30}
31
32impl Display for Error {
33    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
34        write!(f, "{}", self.inner.kind)
35    }
36}
37
38impl StdError for Error {}
39
40impl Error {
41    pub(crate) fn new_server_error(e: ServerError) -> Error {
42        Self {
43            inner: ErrorImpl {
44                kind: Box::new(ErrorKind::Server(e)),
45            },
46        }
47    }
48
49    pub(crate) fn new_message_error(
50        msg: impl Into<String>,
51        endpoint: impl Into<Option<String>>,
52    ) -> Error {
53        Self {
54            inner: ErrorImpl {
55                kind: Box::new(ErrorKind::Message {
56                    msg: msg.into(),
57                    endpoint: endpoint.into(),
58                }),
59            },
60        }
61    }
62
63    pub(crate) fn new_encoding_error(msg: impl Into<String>) -> Error {
64        Self {
65            inner: ErrorImpl {
66                kind: Box::new(ErrorKind::Encoding { msg: msg.into() }),
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            },
82        }
83    }
84
85    pub(crate) fn new_http_error(error: httpx::error::Error, endpoint: impl Into<String>) -> Self {
86        Self {
87            inner: ErrorImpl {
88                kind: Box::new(ErrorKind::Http {
89                    error,
90                    endpoint: endpoint.into(),
91                }),
92            },
93        }
94    }
95
96    pub(crate) fn new_unsupported_feature_error(feature: String) -> Self {
97        Self {
98            inner: ErrorImpl {
99                kind: Box::new(ErrorKind::UnsupportedFeature { feature }),
100            },
101        }
102    }
103
104    pub fn kind(&self) -> &ErrorKind {
105        &self.inner.kind
106    }
107}
108
109#[derive(Debug, Clone)]
110struct ErrorImpl {
111    kind: Box<ErrorKind>,
112}
113
114impl PartialEq for ErrorImpl {
115    fn eq(&self, other: &Self) -> bool {
116        self.kind == other.kind
117    }
118}
119
120#[derive(Clone, Debug, PartialEq, Eq)]
121#[non_exhaustive]
122pub enum ErrorKind {
123    Server(ServerError),
124    #[non_exhaustive]
125    Http {
126        error: httpx::error::Error,
127        endpoint: String,
128    },
129    #[non_exhaustive]
130    Message {
131        msg: String,
132        endpoint: Option<String>,
133    },
134    #[non_exhaustive]
135    InvalidArgument {
136        msg: String,
137        arg: Option<String>,
138    },
139    #[non_exhaustive]
140    Encoding {
141        msg: String,
142    },
143    UnsupportedFeature {
144        feature: String,
145    },
146}
147
148impl Display for ErrorKind {
149    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
150        match self {
151            ErrorKind::Server(e) => write!(f, "{e}"),
152            ErrorKind::Http { error, endpoint } => {
153                write!(f, "http error {error}: endpoint {endpoint}")
154            }
155            ErrorKind::Message { msg, endpoint } => {
156                if let Some(endpoint) = endpoint {
157                    write!(f, "message error for endpoint {endpoint}: {msg}")
158                } else {
159                    write!(f, "message error: {msg}")
160                }
161            }
162            ErrorKind::InvalidArgument { msg, arg } => {
163                if let Some(arg) = arg {
164                    write!(f, "invalid argument error for argument {arg}: {msg}")
165                } else {
166                    write!(f, "invalid argument error: {msg}")
167                }
168            }
169            ErrorKind::Encoding { msg } => write!(f, "encoding error: {msg}"),
170            ErrorKind::UnsupportedFeature { feature } => {
171                write!(f, "unsupported feature: {feature}")
172            }
173        }
174    }
175}
176
177#[derive(Clone, Debug, PartialEq, Eq)]
178pub struct ServerError {
179    kind: ServerErrorKind,
180
181    index_name: String,
182
183    error_text: String,
184    endpoint: String,
185    status_code: StatusCode,
186}
187
188impl Display for ServerError {
189    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
190        write!(
191            f,
192            "server error for index {} at endpoint {}, status code: {}: {}, error text: {}",
193            self.index_name, self.endpoint, self.status_code, self.kind, self.error_text
194        )
195    }
196}
197
198impl ServerError {
199    pub(crate) fn new(
200        kind: ServerErrorKind,
201        index_name: impl Into<String>,
202        error_text: impl Into<String>,
203        endpoint: impl Into<String>,
204        status_code: StatusCode,
205    ) -> Self {
206        Self {
207            kind,
208            error_text: error_text.into(),
209            index_name: index_name.into(),
210            endpoint: endpoint.into(),
211            status_code,
212        }
213    }
214
215    pub fn kind(&self) -> &ServerErrorKind {
216        &self.kind
217    }
218
219    pub fn index_name(&self) -> &str {
220        &self.index_name
221    }
222
223    pub fn endpoint(&self) -> &str {
224        &self.endpoint
225    }
226
227    pub fn status_code(&self) -> StatusCode {
228        self.status_code
229    }
230
231    pub fn error_text(&self) -> &str {
232        &self.error_text
233    }
234}
235
236#[derive(Clone, Debug, PartialEq, Eq)]
237#[non_exhaustive]
238pub enum ServerErrorKind {
239    Internal,
240    AuthenticationFailure,
241    IndexExists,
242    IndexNotFound,
243    UnknownIndexType,
244    SourceTypeIncorrect,
245    SourceNotFound,
246    NoIndexPartitionsPlanned,
247    NoIndexPartitionsFound,
248    UnsupportedFeature,
249    RateLimitedFailure,
250    Unknown,
251}
252
253impl Display for ServerErrorKind {
254    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
255        match self {
256            ServerErrorKind::Internal => write!(f, "internal server error"),
257            ServerErrorKind::AuthenticationFailure => write!(f, "authentication failure"),
258            ServerErrorKind::IndexExists => write!(f, "index exists"),
259            ServerErrorKind::IndexNotFound => write!(f, "index not found"),
260            ServerErrorKind::UnknownIndexType => write!(f, "unknown index type"),
261            ServerErrorKind::SourceTypeIncorrect => write!(f, "source type incorrect"),
262            ServerErrorKind::SourceNotFound => write!(f, "source not found"),
263            ServerErrorKind::NoIndexPartitionsPlanned => write!(f, "no index partitions planned"),
264            ServerErrorKind::NoIndexPartitionsFound => write!(f, "no index partitions found"),
265            ServerErrorKind::UnsupportedFeature => write!(f, "unsupported feature"),
266            ServerErrorKind::RateLimitedFailure => write!(f, "rate limited failure"),
267            ServerErrorKind::Unknown => write!(f, "unknown error"),
268        }
269    }
270}
271
272impl MetricsName for Error {
273    fn metrics_name(&self) -> &'static str {
274        match self.kind() {
275            ErrorKind::Server(e) => e.kind().metrics_name(),
276            ErrorKind::Http { error, .. } => error.metrics_name(),
277            ErrorKind::Message { .. } => "searchx._OTHER",
278            ErrorKind::InvalidArgument { .. } => "searchx.InvalidArgument",
279            ErrorKind::Encoding { .. } => "searchx.Encoding",
280            ErrorKind::UnsupportedFeature { .. } => "searchx.UnsupportedFeature",
281        }
282    }
283}
284
285impl MetricsName for ServerErrorKind {
286    fn metrics_name(&self) -> &'static str {
287        match self {
288            ServerErrorKind::Internal => "searchx.Internal",
289            ServerErrorKind::AuthenticationFailure => "searchx.AuthenticationFailure",
290            ServerErrorKind::IndexExists => "searchx.IndexExists",
291            ServerErrorKind::IndexNotFound => "searchx.IndexNotFound",
292            ServerErrorKind::UnknownIndexType => "searchx.UnknownIndexType",
293            ServerErrorKind::SourceTypeIncorrect => "searchx.SourceTypeIncorrect",
294            ServerErrorKind::SourceNotFound => "searchx.SourceNotFound",
295            ServerErrorKind::NoIndexPartitionsPlanned => "searchx.NoIndexPartitionsPlanned",
296            ServerErrorKind::NoIndexPartitionsFound => "searchx.NoIndexPartitionsFound",
297            ServerErrorKind::UnsupportedFeature => "searchx.UnsupportedFeature",
298            ServerErrorKind::RateLimitedFailure => "searchx.RateLimitedFailure",
299            ServerErrorKind::Unknown => "searchx._OTHER",
300        }
301    }
302}