1use crate::frame::frame_errors::{CqlErrorParseError, LowLevelDeserializationError};
4use crate::frame::protocol_features::ProtocolFeatures;
5use crate::frame::types;
6use crate::Consistency;
7use byteorder::ReadBytesExt;
8use bytes::Bytes;
9use thiserror::Error;
10
11#[derive(Debug, Clone)]
14pub struct Error {
15 pub error: DbError,
17
18 pub reason: String,
20}
21
22fn make_error_field_err(
23 db_error: &'static str,
24 field: &'static str,
25 err: impl Into<LowLevelDeserializationError>,
26) -> CqlErrorParseError {
27 CqlErrorParseError::MalformedErrorField {
28 db_error,
29 field,
30 err: err.into(),
31 }
32}
33
34impl Error {
35 pub fn deserialize(
37 features: &ProtocolFeatures,
38 buf: &mut &[u8],
39 ) -> Result<Self, CqlErrorParseError> {
40 let code = types::read_int(buf)
41 .map_err(|err| CqlErrorParseError::ErrorCodeParseError(err.into()))?;
42 let reason = types::read_string(buf)
43 .map_err(CqlErrorParseError::ReasonParseError)?
44 .to_owned();
45
46 let error: DbError = match code {
47 0x0000 => DbError::ServerError,
48 0x000A => DbError::ProtocolError,
49 0x0100 => DbError::AuthenticationError,
50 0x1000 => DbError::Unavailable {
51 consistency: types::read_consistency(buf)
52 .map_err(|err| make_error_field_err("UNAVAILABLE", "CONSISTENCY", err))?,
53 required: types::read_int(buf)
54 .map_err(|err| make_error_field_err("UNAVAILABLE", "REQUIRED", err))?,
55 alive: types::read_int(buf)
56 .map_err(|err| make_error_field_err("UNAVAILABLE", "ALIVE", err))?,
57 },
58 0x1001 => DbError::Overloaded,
59 0x1002 => DbError::IsBootstrapping,
60 0x1003 => DbError::TruncateError,
61 0x1100 => DbError::WriteTimeout {
62 consistency: types::read_consistency(buf)
63 .map_err(|err| make_error_field_err("WRITE_TIMEOUT", "CONSISTENCY", err))?,
64 received: types::read_int(buf)
65 .map_err(|err| make_error_field_err("WRITE_TIMEOUT", "RECEIVED", err))?,
66 required: types::read_int(buf)
67 .map_err(|err| make_error_field_err("WRITE_TIMEOUT", "REQUIRED", err))?,
68 write_type: WriteType::from(
69 types::read_string(buf)
70 .map_err(|err| make_error_field_err("WRITE_TIMEOUT", "WRITE_TYPE", err))?,
71 ),
72 },
73 0x1200 => DbError::ReadTimeout {
74 consistency: types::read_consistency(buf)
75 .map_err(|err| make_error_field_err("READ_TIMEOUT", "CONSISTENCY", err))?,
76 received: types::read_int(buf)
77 .map_err(|err| make_error_field_err("READ_TIMEOUT", "RECEIVED", err))?,
78 required: types::read_int(buf)
79 .map_err(|err| make_error_field_err("READ_TIMEOUT", "REQUIRED", err))?,
80 data_present: buf
81 .read_u8()
82 .map_err(|err| make_error_field_err("READ_TIMEOUT", "DATA_PRESENT", err))?
83 != 0,
84 },
85 0x1300 => DbError::ReadFailure {
86 consistency: types::read_consistency(buf)
87 .map_err(|err| make_error_field_err("READ_FAILURE", "CONSISTENCY", err))?,
88 received: types::read_int(buf)
89 .map_err(|err| make_error_field_err("READ_FAILURE", "RECEIVED", err))?,
90 required: types::read_int(buf)
91 .map_err(|err| make_error_field_err("READ_FAILURE", "REQUIRED", err))?,
92 numfailures: types::read_int(buf)
93 .map_err(|err| make_error_field_err("READ_FAILURE", "NUM_FAILURES", err))?,
94 data_present: buf
95 .read_u8()
96 .map_err(|err| make_error_field_err("READ_FAILURE", "DATA_PRESENT", err))?
97 != 0,
98 },
99 0x1400 => DbError::FunctionFailure {
100 keyspace: types::read_string(buf)
101 .map_err(|err| make_error_field_err("FUNCTION_FAILURE", "KEYSPACE", err))?
102 .to_string(),
103 function: types::read_string(buf)
104 .map_err(|err| make_error_field_err("FUNCTION_FAILURE", "FUNCTION", err))?
105 .to_string(),
106 arg_types: types::read_string_list(buf)
107 .map_err(|err| make_error_field_err("FUNCTION_FAILURE", "ARG_TYPES", err))?,
108 },
109 0x1500 => DbError::WriteFailure {
110 consistency: types::read_consistency(buf)
111 .map_err(|err| make_error_field_err("WRITE_FAILURE", "CONSISTENCY", err))?,
112 received: types::read_int(buf)
113 .map_err(|err| make_error_field_err("WRITE_FAILURE", "RECEIVED", err))?,
114 required: types::read_int(buf)
115 .map_err(|err| make_error_field_err("WRITE_FAILURE", "REQUIRED", err))?,
116 numfailures: types::read_int(buf)
117 .map_err(|err| make_error_field_err("WRITE_FAILURE", "NUM_FAILURES", err))?,
118 write_type: WriteType::from(
119 types::read_string(buf)
120 .map_err(|err| make_error_field_err("WRITE_FAILURE", "WRITE_TYPE", err))?,
121 ),
122 },
123 0x2000 => DbError::SyntaxError,
124 0x2100 => DbError::Unauthorized,
125 0x2200 => DbError::Invalid,
126 0x2300 => DbError::ConfigError,
127 0x2400 => DbError::AlreadyExists {
128 keyspace: types::read_string(buf)
129 .map_err(|err| make_error_field_err("ALREADY_EXISTS", "KEYSPACE", err))?
130 .to_string(),
131 table: types::read_string(buf)
132 .map_err(|err| make_error_field_err("ALREADY_EXISTS", "TABLE", err))?
133 .to_string(),
134 },
135 0x2500 => DbError::Unprepared {
136 statement_id: Bytes::from(
137 types::read_short_bytes(buf)
138 .map_err(|err| make_error_field_err("UNPREPARED", "STATEMENT_ID", err))?
139 .to_owned(),
140 ),
141 },
142 code if Some(code) == features.rate_limit_error => {
143 DbError::RateLimitReached {
144 op_type: OperationType::from(buf.read_u8().map_err(|err| {
145 make_error_field_err("RATE_LIMIT_REACHED", "OP_TYPE", err)
146 })?),
147 rejected_by_coordinator: buf.read_u8().map_err(|err| {
148 make_error_field_err("RATE_LIMIT_REACHED", "REJECTED_BY_COORDINATOR", err)
149 })? != 0,
150 }
151 }
152 _ => DbError::Other(code),
153 };
154
155 Ok(Error { error, reason })
156 }
157}
158
159#[derive(Error, Debug, Clone, PartialEq, Eq)]
162#[non_exhaustive]
163pub enum DbError {
164 #[error("The submitted query has a syntax error")]
166 SyntaxError,
167
168 #[error("The query is syntactically correct but invalid")]
170 Invalid,
171
172 #[error(
174 "Attempted to create a keyspace or a table that was already existing \
175 (keyspace: {keyspace}, table: {table})"
176 )]
177 AlreadyExists {
178 keyspace: String,
180 table: String,
182 },
183
184 #[error(
186 "User defined function failed during execution \
187 (keyspace: {keyspace}, function: {function}, arg_types: {arg_types:?})"
188 )]
189 FunctionFailure {
190 keyspace: String,
192 function: String,
194 arg_types: Vec<String>,
196 },
197
198 #[error("Authentication failed - bad credentials")]
200 AuthenticationError,
201
202 #[error("The logged user doesn't have the right to perform the query")]
204 Unauthorized,
205
206 #[error("The query is invalid because of some configuration issue")]
208 ConfigError,
209
210 #[error(
212 "Not enough nodes are alive to satisfy required consistency level \
213 (consistency: {consistency}, required: {required}, alive: {alive})"
214 )]
215 Unavailable {
216 consistency: Consistency,
218 required: i32,
220 alive: i32,
222 },
223
224 #[error("The request cannot be processed because the coordinator node is overloaded")]
226 Overloaded,
227
228 #[error("The coordinator node is still bootstrapping")]
230 IsBootstrapping,
231
232 #[error("Error during truncate operation")]
234 TruncateError,
235
236 #[error("Not enough nodes responded to the read request in time to satisfy required consistency level \
238 (consistency: {consistency}, received: {received}, required: {required}, data_present: {data_present})")]
239 ReadTimeout {
240 consistency: Consistency,
242 received: i32,
244 required: i32,
246 data_present: bool,
248 },
249
250 #[error("Not enough nodes responded to the write request in time to satisfy required consistency level \
252 (consistency: {consistency}, received: {received}, required: {required}, write_type: {write_type})")]
253 WriteTimeout {
254 consistency: Consistency,
256 received: i32,
258 required: i32,
260 write_type: WriteType,
262 },
263
264 #[error(
266 "A non-timeout error during a read request \
267 (consistency: {consistency}, received: {received}, required: {required}, \
268 numfailures: {numfailures}, data_present: {data_present})"
269 )]
270 ReadFailure {
271 consistency: Consistency,
273 received: i32,
275 required: i32,
277 numfailures: i32,
279 data_present: bool,
281 },
282
283 #[error(
285 "A non-timeout error during a write request \
286 (consistency: {consistency}, received: {received}, required: {required}, \
287 numfailures: {numfailures}, write_type: {write_type}"
288 )]
289 WriteFailure {
290 consistency: Consistency,
292 received: i32,
294 required: i32,
296 numfailures: i32,
298 write_type: WriteType,
300 },
301
302 #[error(
304 "Tried to execute a prepared statement that is not prepared. Driver should prepare it again"
305 )]
306 Unprepared {
307 statement_id: Bytes,
309 },
310
311 #[error("Internal server error. This indicates a server-side bug")]
313 ServerError,
314
315 #[error("Invalid protocol message received from the driver")]
317 ProtocolError,
318
319 #[error("Rate limit was exceeded for a partition affected by the request")]
323 RateLimitReached {
324 op_type: OperationType,
326 rejected_by_coordinator: bool,
330 },
331
332 #[error("Other error not specified in the specification. Error code: {0}")]
334 Other(i32),
335}
336
337impl DbError {
338 pub fn code(&self, protocol_features: &ProtocolFeatures) -> i32 {
340 match self {
341 DbError::ServerError => 0x0000,
342 DbError::ProtocolError => 0x000A,
343 DbError::AuthenticationError => 0x0100,
344 DbError::Unavailable {
345 consistency: _,
346 required: _,
347 alive: _,
348 } => 0x1000,
349 DbError::Overloaded => 0x1001,
350 DbError::IsBootstrapping => 0x1002,
351 DbError::TruncateError => 0x1003,
352 DbError::WriteTimeout {
353 consistency: _,
354 received: _,
355 required: _,
356 write_type: _,
357 } => 0x1100,
358 DbError::ReadTimeout {
359 consistency: _,
360 received: _,
361 required: _,
362 data_present: _,
363 } => 0x1200,
364 DbError::ReadFailure {
365 consistency: _,
366 received: _,
367 required: _,
368 numfailures: _,
369 data_present: _,
370 } => 0x1300,
371 DbError::FunctionFailure {
372 keyspace: _,
373 function: _,
374 arg_types: _,
375 } => 0x1400,
376 DbError::WriteFailure {
377 consistency: _,
378 received: _,
379 required: _,
380 numfailures: _,
381 write_type: _,
382 } => 0x1500,
383 DbError::SyntaxError => 0x2000,
384 DbError::Unauthorized => 0x2100,
385 DbError::Invalid => 0x2200,
386 DbError::ConfigError => 0x2300,
387 DbError::AlreadyExists {
388 keyspace: _,
389 table: _,
390 } => 0x2400,
391 DbError::Unprepared { statement_id: _ } => 0x2500,
392 DbError::Other(code) => *code,
393 DbError::RateLimitReached {
394 op_type: _,
395 rejected_by_coordinator: _,
396 } => protocol_features.rate_limit_error.unwrap(),
397 }
398 }
399
400 pub fn can_speculative_retry(&self) -> bool {
403 #[deny(clippy::wildcard_enum_match_arm)]
407 match self {
408 DbError::SyntaxError
410 | DbError::Invalid
411 | DbError::AlreadyExists { .. }
412 | DbError::Unauthorized
413 | DbError::ProtocolError => false,
414
415 DbError::AuthenticationError | DbError::Other(_) => false,
417
418 DbError::FunctionFailure { .. } => false,
421
422 DbError::ConfigError | DbError::TruncateError => false,
425
426 DbError::Unavailable { .. }
428 | DbError::Overloaded
429 | DbError::IsBootstrapping
430 | DbError::ReadTimeout { .. }
431 | DbError::WriteTimeout { .. }
432 | DbError::ReadFailure { .. }
433 | DbError::WriteFailure { .. }
434 | DbError::Unprepared { .. }
436 | DbError::ServerError
437 | DbError::RateLimitReached { .. } => true,
438 }
439 }
440}
441
442#[derive(Debug, Clone, PartialEq, Eq)]
444pub enum OperationType {
445 Read,
446 Write,
447 Other(u8),
448}
449
450#[derive(Debug, Clone, PartialEq, Eq)]
452pub enum WriteType {
453 Simple,
455 Batch,
458 UnloggedBatch,
460 Counter,
462 BatchLog,
464 Cas,
466 View,
468 Cdc,
470 Other(String),
472}
473
474impl std::fmt::Display for WriteType {
475 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
476 write!(f, "{self:?}")
477 }
478}
479
480impl From<u8> for OperationType {
481 fn from(operation_type: u8) -> OperationType {
482 match operation_type {
483 0 => OperationType::Read,
484 1 => OperationType::Write,
485 other => OperationType::Other(other),
486 }
487 }
488}
489
490impl From<&str> for WriteType {
491 fn from(write_type_str: &str) -> WriteType {
492 match write_type_str {
493 "SIMPLE" => WriteType::Simple,
494 "BATCH" => WriteType::Batch,
495 "UNLOGGED_BATCH" => WriteType::UnloggedBatch,
496 "COUNTER" => WriteType::Counter,
497 "BATCH_LOG" => WriteType::BatchLog,
498 "CAS" => WriteType::Cas,
499 "VIEW" => WriteType::View,
500 "CDC" => WriteType::Cdc,
501 _ => WriteType::Other(write_type_str.to_string()),
502 }
503 }
504}
505
506impl WriteType {
507 pub fn as_str(&self) -> &str {
509 match self {
510 WriteType::Simple => "SIMPLE",
511 WriteType::Batch => "BATCH",
512 WriteType::UnloggedBatch => "UNLOGGED_BATCH",
513 WriteType::Counter => "COUNTER",
514 WriteType::BatchLog => "BATCH_LOG",
515 WriteType::Cas => "CAS",
516 WriteType::View => "VIEW",
517 WriteType::Cdc => "CDC",
518 WriteType::Other(write_type) => write_type.as_str(),
519 }
520 }
521}
522
523#[cfg(test)]
524mod tests {
525 use super::{DbError, Error, OperationType, WriteType};
526 use crate::frame::protocol_features::ProtocolFeatures;
527 use crate::Consistency;
528 use bytes::Bytes;
529 use std::convert::TryInto;
530
531 fn make_error_request_bytes(error_code: i32, message: &str) -> Vec<u8> {
534 let mut bytes: Vec<u8> = Vec::new();
535 let message_len: u16 = message.len().try_into().unwrap();
536
537 bytes.extend(error_code.to_be_bytes());
538 bytes.extend(message_len.to_be_bytes());
539 bytes.extend(message.as_bytes());
540
541 bytes
542 }
543
544 #[test]
546 fn deserialize_simple_errors() {
547 let simple_error_mappings: [(i32, DbError); 11] = [
548 (0x0000, DbError::ServerError),
549 (0x000A, DbError::ProtocolError),
550 (0x0100, DbError::AuthenticationError),
551 (0x1001, DbError::Overloaded),
552 (0x1002, DbError::IsBootstrapping),
553 (0x1003, DbError::TruncateError),
554 (0x2000, DbError::SyntaxError),
555 (0x2100, DbError::Unauthorized),
556 (0x2200, DbError::Invalid),
557 (0x2300, DbError::ConfigError),
558 (0x1234, DbError::Other(0x1234)),
559 ];
560
561 let features = ProtocolFeatures::default();
562
563 for (error_code, expected_error) in &simple_error_mappings {
564 let bytes: Vec<u8> = make_error_request_bytes(*error_code, "simple message");
565 let error: Error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
566 assert_eq!(error.error, *expected_error);
567 assert_eq!(error.reason, "simple message");
568 }
569 }
570
571 #[test]
572 fn deserialize_unavailable() {
573 let features = ProtocolFeatures::default();
574
575 let mut bytes = make_error_request_bytes(0x1000, "message 2");
576 bytes.extend(1_i16.to_be_bytes());
577 bytes.extend(2_i32.to_be_bytes());
578 bytes.extend(3_i32.to_be_bytes());
579
580 let error: Error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
581
582 assert_eq!(
583 error.error,
584 DbError::Unavailable {
585 consistency: Consistency::One,
586 required: 2,
587 alive: 3,
588 }
589 );
590 assert_eq!(error.reason, "message 2");
591 }
592
593 #[test]
594 fn deserialize_write_timeout() {
595 let features = ProtocolFeatures::default();
596
597 let mut bytes = make_error_request_bytes(0x1100, "message 2");
598 bytes.extend(0x0004_i16.to_be_bytes());
599 bytes.extend((-5_i32).to_be_bytes());
600 bytes.extend(100_i32.to_be_bytes());
601
602 let write_type_str = "SIMPLE";
603 let write_type_str_len: u16 = write_type_str.len().try_into().unwrap();
604 bytes.extend(write_type_str_len.to_be_bytes());
605 bytes.extend(write_type_str.as_bytes());
606
607 let error: Error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
608
609 assert_eq!(
610 error.error,
611 DbError::WriteTimeout {
612 consistency: Consistency::Quorum,
613 received: -5, required: 100,
615 write_type: WriteType::Simple,
616 }
617 );
618 assert_eq!(error.reason, "message 2");
619 }
620
621 #[test]
622 fn deserialize_read_timeout() {
623 let features = ProtocolFeatures::default();
624
625 let mut bytes = make_error_request_bytes(0x1200, "message 2");
626 bytes.extend(0x0002_i16.to_be_bytes());
627 bytes.extend(8_i32.to_be_bytes());
628 bytes.extend(32_i32.to_be_bytes());
629 bytes.push(0_u8);
630
631 let error: Error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
632
633 assert_eq!(
634 error.error,
635 DbError::ReadTimeout {
636 consistency: Consistency::Two,
637 received: 8,
638 required: 32,
639 data_present: false,
640 }
641 );
642 assert_eq!(error.reason, "message 2");
643 }
644
645 #[test]
646 fn deserialize_read_failure() {
647 let features = ProtocolFeatures::default();
648
649 let mut bytes = make_error_request_bytes(0x1300, "message 2");
650 bytes.extend(0x0003_i16.to_be_bytes());
651 bytes.extend(4_i32.to_be_bytes());
652 bytes.extend(5_i32.to_be_bytes());
653 bytes.extend(6_i32.to_be_bytes());
654 bytes.push(123_u8); let error: Error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
657
658 assert_eq!(
659 error.error,
660 DbError::ReadFailure {
661 consistency: Consistency::Three,
662 received: 4,
663 required: 5,
664 numfailures: 6,
665 data_present: true,
666 }
667 );
668 assert_eq!(error.reason, "message 2");
669 }
670
671 #[test]
672 fn deserialize_function_failure() {
673 let features = ProtocolFeatures::default();
674
675 let mut bytes = make_error_request_bytes(0x1400, "message 2");
676
677 let keyspace_name: &str = "keyspace_name";
678 let keyspace_name_len: u16 = keyspace_name.len().try_into().unwrap();
679
680 let function_name: &str = "function_name";
681 let function_name_len: u16 = function_name.len().try_into().unwrap();
682
683 let type1: &str = "type1";
684 let type1_len: u16 = type1.len().try_into().unwrap();
685
686 let type2: &str = "type2";
687 let type2_len: u16 = type1.len().try_into().unwrap();
688
689 bytes.extend(keyspace_name_len.to_be_bytes());
690 bytes.extend(keyspace_name.as_bytes());
691 bytes.extend(function_name_len.to_be_bytes());
692 bytes.extend(function_name.as_bytes());
693 bytes.extend(2_i16.to_be_bytes());
694 bytes.extend(type1_len.to_be_bytes());
695 bytes.extend(type1.as_bytes());
696 bytes.extend(type2_len.to_be_bytes());
697 bytes.extend(type2.as_bytes());
698
699 let error: Error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
700
701 assert_eq!(
702 error.error,
703 DbError::FunctionFailure {
704 keyspace: "keyspace_name".to_string(),
705 function: "function_name".to_string(),
706 arg_types: vec!["type1".to_string(), "type2".to_string()]
707 }
708 );
709 assert_eq!(error.reason, "message 2");
710 }
711
712 #[test]
713 fn deserialize_write_failure() {
714 let features = ProtocolFeatures::default();
715
716 let mut bytes = make_error_request_bytes(0x1500, "message 2");
717
718 bytes.extend(0x0000_i16.to_be_bytes());
719 bytes.extend(2_i32.to_be_bytes());
720 bytes.extend(4_i32.to_be_bytes());
721 bytes.extend(8_i32.to_be_bytes());
722
723 let write_type_str = "COUNTER";
724 let write_type_str_len: u16 = write_type_str.len().try_into().unwrap();
725 bytes.extend(write_type_str_len.to_be_bytes());
726 bytes.extend(write_type_str.as_bytes());
727
728 let error: Error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
729
730 assert_eq!(
731 error.error,
732 DbError::WriteFailure {
733 consistency: Consistency::Any,
734 received: 2,
735 required: 4,
736 numfailures: 8,
737 write_type: WriteType::Counter,
738 }
739 );
740 assert_eq!(error.reason, "message 2");
741 }
742
743 #[test]
744 fn deserialize_already_exists() {
745 let features = ProtocolFeatures::default();
746
747 let mut bytes = make_error_request_bytes(0x2400, "message 2");
748
749 let keyspace_name: &str = "keyspace_name";
750 let keyspace_name_len: u16 = keyspace_name.len().try_into().unwrap();
751
752 let table_name: &str = "table_name";
753 let table_name_len: u16 = table_name.len().try_into().unwrap();
754
755 bytes.extend(keyspace_name_len.to_be_bytes());
756 bytes.extend(keyspace_name.as_bytes());
757 bytes.extend(table_name_len.to_be_bytes());
758 bytes.extend(table_name.as_bytes());
759
760 let error: Error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
761
762 assert_eq!(
763 error.error,
764 DbError::AlreadyExists {
765 keyspace: "keyspace_name".to_string(),
766 table: "table_name".to_string(),
767 }
768 );
769 assert_eq!(error.reason, "message 2");
770 }
771
772 #[test]
773 fn deserialize_unprepared() {
774 let features = ProtocolFeatures::default();
775
776 let mut bytes = make_error_request_bytes(0x2500, "message 3");
777 let statement_id = b"deadbeef";
778 bytes.extend((statement_id.len() as i16).to_be_bytes());
779 bytes.extend(statement_id);
780
781 let error: Error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
782
783 assert_eq!(
784 error.error,
785 DbError::Unprepared {
786 statement_id: Bytes::from_static(b"deadbeef")
787 }
788 );
789 assert_eq!(error.reason, "message 3");
790 }
791
792 #[test]
793 fn deserialize_rate_limit_error() {
794 let features = ProtocolFeatures {
795 rate_limit_error: Some(0x4321),
796 ..Default::default()
797 };
798 let mut bytes = make_error_request_bytes(0x4321, "message 1");
799 bytes.extend([0u8]); bytes.extend([1u8]); let error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
802
803 assert_eq!(
804 error.error,
805 DbError::RateLimitReached {
806 op_type: OperationType::Read,
807 rejected_by_coordinator: true,
808 }
809 );
810 assert_eq!(error.reason, "message 1");
811
812 let features = ProtocolFeatures {
813 rate_limit_error: Some(0x8765),
814 ..Default::default()
815 };
816 let mut bytes = make_error_request_bytes(0x8765, "message 2");
817 bytes.extend([1u8]); bytes.extend([0u8]); let error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
820
821 assert_eq!(
822 error.error,
823 DbError::RateLimitReached {
824 op_type: OperationType::Write,
825 rejected_by_coordinator: false,
826 }
827 );
828 assert_eq!(error.reason, "message 2");
829 }
830}