tiberius/tds/codec/token/
token_error.rs1use crate::{tds::codec::FeatureLevel, SqlReadBytes};
2use std::fmt;
3
4#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
5pub struct TokenError {
7 pub(crate) code: u32,
9 pub(crate) state: u8,
11 pub(crate) class: u8,
13 pub(crate) message: String,
15 pub(crate) server: String,
16 pub(crate) procedure: String,
17 pub(crate) line: u32,
18}
19
20impl TokenError {
21 pub(crate) async fn decode<R>(src: &mut R) -> crate::Result<Self>
22 where
23 R: SqlReadBytes + Unpin,
24 {
25 let _length = src.read_u16_le().await? as usize;
26
27 let code = src.read_u32_le().await?;
28 let state = src.read_u8().await?;
29 let class = src.read_u8().await?;
30
31 let message = src.read_us_varchar().await?;
32 let server = src.read_b_varchar().await?;
33 let procedure = src.read_b_varchar().await?;
34
35 let line = if src.context().version() >= FeatureLevel::SqlServer2005 {
40 src.read_u32_le().await?
41 } else {
42 src.read_u16_le().await? as u32
43 };
44
45 let token = TokenError {
46 code,
47 state,
48 class,
49 message,
50 server,
51 procedure,
52 line,
53 };
54
55 Ok(token)
56 }
57
58 pub fn code(&self) -> u32 {
62 self.code
63 }
64
65 pub fn state(&self) -> u8 {
67 self.state
68 }
69
70 pub fn class(&self) -> u8 {
73 self.class
74 }
75
76 pub fn message(&self) -> &str {
78 &self.message
79 }
80
81 pub fn server(&self) -> &str {
83 &self.server
84 }
85
86 pub fn procedure(&self) -> &str {
88 &self.procedure
89 }
90
91 pub fn line(&self) -> u32 {
95 self.line
96 }
97}
98
99impl fmt::Display for TokenError {
100 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101 write!(
102 f,
103 "'{}' on server {} executing {} on line {} (code: {}, state: {}, class: {})",
104 self.message, self.server, self.procedure, self.line, self.code, self.state, self.class
105 )
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112
113 fn sample() -> TokenError {
114 TokenError {
115 code: 1205,
116 state: 2,
117 class: 13,
118 message: "deadlocked".to_string(),
119 server: "myserver".to_string(),
120 procedure: "myproc".to_string(),
121 line: 42,
122 }
123 }
124
125 #[test]
126 fn accessors() {
127 let e = sample();
128 assert_eq!(e.code(), 1205);
129 assert_eq!(e.state(), 2);
130 assert_eq!(e.class(), 13);
131 assert_eq!(e.message(), "deadlocked");
132 assert_eq!(e.server(), "myserver");
133 assert_eq!(e.procedure(), "myproc");
134 assert_eq!(e.line(), 42);
135 }
136
137 #[test]
138 fn display_contains_all_fields() {
139 let rendered = format!("{}", sample());
140 assert_eq!(
141 rendered,
142 "'deadlocked' on server myserver executing myproc on line 42 (code: 1205, state: 2, class: 13)"
143 );
144 }
145
146 #[tokio::test]
147 async fn decode_reads_all_fields_with_four_byte_line_number() {
148 use crate::sql_read_bytes::test_utils::IntoSqlReadBytes;
149 use byteorder::{LittleEndian, WriteBytesExt};
150 use bytes::{BufMut, BytesMut};
151
152 fn write_us_varchar(buf: &mut Vec<u8>, s: &str) {
153 buf.write_u16::<LittleEndian>(s.encode_utf16().count() as u16)
154 .unwrap();
155 for u in s.encode_utf16() {
156 buf.write_u16::<LittleEndian>(u).unwrap();
157 }
158 }
159
160 fn write_b_varchar(buf: &mut Vec<u8>, s: &str) {
161 buf.push(s.encode_utf16().count() as u8);
162 for u in s.encode_utf16() {
163 buf.write_u16::<LittleEndian>(u).unwrap();
164 }
165 }
166
167 let mut body = Vec::new();
168 body.write_u32::<LittleEndian>(1205).unwrap(); body.push(2); body.push(13); write_us_varchar(&mut body, "deadlocked");
172 write_b_varchar(&mut body, "myserver");
173 write_b_varchar(&mut body, "myproc");
174 body.write_u32::<LittleEndian>(42).unwrap(); let mut buf = BytesMut::new();
177 buf.put_u16_le(body.len() as u16); buf.put_slice(&body);
179
180 let decoded = TokenError::decode(&mut buf.into_sql_read_bytes())
181 .await
182 .unwrap();
183
184 assert_eq!(decoded, sample());
185 }
186
187 #[tokio::test]
188 async fn decode_reads_full_four_byte_line_number_on_tds72_plus() {
189 use crate::sql_read_bytes::test_utils::IntoSqlReadBytes;
195 use bytes::{BufMut, BytesMut};
196
197 let mut body = BytesMut::new();
198 body.put_u32_le(1205); body.put_u8(2); body.put_u8(13); body.put_u16_le(0); body.put_u8(0); body.put_u8(0); body.put_u32_le(0x0001_0001); let mut buf = BytesMut::new();
207 buf.put_u16_le(body.len() as u16); buf.put_slice(&body);
209
210 let decoded = TokenError::decode(&mut buf.into_sql_read_bytes())
211 .await
212 .unwrap();
213
214 assert_eq!(decoded.line(), 0x0001_0001);
215 }
216}