1use crate::Consistency;
4use crate::frame::frame_errors::{CqlErrorParseError, LowLevelDeserializationError};
5use crate::frame::protocol_features::ProtocolFeatures;
6use crate::frame::types;
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(
238 "Not enough nodes responded to the read request in time to satisfy required consistency level \
239 (consistency: {consistency}, received: {received}, required: {required}, data_present: {data_present})"
240 )]
241 ReadTimeout {
242 consistency: Consistency,
244 received: i32,
246 required: i32,
248 data_present: bool,
250 },
251
252 #[error(
254 "Not enough nodes responded to the write request in time to satisfy required consistency level \
255 (consistency: {consistency}, received: {received}, required: {required}, write_type: {write_type})"
256 )]
257 WriteTimeout {
258 consistency: Consistency,
260 received: i32,
262 required: i32,
264 write_type: WriteType,
266 },
267
268 #[error(
270 "A non-timeout error during a read request \
271 (consistency: {consistency}, received: {received}, required: {required}, \
272 numfailures: {numfailures}, data_present: {data_present})"
273 )]
274 ReadFailure {
275 consistency: Consistency,
277 received: i32,
279 required: i32,
281 numfailures: i32,
283 data_present: bool,
285 },
286
287 #[error(
289 "A non-timeout error during a write request \
290 (consistency: {consistency}, received: {received}, required: {required}, \
291 numfailures: {numfailures}, write_type: {write_type}"
292 )]
293 WriteFailure {
294 consistency: Consistency,
296 received: i32,
298 required: i32,
300 numfailures: i32,
302 write_type: WriteType,
304 },
305
306 #[error(
308 "Tried to execute a prepared statement that is not prepared. Driver should prepare it again"
309 )]
310 Unprepared {
311 statement_id: Bytes,
313 },
314
315 #[error("Internal server error. This indicates a server-side bug")]
317 ServerError,
318
319 #[error("Invalid protocol message received from the driver")]
321 ProtocolError,
322
323 #[error("Rate limit was exceeded for a partition affected by the request")]
327 RateLimitReached {
328 op_type: OperationType,
330 rejected_by_coordinator: bool,
334 },
335
336 #[error("Other error not specified in the specification. Error code: {0}")]
338 Other(i32),
339}
340
341impl DbError {
342 pub fn code(&self, protocol_features: &ProtocolFeatures) -> i32 {
344 match self {
345 DbError::ServerError => 0x0000,
346 DbError::ProtocolError => 0x000A,
347 DbError::AuthenticationError => 0x0100,
348 DbError::Unavailable {
349 consistency: _,
350 required: _,
351 alive: _,
352 } => 0x1000,
353 DbError::Overloaded => 0x1001,
354 DbError::IsBootstrapping => 0x1002,
355 DbError::TruncateError => 0x1003,
356 DbError::WriteTimeout {
357 consistency: _,
358 received: _,
359 required: _,
360 write_type: _,
361 } => 0x1100,
362 DbError::ReadTimeout {
363 consistency: _,
364 received: _,
365 required: _,
366 data_present: _,
367 } => 0x1200,
368 DbError::ReadFailure {
369 consistency: _,
370 received: _,
371 required: _,
372 numfailures: _,
373 data_present: _,
374 } => 0x1300,
375 DbError::FunctionFailure {
376 keyspace: _,
377 function: _,
378 arg_types: _,
379 } => 0x1400,
380 DbError::WriteFailure {
381 consistency: _,
382 received: _,
383 required: _,
384 numfailures: _,
385 write_type: _,
386 } => 0x1500,
387 DbError::SyntaxError => 0x2000,
388 DbError::Unauthorized => 0x2100,
389 DbError::Invalid => 0x2200,
390 DbError::ConfigError => 0x2300,
391 DbError::AlreadyExists {
392 keyspace: _,
393 table: _,
394 } => 0x2400,
395 DbError::Unprepared { statement_id: _ } => 0x2500,
396 DbError::Other(code) => *code,
397 DbError::RateLimitReached {
398 op_type: _,
399 rejected_by_coordinator: _,
400 } => protocol_features.rate_limit_error.unwrap(),
401 }
402 }
403
404 pub fn can_speculative_retry(&self) -> bool {
407 #[deny(clippy::wildcard_enum_match_arm)]
411 match self {
412 DbError::SyntaxError
414 | DbError::Invalid
415 | DbError::AlreadyExists { .. }
416 | DbError::Unauthorized
417 | DbError::ProtocolError => false,
418
419 DbError::AuthenticationError | DbError::Other(_) => false,
421
422 DbError::FunctionFailure { .. } => false,
425
426 DbError::ConfigError | DbError::TruncateError => false,
429
430 DbError::Unavailable { .. }
432 | DbError::Overloaded
433 | DbError::IsBootstrapping
434 | DbError::ReadTimeout { .. }
435 | DbError::WriteTimeout { .. }
436 | DbError::ReadFailure { .. }
437 | DbError::WriteFailure { .. }
438 | DbError::Unprepared { .. }
440 | DbError::ServerError
441 | DbError::RateLimitReached { .. } => true,
442 }
443 }
444}
445
446#[derive(Debug, Clone, PartialEq, Eq)]
448pub enum OperationType {
449 Read,
450 Write,
451 Other(u8),
452}
453
454#[derive(Debug, Clone, PartialEq, Eq)]
456pub enum WriteType {
457 Simple,
459 Batch,
462 UnloggedBatch,
464 Counter,
466 BatchLog,
468 Cas,
470 View,
472 Cdc,
474 Other(String),
476}
477
478impl std::fmt::Display for WriteType {
479 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
480 write!(f, "{self:?}")
481 }
482}
483
484impl From<u8> for OperationType {
485 fn from(operation_type: u8) -> OperationType {
486 match operation_type {
487 0 => OperationType::Read,
488 1 => OperationType::Write,
489 other => OperationType::Other(other),
490 }
491 }
492}
493
494impl From<&str> for WriteType {
495 fn from(write_type_str: &str) -> WriteType {
496 match write_type_str {
497 "SIMPLE" => WriteType::Simple,
498 "BATCH" => WriteType::Batch,
499 "UNLOGGED_BATCH" => WriteType::UnloggedBatch,
500 "COUNTER" => WriteType::Counter,
501 "BATCH_LOG" => WriteType::BatchLog,
502 "CAS" => WriteType::Cas,
503 "VIEW" => WriteType::View,
504 "CDC" => WriteType::Cdc,
505 _ => WriteType::Other(write_type_str.to_string()),
506 }
507 }
508}
509
510impl WriteType {
511 pub fn as_str(&self) -> &str {
513 match self {
514 WriteType::Simple => "SIMPLE",
515 WriteType::Batch => "BATCH",
516 WriteType::UnloggedBatch => "UNLOGGED_BATCH",
517 WriteType::Counter => "COUNTER",
518 WriteType::BatchLog => "BATCH_LOG",
519 WriteType::Cas => "CAS",
520 WriteType::View => "VIEW",
521 WriteType::Cdc => "CDC",
522 WriteType::Other(write_type) => write_type.as_str(),
523 }
524 }
525}
526
527#[cfg(test)]
528mod tests {
529 use super::{DbError, Error, OperationType, WriteType};
530 use crate::Consistency;
531 use crate::frame::protocol_features::ProtocolFeatures;
532 use bytes::Bytes;
533 use std::convert::TryInto;
534
535 fn make_error_request_bytes(error_code: i32, message: &str) -> Vec<u8> {
538 let mut bytes: Vec<u8> = Vec::new();
539 let message_len: u16 = message.len().try_into().unwrap();
540
541 bytes.extend(error_code.to_be_bytes());
542 bytes.extend(message_len.to_be_bytes());
543 bytes.extend(message.as_bytes());
544
545 bytes
546 }
547
548 #[test]
550 fn deserialize_simple_errors() {
551 let simple_error_mappings: [(i32, DbError); 11] = [
552 (0x0000, DbError::ServerError),
553 (0x000A, DbError::ProtocolError),
554 (0x0100, DbError::AuthenticationError),
555 (0x1001, DbError::Overloaded),
556 (0x1002, DbError::IsBootstrapping),
557 (0x1003, DbError::TruncateError),
558 (0x2000, DbError::SyntaxError),
559 (0x2100, DbError::Unauthorized),
560 (0x2200, DbError::Invalid),
561 (0x2300, DbError::ConfigError),
562 (0x1234, DbError::Other(0x1234)),
563 ];
564
565 let features = ProtocolFeatures::default();
566
567 for (error_code, expected_error) in &simple_error_mappings {
568 let bytes: Vec<u8> = make_error_request_bytes(*error_code, "simple message");
569 let error: Error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
570 assert_eq!(error.error, *expected_error);
571 assert_eq!(error.reason, "simple message");
572 }
573 }
574
575 #[test]
576 fn deserialize_unavailable() {
577 let features = ProtocolFeatures::default();
578
579 let mut bytes = make_error_request_bytes(0x1000, "message 2");
580 bytes.extend(1_i16.to_be_bytes());
581 bytes.extend(2_i32.to_be_bytes());
582 bytes.extend(3_i32.to_be_bytes());
583
584 let error: Error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
585
586 assert_eq!(
587 error.error,
588 DbError::Unavailable {
589 consistency: Consistency::One,
590 required: 2,
591 alive: 3,
592 }
593 );
594 assert_eq!(error.reason, "message 2");
595 }
596
597 #[test]
598 fn deserialize_write_timeout() {
599 let features = ProtocolFeatures::default();
600
601 let mut bytes = make_error_request_bytes(0x1100, "message 2");
602 bytes.extend(0x0004_i16.to_be_bytes());
603 bytes.extend((-5_i32).to_be_bytes());
604 bytes.extend(100_i32.to_be_bytes());
605
606 let write_type_str = "SIMPLE";
607 let write_type_str_len: u16 = write_type_str.len().try_into().unwrap();
608 bytes.extend(write_type_str_len.to_be_bytes());
609 bytes.extend(write_type_str.as_bytes());
610
611 let error: Error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
612
613 assert_eq!(
614 error.error,
615 DbError::WriteTimeout {
616 consistency: Consistency::Quorum,
617 received: -5, required: 100,
619 write_type: WriteType::Simple,
620 }
621 );
622 assert_eq!(error.reason, "message 2");
623 }
624
625 #[test]
626 fn deserialize_read_timeout() {
627 let features = ProtocolFeatures::default();
628
629 let mut bytes = make_error_request_bytes(0x1200, "message 2");
630 bytes.extend(0x0002_i16.to_be_bytes());
631 bytes.extend(8_i32.to_be_bytes());
632 bytes.extend(32_i32.to_be_bytes());
633 bytes.push(0_u8);
634
635 let error: Error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
636
637 assert_eq!(
638 error.error,
639 DbError::ReadTimeout {
640 consistency: Consistency::Two,
641 received: 8,
642 required: 32,
643 data_present: false,
644 }
645 );
646 assert_eq!(error.reason, "message 2");
647 }
648
649 #[test]
650 fn deserialize_read_failure() {
651 let features = ProtocolFeatures::default();
652
653 let mut bytes = make_error_request_bytes(0x1300, "message 2");
654 bytes.extend(0x0003_i16.to_be_bytes());
655 bytes.extend(4_i32.to_be_bytes());
656 bytes.extend(5_i32.to_be_bytes());
657 bytes.extend(6_i32.to_be_bytes());
658 bytes.push(123_u8); let error: Error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
661
662 assert_eq!(
663 error.error,
664 DbError::ReadFailure {
665 consistency: Consistency::Three,
666 received: 4,
667 required: 5,
668 numfailures: 6,
669 data_present: true,
670 }
671 );
672 assert_eq!(error.reason, "message 2");
673 }
674
675 #[test]
676 fn deserialize_function_failure() {
677 let features = ProtocolFeatures::default();
678
679 let mut bytes = make_error_request_bytes(0x1400, "message 2");
680
681 let keyspace_name: &str = "keyspace_name";
682 let keyspace_name_len: u16 = keyspace_name.len().try_into().unwrap();
683
684 let function_name: &str = "function_name";
685 let function_name_len: u16 = function_name.len().try_into().unwrap();
686
687 let type1: &str = "type1";
688 let type1_len: u16 = type1.len().try_into().unwrap();
689
690 let type2: &str = "type2";
691 let type2_len: u16 = type1.len().try_into().unwrap();
692
693 bytes.extend(keyspace_name_len.to_be_bytes());
694 bytes.extend(keyspace_name.as_bytes());
695 bytes.extend(function_name_len.to_be_bytes());
696 bytes.extend(function_name.as_bytes());
697 bytes.extend(2_i16.to_be_bytes());
698 bytes.extend(type1_len.to_be_bytes());
699 bytes.extend(type1.as_bytes());
700 bytes.extend(type2_len.to_be_bytes());
701 bytes.extend(type2.as_bytes());
702
703 let error: Error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
704
705 assert_eq!(
706 error.error,
707 DbError::FunctionFailure {
708 keyspace: "keyspace_name".to_string(),
709 function: "function_name".to_string(),
710 arg_types: vec!["type1".to_string(), "type2".to_string()]
711 }
712 );
713 assert_eq!(error.reason, "message 2");
714 }
715
716 #[test]
717 fn deserialize_write_failure() {
718 let features = ProtocolFeatures::default();
719
720 let mut bytes = make_error_request_bytes(0x1500, "message 2");
721
722 bytes.extend(0x0000_i16.to_be_bytes());
723 bytes.extend(2_i32.to_be_bytes());
724 bytes.extend(4_i32.to_be_bytes());
725 bytes.extend(8_i32.to_be_bytes());
726
727 let write_type_str = "COUNTER";
728 let write_type_str_len: u16 = write_type_str.len().try_into().unwrap();
729 bytes.extend(write_type_str_len.to_be_bytes());
730 bytes.extend(write_type_str.as_bytes());
731
732 let error: Error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
733
734 assert_eq!(
735 error.error,
736 DbError::WriteFailure {
737 consistency: Consistency::Any,
738 received: 2,
739 required: 4,
740 numfailures: 8,
741 write_type: WriteType::Counter,
742 }
743 );
744 assert_eq!(error.reason, "message 2");
745 }
746
747 #[test]
748 fn deserialize_already_exists() {
749 let features = ProtocolFeatures::default();
750
751 let mut bytes = make_error_request_bytes(0x2400, "message 2");
752
753 let keyspace_name: &str = "keyspace_name";
754 let keyspace_name_len: u16 = keyspace_name.len().try_into().unwrap();
755
756 let table_name: &str = "table_name";
757 let table_name_len: u16 = table_name.len().try_into().unwrap();
758
759 bytes.extend(keyspace_name_len.to_be_bytes());
760 bytes.extend(keyspace_name.as_bytes());
761 bytes.extend(table_name_len.to_be_bytes());
762 bytes.extend(table_name.as_bytes());
763
764 let error: Error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
765
766 assert_eq!(
767 error.error,
768 DbError::AlreadyExists {
769 keyspace: "keyspace_name".to_string(),
770 table: "table_name".to_string(),
771 }
772 );
773 assert_eq!(error.reason, "message 2");
774 }
775
776 #[test]
777 fn deserialize_unprepared() {
778 let features = ProtocolFeatures::default();
779
780 let mut bytes = make_error_request_bytes(0x2500, "message 3");
781 let statement_id = b"deadbeef";
782 bytes.extend((statement_id.len() as i16).to_be_bytes());
783 bytes.extend(statement_id);
784
785 let error: Error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
786
787 assert_eq!(
788 error.error,
789 DbError::Unprepared {
790 statement_id: Bytes::from_static(b"deadbeef")
791 }
792 );
793 assert_eq!(error.reason, "message 3");
794 }
795
796 #[test]
797 fn deserialize_rate_limit_error() {
798 let features = ProtocolFeatures {
799 rate_limit_error: Some(0x4321),
800 ..Default::default()
801 };
802 let mut bytes = make_error_request_bytes(0x4321, "message 1");
803 bytes.extend([0u8]); bytes.extend([1u8]); let error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
806
807 assert_eq!(
808 error.error,
809 DbError::RateLimitReached {
810 op_type: OperationType::Read,
811 rejected_by_coordinator: true,
812 }
813 );
814 assert_eq!(error.reason, "message 1");
815
816 let features = ProtocolFeatures {
817 rate_limit_error: Some(0x8765),
818 ..Default::default()
819 };
820 let mut bytes = make_error_request_bytes(0x8765, "message 2");
821 bytes.extend([1u8]); bytes.extend([0u8]); let error = Error::deserialize(&features, &mut bytes.as_slice()).unwrap();
824
825 assert_eq!(
826 error.error,
827 DbError::RateLimitReached {
828 op_type: OperationType::Write,
829 rejected_by_coordinator: false,
830 }
831 );
832 assert_eq!(error.reason, "message 2");
833 }
834}