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