Skip to main content

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}