tds_protocol/error.rs
1//! Protocol-level error types.
2
3use thiserror::Error;
4
5/// Errors that can occur during TDS protocol parsing or encoding.
6#[derive(Debug, Error)]
7#[non_exhaustive]
8pub enum ProtocolError {
9 /// Packet data is truncated or incomplete.
10 #[error("incomplete packet: expected {expected} bytes, got {actual}")]
11 IncompletePacket {
12 /// Expected number of bytes.
13 expected: usize,
14 /// Actual number of bytes available.
15 actual: usize,
16 },
17
18 /// Invalid packet type value.
19 #[error("invalid packet type: {0:#x}")]
20 InvalidPacketType(u8),
21
22 /// Invalid token type value.
23 #[error("invalid token type: {0:#x}")]
24 InvalidTokenType(u8),
25
26 /// Invalid data type value.
27 #[error("invalid data type: {0:#x}")]
28 InvalidDataType(u8),
29
30 /// Invalid prelogin option.
31 #[error("invalid prelogin option: {0:#x}")]
32 InvalidPreloginOption(u8),
33
34 /// Invalid PRELOGIN encryption level byte.
35 #[error("invalid encryption level: {0:#x}")]
36 InvalidEncryptionLevel(u8),
37
38 /// Invalid TDS version.
39 #[error("invalid TDS version: {0:#x}")]
40 InvalidTdsVersion(u32),
41
42 /// String encoding error.
43 #[error("string encoding error: {0}")]
44 StringEncoding(
45 #[cfg(feature = "std")] String,
46 #[cfg(not(feature = "std"))] &'static str,
47 ),
48
49 /// Packet length exceeds maximum allowed.
50 #[error("packet too large: {length} bytes (max {max})")]
51 PacketTooLarge {
52 /// Actual packet length.
53 length: usize,
54 /// Maximum allowed length.
55 max: usize,
56 },
57
58 /// Invalid packet status flags.
59 #[error("invalid packet status: {0:#x}")]
60 InvalidPacketStatus(u8),
61
62 /// Buffer overflow during encoding.
63 #[error("buffer overflow: needed {needed} bytes, capacity {capacity}")]
64 BufferOverflow {
65 /// Bytes needed.
66 needed: usize,
67 /// Buffer capacity.
68 capacity: usize,
69 },
70
71 /// Unexpected end of stream.
72 #[error("unexpected end of stream")]
73 UnexpectedEof,
74
75 /// Protocol version mismatch.
76 #[error("unsupported protocol version: {0}")]
77 UnsupportedVersion(u32),
78
79 /// Invalid field value in a protocol structure.
80 #[error("invalid {field} value: {value}")]
81 InvalidField {
82 /// Field name.
83 field: &'static str,
84 /// Invalid value.
85 value: u32,
86 },
87}
88
89impl ProtocolError {
90 /// Check if this error is transient and may succeed on retry.
91 ///
92 /// Protocol errors are always terminal — they indicate malformed data or
93 /// driver bugs. In particular `UnexpectedEof` is produced when a token
94 /// inside a fully-received message is truncated or misparsed; retrying
95 /// deterministically fails again. Genuine connection loss surfaces at the
96 /// transport layer (`CodecError::Io` / `CodecError::ConnectionClosed`),
97 /// which remains transient.
98 #[must_use]
99 pub fn is_transient(&self) -> bool {
100 false
101 }
102
103 /// Check if this error is terminal and will never succeed on retry.
104 ///
105 /// Most protocol errors indicate a fundamental mismatch between client
106 /// and server, a driver bug, or corrupted data.
107 #[must_use]
108 pub fn is_terminal(&self) -> bool {
109 !self.is_transient()
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 /// Issue #160 regression: `UnexpectedEof` is produced for malformed
118 /// tokens inside a fully-received buffer (dozens of parse sites), so it
119 /// must not be classified retryable — retry layers honoring
120 /// `is_transient` would re-run a deterministically failing parse.
121 #[test]
122 fn unexpected_eof_is_terminal_not_transient() {
123 assert!(!ProtocolError::UnexpectedEof.is_transient());
124 assert!(ProtocolError::UnexpectedEof.is_terminal());
125 }
126}