cassandra_protocol/frame/
message_error.rs

1use super::Serialize;
2use crate::consistency::Consistency;
3use crate::frame::traits::FromCursor;
4use crate::frame::Version;
5use crate::types::*;
6use crate::{error, Error};
7/// This modules contains [Cassandra's errors](<https://github.com/apache/cassandra/blob/trunk/doc/native_protocol_v4.spec>)
8/// which server could respond to client.
9use derive_more::Display;
10use std::collections::HashMap;
11use std::io::{Cursor, Read};
12use std::net::SocketAddr;
13
14/// CDRS error which could be returned by Cassandra server as a response. As in the specification,
15/// it contains an error code and an error message. Apart of those depending of type of error,
16/// it could contain additional information represented by `additional_info` property.
17#[derive(Debug, PartialEq, Eq, Clone)]
18pub struct ErrorBody {
19    /// Error message.
20    pub message: String,
21    /// The type of error, possibly including type specific additional information.
22    pub ty: ErrorType,
23}
24
25impl Serialize for ErrorBody {
26    fn serialize(&self, cursor: &mut Cursor<&mut Vec<u8>>, version: Version) {
27        self.ty.to_error_code().serialize(cursor, version);
28        serialize_str(cursor, &self.message, version);
29        self.ty.serialize(cursor, version);
30    }
31}
32
33impl FromCursor for ErrorBody {
34    fn from_cursor(cursor: &mut Cursor<&[u8]>, version: Version) -> error::Result<ErrorBody> {
35        let error_code = CInt::from_cursor(cursor, version)?;
36        let message = from_cursor_str(cursor)?.to_string();
37        let ty = ErrorType::from_cursor_with_code(cursor, error_code, version)?;
38
39        Ok(ErrorBody { message, ty })
40    }
41}
42
43impl ErrorBody {
44    /// Is the error related to bad protocol used. This is a special case which is used in some
45    /// situations to detect when a node should not be contacted.
46    pub fn is_bad_protocol(&self) -> bool {
47        // based on ProtocolInitHandler from the Datastax driver
48        (self.ty == ErrorType::Server || self.ty == ErrorType::Protocol)
49            && (self
50                .message
51                .contains("Invalid or unsupported protocol version")
52                || self.message.contains("Beta version of the protocol used"))
53    }
54}
55
56/// Protocol-dependent failure information. V5 contains a map of endpoint->code entries, while
57/// previous versions contain only error count.
58#[derive(Debug, PartialEq, Eq, Clone)]
59#[non_exhaustive]
60pub enum FailureInfo {
61    /// Represents the number of nodes that experience a failure while executing the request.
62    NumFailures(CInt),
63    /// Error code map for affected nodes.
64    ReasonMap(HashMap<SocketAddr, CIntShort>),
65}
66
67impl Serialize for FailureInfo {
68    fn serialize(&self, cursor: &mut Cursor<&mut Vec<u8>>, version: Version) {
69        match self {
70            FailureInfo::NumFailures(count) => count.serialize(cursor, version),
71            FailureInfo::ReasonMap(map) => {
72                let num_failures = map.len() as CInt;
73                num_failures.serialize(cursor, version);
74
75                for (endpoint, error_code) in map {
76                    endpoint.serialize(cursor, version);
77                    error_code.serialize(cursor, version);
78                }
79            }
80        }
81    }
82}
83
84impl FromCursor for FailureInfo {
85    fn from_cursor(cursor: &mut Cursor<&[u8]>, version: Version) -> error::Result<Self> {
86        Ok(match version {
87            Version::V3 | Version::V4 => Self::NumFailures(CInt::from_cursor(cursor, version)?),
88            Version::V5 => {
89                let num_failures = CInt::from_cursor(cursor, version)?;
90                let mut map = HashMap::with_capacity(num_failures as usize);
91
92                for _ in 0..num_failures {
93                    let endpoint = SocketAddr::from_cursor(cursor, version)?;
94                    let error_code = CIntShort::from_cursor(cursor, version)?;
95                    map.insert(endpoint, error_code);
96                }
97
98                Self::ReasonMap(map)
99            }
100        })
101    }
102}
103
104/// Additional error info in accordance to
105/// [Cassandra protocol v4](<https://github.com/apache/cassandra/blob/trunk/doc/native_protocol_v4.spec>).
106#[derive(Debug, PartialEq, Eq, Clone)]
107#[non_exhaustive]
108pub enum ErrorType {
109    Server,
110    Protocol,
111    Authentication,
112    Unavailable(UnavailableError),
113    Overloaded,
114    IsBootstrapping,
115    Truncate,
116    WriteTimeout(WriteTimeoutError),
117    ReadTimeout(ReadTimeoutError),
118    ReadFailure(ReadFailureError),
119    FunctionFailure(FunctionFailureError),
120    WriteFailure(WriteFailureError),
121    Syntax,
122    Unauthorized,
123    Invalid,
124    Config,
125    AlreadyExists(AlreadyExistsError),
126    Unprepared(UnpreparedError),
127}
128
129impl Serialize for ErrorType {
130    fn serialize(&self, cursor: &mut Cursor<&mut Vec<u8>>, version: Version) {
131        match self {
132            ErrorType::Unavailable(unavailable) => unavailable.serialize(cursor, version),
133            ErrorType::WriteTimeout(write_timeout) => write_timeout.serialize(cursor, version),
134            ErrorType::ReadTimeout(read_timeout) => read_timeout.serialize(cursor, version),
135            ErrorType::ReadFailure(read_failure) => read_failure.serialize(cursor, version),
136            ErrorType::FunctionFailure(function_failure) => {
137                function_failure.serialize(cursor, version)
138            }
139            ErrorType::WriteFailure(write_failure) => write_failure.serialize(cursor, version),
140            ErrorType::AlreadyExists(already_exists) => already_exists.serialize(cursor, version),
141            ErrorType::Unprepared(unprepared) => unprepared.serialize(cursor, version),
142            _ => {}
143        }
144    }
145}
146
147impl ErrorType {
148    pub fn from_cursor_with_code(
149        cursor: &mut Cursor<&[u8]>,
150        error_code: CInt,
151        version: Version,
152    ) -> error::Result<ErrorType> {
153        match error_code {
154            0x0000 => Ok(ErrorType::Server),
155            0x000A => Ok(ErrorType::Protocol),
156            0x0100 => Ok(ErrorType::Authentication),
157            0x1000 => UnavailableError::from_cursor(cursor, version).map(ErrorType::Unavailable),
158            0x1001 => Ok(ErrorType::Overloaded),
159            0x1002 => Ok(ErrorType::IsBootstrapping),
160            0x1003 => Ok(ErrorType::Truncate),
161            0x1100 => WriteTimeoutError::from_cursor(cursor, version).map(ErrorType::WriteTimeout),
162            0x1200 => ReadTimeoutError::from_cursor(cursor, version).map(ErrorType::ReadTimeout),
163            0x1300 => ReadFailureError::from_cursor(cursor, version).map(ErrorType::ReadFailure),
164            0x1400 => {
165                FunctionFailureError::from_cursor(cursor, version).map(ErrorType::FunctionFailure)
166            }
167            0x1500 => WriteFailureError::from_cursor(cursor, version).map(ErrorType::WriteFailure),
168            0x2000 => Ok(ErrorType::Syntax),
169            0x2100 => Ok(ErrorType::Unauthorized),
170            0x2200 => Ok(ErrorType::Invalid),
171            0x2300 => Ok(ErrorType::Config),
172            0x2400 => {
173                AlreadyExistsError::from_cursor(cursor, version).map(ErrorType::AlreadyExists)
174            }
175            0x2500 => UnpreparedError::from_cursor(cursor, version).map(ErrorType::Unprepared),
176            _ => Err(Error::UnexpectedErrorCode(error_code)),
177        }
178    }
179
180    pub fn to_error_code(&self) -> CInt {
181        match self {
182            ErrorType::Server => 0x0000,
183            ErrorType::Protocol => 0x000A,
184            ErrorType::Authentication => 0x0100,
185            ErrorType::Unavailable(_) => 0x1000,
186            ErrorType::Overloaded => 0x1001,
187            ErrorType::IsBootstrapping => 0x1002,
188            ErrorType::Truncate => 0x1003,
189            ErrorType::WriteTimeout(_) => 0x1100,
190            ErrorType::ReadTimeout(_) => 0x1200,
191            ErrorType::ReadFailure(_) => 0x1300,
192            ErrorType::FunctionFailure(_) => 0x1400,
193            ErrorType::WriteFailure(_) => 0x1500,
194            ErrorType::Syntax => 0x2000,
195            ErrorType::Unauthorized => 0x2100,
196            ErrorType::Invalid => 0x2200,
197            ErrorType::Config => 0x2300,
198            ErrorType::AlreadyExists(_) => 0x2400,
199            ErrorType::Unprepared(_) => 0x2500,
200        }
201    }
202}
203
204/// Additional info about
205/// [unavailable exception](<https://github.com/apache/cassandra/blob/trunk/doc/native_protocol_v4.spec>)
206#[derive(Debug, PartialEq, Ord, PartialOrd, Eq, Copy, Clone, Hash)]
207pub struct UnavailableError {
208    /// Consistency level of query.
209    pub cl: Consistency,
210    /// Number of nodes that should be available to respect `cl`.
211    pub required: CInt,
212    /// Number of replicas that we were know to be alive.
213    pub alive: CInt,
214}
215
216impl Serialize for UnavailableError {
217    fn serialize(&self, cursor: &mut Cursor<&mut Vec<u8>>, version: Version) {
218        self.cl.serialize(cursor, version);
219        self.required.serialize(cursor, version);
220        self.alive.serialize(cursor, version);
221    }
222}
223
224impl FromCursor for UnavailableError {
225    fn from_cursor(
226        cursor: &mut Cursor<&[u8]>,
227        version: Version,
228    ) -> error::Result<UnavailableError> {
229        let cl = Consistency::from_cursor(cursor, version)?;
230        let required = CInt::from_cursor(cursor, version)?;
231        let alive = CInt::from_cursor(cursor, version)?;
232
233        Ok(UnavailableError {
234            cl,
235            required,
236            alive,
237        })
238    }
239}
240
241/// Timeout exception during a write request.
242#[derive(Debug, PartialEq, Clone, Ord, PartialOrd, Eq, Hash)]
243pub struct WriteTimeoutError {
244    /// Consistency level of query.
245    pub cl: Consistency,
246    /// `i32` representing the number of nodes having acknowledged the request.
247    pub received: CInt,
248    /// `i32` representing the number of replicas whose acknowledgement is required to achieve `cl`.
249    pub block_for: CInt,
250    /// Describes the type of the write that timed out.
251    pub write_type: WriteType,
252    /// The number of contentions occurred during the CAS operation. The field only presents when
253    /// the `write_type` is `Cas`.
254    pub contentions: Option<CIntShort>,
255}
256
257impl Serialize for WriteTimeoutError {
258    fn serialize(&self, cursor: &mut Cursor<&mut Vec<u8>>, version: Version) {
259        self.cl.serialize(cursor, version);
260        self.received.serialize(cursor, version);
261        self.block_for.serialize(cursor, version);
262        self.write_type.serialize(cursor, version);
263
264        if let Some(contentions) = self.contentions {
265            contentions.serialize(cursor, version);
266        }
267    }
268}
269
270impl FromCursor for WriteTimeoutError {
271    fn from_cursor(
272        cursor: &mut Cursor<&[u8]>,
273        version: Version,
274    ) -> error::Result<WriteTimeoutError> {
275        let cl = Consistency::from_cursor(cursor, version)?;
276        let received = CInt::from_cursor(cursor, version)?;
277        let block_for = CInt::from_cursor(cursor, version)?;
278        let write_type = WriteType::from_cursor(cursor, version)?;
279        let contentions = if write_type == WriteType::Cas {
280            Some(CIntShort::from_cursor(cursor, version)?)
281        } else {
282            None
283        };
284
285        Ok(WriteTimeoutError {
286            cl,
287            received,
288            block_for,
289            write_type,
290            contentions,
291        })
292    }
293}
294
295/// Timeout exception during a read request.
296#[derive(Debug, PartialEq, Ord, PartialOrd, Eq, Copy, Clone, Hash)]
297pub struct ReadTimeoutError {
298    /// Consistency level of query.
299    pub cl: Consistency,
300    /// `i32` representing the number of nodes having acknowledged the request.
301    pub received: CInt,
302    /// `i32` representing the number of replicas whose acknowledgement is required to achieve `cl`.
303    pub block_for: CInt,
304    data_present: u8,
305}
306
307impl Serialize for ReadTimeoutError {
308    fn serialize(&self, cursor: &mut Cursor<&mut Vec<u8>>, version: Version) {
309        self.cl.serialize(cursor, version);
310        self.received.serialize(cursor, version);
311        self.block_for.serialize(cursor, version);
312        self.data_present.serialize(cursor, version);
313    }
314}
315
316impl ReadTimeoutError {
317    /// Shows if a replica has responded to a query.
318    #[inline]
319    pub fn replica_has_responded(&self) -> bool {
320        self.data_present != 0
321    }
322}
323
324impl FromCursor for ReadTimeoutError {
325    fn from_cursor(
326        cursor: &mut Cursor<&[u8]>,
327        version: Version,
328    ) -> error::Result<ReadTimeoutError> {
329        let cl = Consistency::from_cursor(cursor, version)?;
330        let received = CInt::from_cursor(cursor, version)?;
331        let block_for = CInt::from_cursor(cursor, version)?;
332
333        let mut buff = [0];
334        cursor.read_exact(&mut buff)?;
335
336        let data_present = buff[0];
337
338        Ok(ReadTimeoutError {
339            cl,
340            received,
341            block_for,
342            data_present,
343        })
344    }
345}
346
347/// A non-timeout exception during a read request.
348#[derive(Debug, PartialEq, Eq, Clone)]
349pub struct ReadFailureError {
350    /// Consistency level of query.
351    pub cl: Consistency,
352    /// The number of nodes having acknowledged the request.
353    pub received: CInt,
354    /// The number of replicas whose acknowledgement is required to achieve `cl`.
355    pub block_for: CInt,
356    /// Failure information.
357    pub failure_info: FailureInfo,
358    data_present: u8,
359}
360
361impl Serialize for ReadFailureError {
362    fn serialize(&self, cursor: &mut Cursor<&mut Vec<u8>>, version: Version) {
363        self.cl.serialize(cursor, version);
364        self.received.serialize(cursor, version);
365        self.block_for.serialize(cursor, version);
366        self.failure_info.serialize(cursor, version);
367        self.data_present.serialize(cursor, version);
368    }
369}
370
371impl ReadFailureError {
372    /// Shows if replica has responded to a query.
373    #[inline]
374    pub fn replica_has_responded(&self) -> bool {
375        self.data_present != 0
376    }
377}
378
379impl FromCursor for ReadFailureError {
380    fn from_cursor(
381        cursor: &mut Cursor<&[u8]>,
382        version: Version,
383    ) -> error::Result<ReadFailureError> {
384        let cl = Consistency::from_cursor(cursor, version)?;
385        let received = CInt::from_cursor(cursor, version)?;
386        let block_for = CInt::from_cursor(cursor, version)?;
387        let failure_info = FailureInfo::from_cursor(cursor, version)?;
388
389        let mut buff = [0];
390        cursor.read_exact(&mut buff)?;
391
392        let data_present = buff[0];
393
394        Ok(ReadFailureError {
395            cl,
396            received,
397            block_for,
398            failure_info,
399            data_present,
400        })
401    }
402}
403
404/// A (user defined) function failed during execution.
405#[derive(Debug, PartialEq, Ord, PartialOrd, Eq, Hash, Clone)]
406pub struct FunctionFailureError {
407    /// The keyspace of the failed function.
408    pub keyspace: String,
409    /// The name of the failed function
410    pub function: String,
411    /// One string for each argument type (as CQL type) of the failed function.
412    pub arg_types: Vec<String>,
413}
414
415impl Serialize for FunctionFailureError {
416    fn serialize(&self, cursor: &mut Cursor<&mut Vec<u8>>, version: Version) {
417        serialize_str(cursor, &self.keyspace, version);
418        serialize_str(cursor, &self.function, version);
419        serialize_str_list(cursor, self.arg_types.iter().map(|x| x.as_str()), version);
420    }
421}
422
423impl FromCursor for FunctionFailureError {
424    fn from_cursor(
425        cursor: &mut Cursor<&[u8]>,
426        _version: Version,
427    ) -> error::Result<FunctionFailureError> {
428        let keyspace = from_cursor_str(cursor)?.to_string();
429        let function = from_cursor_str(cursor)?.to_string();
430        let arg_types = from_cursor_string_list(cursor)?;
431
432        Ok(FunctionFailureError {
433            keyspace,
434            function,
435            arg_types,
436        })
437    }
438}
439
440/// A non-timeout exception during a write request.
441#[derive(Debug, PartialEq, Eq, Clone)]
442pub struct WriteFailureError {
443    /// Consistency of the query having triggered the exception.
444    pub cl: Consistency,
445    /// The number of nodes having answered the request.
446    pub received: CInt,
447    /// The number of replicas whose acknowledgement is required to achieve `cl`.
448    pub block_for: CInt,
449    /// Failure information.
450    pub failure_info: FailureInfo,
451    /// describes the type of the write that failed.
452    pub write_type: WriteType,
453}
454
455impl Serialize for WriteFailureError {
456    fn serialize(&self, cursor: &mut Cursor<&mut Vec<u8>>, version: Version) {
457        self.cl.serialize(cursor, version);
458        self.received.serialize(cursor, version);
459        self.block_for.serialize(cursor, version);
460        self.failure_info.serialize(cursor, version);
461        self.write_type.serialize(cursor, version);
462    }
463}
464
465impl FromCursor for WriteFailureError {
466    fn from_cursor(
467        cursor: &mut Cursor<&[u8]>,
468        version: Version,
469    ) -> error::Result<WriteFailureError> {
470        let cl = Consistency::from_cursor(cursor, version)?;
471        let received = CInt::from_cursor(cursor, version)?;
472        let block_for = CInt::from_cursor(cursor, version)?;
473        let failure_info = FailureInfo::from_cursor(cursor, version)?;
474        let write_type = WriteType::from_cursor(cursor, version)?;
475
476        Ok(WriteFailureError {
477            cl,
478            received,
479            block_for,
480            failure_info,
481            write_type,
482        })
483    }
484}
485
486/// Describes the type of the write that failed.
487/// [Read more...](https://github.com/apache/cassandra/blob/trunk/doc/native_protocol_v4.spec#L1118)
488#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Clone, Display)]
489#[non_exhaustive]
490pub enum WriteType {
491    /// The write was a non-batched non-counter write.
492    Simple,
493    /// The write was a (logged) batch write. If this type is received, it means the batch log
494    /// has been successfully written.
495    Batch,
496    /// The write was an unlogged batch. No batch log write has been attempted.
497    UnloggedBatch,
498    /// The write was a counter write (batched or not).
499    Counter,
500    /// The failure occurred during the write to the batch log when a (logged) batch
501    /// write was requested.
502    BatchLog,
503    /// The timeout occurred during the Compare And Set write/update.
504    Cas,
505    /// The timeout occurred when a write involves VIEW update and failure to acquire local view(MV)
506    /// lock for key within timeout.
507    View,
508    /// The timeout occurred when cdc_total_space is exceeded when doing a write to data tracked by
509    /// cdc.
510    Cdc,
511    /// Unknown write type.
512    Unknown(String),
513}
514
515impl Serialize for WriteType {
516    fn serialize(&self, cursor: &mut Cursor<&mut Vec<u8>>, version: Version) {
517        match self {
518            WriteType::Simple => serialize_str(cursor, "SIMPLE", version),
519            WriteType::Batch => serialize_str(cursor, "BATCH", version),
520            WriteType::UnloggedBatch => serialize_str(cursor, "UNLOGGED_BATCH", version),
521            WriteType::Counter => serialize_str(cursor, "COUNTER", version),
522            WriteType::BatchLog => serialize_str(cursor, "BATCH_LOG", version),
523            WriteType::Cas => serialize_str(cursor, "CAS", version),
524            WriteType::View => serialize_str(cursor, "VIEW", version),
525            WriteType::Cdc => serialize_str(cursor, "CDC", version),
526            WriteType::Unknown(write_type) => serialize_str(cursor, write_type, version),
527        }
528    }
529}
530
531impl FromCursor for WriteType {
532    fn from_cursor(cursor: &mut Cursor<&[u8]>, _version: Version) -> error::Result<WriteType> {
533        match from_cursor_str(cursor)? {
534            "SIMPLE" => Ok(WriteType::Simple),
535            "BATCH" => Ok(WriteType::Batch),
536            "UNLOGGED_BATCH" => Ok(WriteType::UnloggedBatch),
537            "COUNTER" => Ok(WriteType::Counter),
538            "BATCH_LOG" => Ok(WriteType::BatchLog),
539            "CAS" => Ok(WriteType::Cas),
540            "VIEW" => Ok(WriteType::View),
541            "CDC" => Ok(WriteType::Cdc),
542            wt => Ok(WriteType::Unknown(wt.into())),
543        }
544    }
545}
546
547/// The query attempted to create a keyspace or a table that was already existing.
548/// [Read more...](https://github.com/apache/cassandra/blob/trunk/doc/native_protocol_v4.spec#L1140)
549#[derive(Debug, PartialEq, Ord, PartialOrd, Eq, Hash, Clone)]
550pub struct AlreadyExistsError {
551    /// Represents either the keyspace that already exists,
552    /// or the keyspace in which the table that already exists is.
553    pub ks: String,
554    /// Represents the name of the table that already exists.
555    pub table: String,
556}
557
558impl Serialize for AlreadyExistsError {
559    fn serialize(&self, cursor: &mut Cursor<&mut Vec<u8>>, version: Version) {
560        serialize_str(cursor, &self.ks, version);
561        serialize_str(cursor, &self.table, version);
562    }
563}
564
565impl FromCursor for AlreadyExistsError {
566    fn from_cursor(
567        cursor: &mut Cursor<&[u8]>,
568        _version: Version,
569    ) -> error::Result<AlreadyExistsError> {
570        let ks = from_cursor_str(cursor)?.to_string();
571        let table = from_cursor_str(cursor)?.to_string();
572
573        Ok(AlreadyExistsError { ks, table })
574    }
575}
576
577/// Can be thrown while a prepared statement tries to be
578/// executed if the provided prepared statement ID is not known by
579/// this host. [Read more...](<https://github.com/apache/cassandra/blob/trunk/doc/native_protocol_v4.spec>)
580#[derive(Debug, PartialEq, Ord, PartialOrd, Eq, Hash, Clone)]
581pub struct UnpreparedError {
582    /// Unknown ID.
583    pub id: CBytesShort,
584}
585
586impl Serialize for UnpreparedError {
587    fn serialize(&self, cursor: &mut Cursor<&mut Vec<u8>>, version: Version) {
588        self.id.serialize(cursor, version);
589    }
590}
591
592impl FromCursor for UnpreparedError {
593    fn from_cursor(cursor: &mut Cursor<&[u8]>, version: Version) -> error::Result<UnpreparedError> {
594        let id = CBytesShort::from_cursor(cursor, version)?;
595        Ok(UnpreparedError { id })
596    }
597}
598
599//noinspection DuplicatedCode
600#[cfg(test)]
601fn test_encode_decode(bytes: &[u8], expected: ErrorBody) {
602    {
603        let mut cursor: Cursor<&[u8]> = Cursor::new(bytes);
604        let result = ErrorBody::from_cursor(&mut cursor, Version::V4).unwrap();
605        assert_eq!(expected, result);
606    }
607
608    {
609        let mut buffer = Vec::new();
610        let mut cursor = Cursor::new(&mut buffer);
611        expected.serialize(&mut cursor, Version::V4);
612        assert_eq!(buffer, bytes);
613    }
614}
615
616#[cfg(test)]
617mod error_tests {
618    use super::*;
619
620    #[test]
621    fn server() {
622        let bytes = &[
623            0, 0, 0, 0, // server
624            0, 3, 102, 111, 111, // message - foo
625        ];
626        let expected = ErrorBody {
627            message: "foo".into(),
628            ty: ErrorType::Server,
629        };
630        test_encode_decode(bytes, expected);
631    }
632
633    #[test]
634    fn protocol() {
635        let bytes = &[
636            0, 0, 0, 10, // protocol
637            0, 3, 102, 111, 111, // message - foo
638        ];
639        let expected = ErrorBody {
640            message: "foo".into(),
641            ty: ErrorType::Protocol,
642        };
643        test_encode_decode(bytes, expected);
644    }
645
646    #[test]
647    fn authentication() {
648        let bytes = &[
649            0, 0, 1, 0, // authentication error
650            0, 3, 102, 111, 111, // message - foo
651        ];
652        let expected = ErrorBody {
653            message: "foo".into(),
654            ty: ErrorType::Authentication,
655        };
656        test_encode_decode(bytes, expected);
657    }
658
659    #[test]
660    fn unavailable() {
661        let bytes = &[
662            0, 0, 16, 0, // unavailable
663            0, 3, 102, 111, 111, // message - foo
664            //
665            // unavailable error
666            0, 0, // consistency any
667            0, 0, 0, 1, // required
668            0, 0, 0, 1, // alive
669        ];
670        let expected = ErrorBody {
671            message: "foo".into(),
672            ty: ErrorType::Unavailable(UnavailableError {
673                cl: Consistency::Any,
674                required: 1,
675                alive: 1,
676            }),
677        };
678        test_encode_decode(bytes, expected);
679    }
680
681    #[test]
682    fn overloaded() {
683        let bytes = &[
684            0, 0, 16, 1, // authentication error
685            0, 3, 102, 111, 111, // message - foo
686        ];
687        let expected = ErrorBody {
688            message: "foo".into(),
689            ty: ErrorType::Overloaded,
690        };
691        test_encode_decode(bytes, expected);
692    }
693
694    #[test]
695    fn is_bootstrapping() {
696        let bytes = &[
697            0, 0, 16, 2, // is bootstrapping
698            0, 3, 102, 111, 111, // message - foo
699        ];
700        let expected = ErrorBody {
701            message: "foo".into(),
702            ty: ErrorType::IsBootstrapping,
703        };
704        test_encode_decode(bytes, expected);
705    }
706
707    #[test]
708    fn truncate() {
709        let bytes = &[
710            0, 0, 16, 3, // truncate
711            0, 3, 102, 111, 111, // message - foo
712        ];
713        let expected = ErrorBody {
714            message: "foo".into(),
715            ty: ErrorType::Truncate,
716        };
717        test_encode_decode(bytes, expected);
718    }
719
720    #[test]
721    fn write_timeout() {
722        let bytes = &[
723            0, 0, 17, 0, // write timeout
724            0, 3, 102, 111, 111, // message - foo
725            //
726            // timeout error
727            0, 0, // consistency any
728            0, 0, 0, 1, // received
729            0, 0, 0, 1, // block_for
730            0, 6, 83, 73, 77, 80, 76, 69, // Write type simple
731        ];
732        let expected = ErrorBody {
733            message: "foo".into(),
734            ty: ErrorType::WriteTimeout(WriteTimeoutError {
735                cl: Consistency::Any,
736                received: 1,
737                block_for: 1,
738                write_type: WriteType::Simple,
739                contentions: None,
740            }),
741        };
742        test_encode_decode(bytes, expected);
743    }
744
745    #[test]
746    fn read_timeout() {
747        let bytes = &[
748            0, 0, 18, 0, // read timeout
749            0, 3, 102, 111, 111, // message - foo
750            //
751            // read timeout
752            0, 0, // consistency any
753            0, 0, 0, 1, // received
754            0, 0, 0, 1, // block_for
755            0, // data present
756        ];
757        let expected = ErrorBody {
758            message: "foo".into(),
759            ty: ErrorType::ReadTimeout(ReadTimeoutError {
760                cl: Consistency::Any,
761                received: 1,
762                block_for: 1,
763                data_present: 0,
764            }),
765        };
766        test_encode_decode(bytes, expected);
767    }
768
769    #[test]
770    fn read_failure() {
771        let bytes = &[
772            0, 0, 19, 0, // read failure
773            0, 3, 102, 111, 111, // message - foo
774            //
775            // read timeout
776            0, 0, // consistency any
777            0, 0, 0, 1, // received
778            0, 0, 0, 1, // block_for
779            0, 0, 0, 1, // num failure
780            0, // data present
781        ];
782        let expected = ErrorBody {
783            message: "foo".into(),
784            ty: ErrorType::ReadFailure(ReadFailureError {
785                cl: Consistency::Any,
786                received: 1,
787                block_for: 1,
788                failure_info: FailureInfo::NumFailures(1),
789                data_present: 0,
790            }),
791        };
792        test_encode_decode(bytes, expected);
793    }
794
795    #[test]
796    fn syntax() {
797        let bytes = &[
798            0, 0, 32, 0, // syntax
799            0, 3, 102, 111, 111, // message - foo
800        ];
801        let expected = ErrorBody {
802            message: "foo".into(),
803            ty: ErrorType::Syntax,
804        };
805        test_encode_decode(bytes, expected);
806    }
807
808    #[test]
809    fn unauthorized() {
810        let bytes = &[
811            0, 0, 33, 0, // unauthorized
812            0, 3, 102, 111, 111, // message - foo
813        ];
814        let expected = ErrorBody {
815            message: "foo".into(),
816            ty: ErrorType::Unauthorized,
817        };
818        test_encode_decode(bytes, expected);
819    }
820
821    #[test]
822    fn invalid() {
823        let bytes = &[
824            0, 0, 34, 0, // invalid
825            0, 3, 102, 111, 111, // message - foo
826        ];
827        let expected = ErrorBody {
828            message: "foo".into(),
829            ty: ErrorType::Invalid,
830        };
831        test_encode_decode(bytes, expected);
832    }
833
834    #[test]
835    fn config() {
836        let bytes = &[
837            0, 0, 35, 0, // config
838            0, 3, 102, 111, 111, // message - foo
839        ];
840        let expected = ErrorBody {
841            message: "foo".into(),
842            ty: ErrorType::Config,
843        };
844        test_encode_decode(bytes, expected);
845    }
846}