tds_driver/tds/codec/token/
token_error.rs1use crate::{tds::codec::TokenInfo, 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: Box<str>,
18 pub(crate) procedure: Box<str>,
19 pub(crate) line: u32,
20 pub(crate) related: Option<Box<RelatedMessages>>,
23}
24
25#[derive(Clone, Debug, Default, PartialEq, Eq)]
28pub(crate) struct RelatedMessages {
29 pub(crate) errors: Vec<TokenError>,
30 pub(crate) infos: Vec<TokenError>,
31}
32
33impl RelatedMessages {
34 pub(crate) fn is_empty(&self) -> bool {
35 self.errors.is_empty() && self.infos.is_empty()
36 }
37}
38
39impl From<&TokenInfo> for TokenError {
40 fn from(info: &TokenInfo) -> Self {
41 Self {
42 code: info.number,
43 state: info.state,
44 class: info.class,
45 message: info.message.clone(),
46 server: info.server.as_str().into(),
47 procedure: info.procedure.as_str().into(),
48 line: info.line,
49 related: None,
50 }
51 }
52}
53
54impl TokenError {
55 pub(crate) async fn decode<R>(src: &mut R) -> crate::Result<Self>
56 where
57 R: SqlReadBytes + Unpin,
58 {
59 let _length = src.read_u16_le().await? as usize;
60
61 let code = src.read_u32_le().await?;
62 let state = src.read_u8().await?;
63 let class = src.read_u8().await?;
64
65 let message = src.read_us_varchar().await?;
66 let server = src.read_b_varchar().await?;
67 let procedure = src.read_b_varchar().await?;
68
69 let line = src.read_u32_le().await?;
72
73 let token = TokenError {
74 code,
75 state,
76 class,
77 message,
78 server: server.into(),
79 procedure: procedure.into(),
80 line,
81 related: None,
82 };
83
84 Ok(token)
85 }
86
87 pub fn code(&self) -> u32 {
91 self.code
92 }
93
94 pub fn state(&self) -> u8 {
96 self.state
97 }
98
99 pub fn class(&self) -> u8 {
102 self.class
103 }
104
105 pub fn message(&self) -> &str {
107 &self.message
108 }
109
110 pub fn server(&self) -> &str {
112 &self.server
113 }
114
115 pub fn procedure(&self) -> &str {
117 &self.procedure
118 }
119
120 pub fn line(&self) -> u32 {
124 self.line
125 }
126
127 pub fn other_errors(&self) -> &[TokenError] {
131 self.related.as_deref().map_or(&[], |r| &r.errors)
132 }
133
134 pub fn info_messages(&self) -> &[TokenError] {
138 self.related.as_deref().map_or(&[], |r| &r.infos)
139 }
140}
141
142impl fmt::Display for TokenError {
143 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
144 write!(
145 f,
146 "'{}' on server {} executing {} on line {} (code: {}, state: {}, class: {})",
147 self.message, self.server, self.procedure, self.line, self.code, self.state, self.class
148 )
149 }
150}
151
152#[cfg(test)]
153mod tests {
154 use super::*;
155
156 fn sample() -> TokenError {
157 TokenError {
158 code: 1205,
159 state: 2,
160 class: 13,
161 message: "deadlocked".to_string(),
162 server: "myserver".into(),
163 procedure: "myproc".into(),
164 line: 42,
165 related: None,
166 }
167 }
168
169 #[test]
170 fn accessors() {
171 let e = sample();
172 assert_eq!(e.code(), 1205);
173 assert_eq!(e.state(), 2);
174 assert_eq!(e.class(), 13);
175 assert_eq!(e.message(), "deadlocked");
176 assert_eq!(e.server(), "myserver");
177 assert_eq!(e.procedure(), "myproc");
178 assert_eq!(e.line(), 42);
179 assert!(e.other_errors().is_empty());
180 assert!(e.info_messages().is_empty());
181 }
182
183 #[test]
184 fn display_contains_all_fields() {
185 let rendered = format!("{}", sample());
186 assert_eq!(
187 rendered,
188 "'deadlocked' on server myserver executing myproc on line 42 (code: 1205, state: 2, class: 13)"
189 );
190 }
191
192 #[tokio::test]
193 async fn decode_reads_all_fields_with_four_byte_line_number() {
194 use crate::sql_read_bytes::test_utils::IntoSqlReadBytes;
195 use byteorder::{LittleEndian, WriteBytesExt};
196 use bytes::{BufMut, BytesMut};
197
198 fn write_us_varchar(buf: &mut Vec<u8>, s: &str) {
199 buf.write_u16::<LittleEndian>(s.encode_utf16().count() as u16)
200 .unwrap();
201 for u in s.encode_utf16() {
202 buf.write_u16::<LittleEndian>(u).unwrap();
203 }
204 }
205
206 fn write_b_varchar(buf: &mut Vec<u8>, s: &str) {
207 buf.push(s.encode_utf16().count() as u8);
208 for u in s.encode_utf16() {
209 buf.write_u16::<LittleEndian>(u).unwrap();
210 }
211 }
212
213 let mut body = Vec::new();
214 body.write_u32::<LittleEndian>(1205).unwrap(); body.push(2); body.push(13); write_us_varchar(&mut body, "deadlocked");
218 write_b_varchar(&mut body, "myserver");
219 write_b_varchar(&mut body, "myproc");
220 body.write_u32::<LittleEndian>(42).unwrap(); let mut buf = BytesMut::new();
223 buf.put_u16_le(body.len() as u16); buf.put_slice(&body);
225
226 let decoded = TokenError::decode(&mut buf.into_sql_read_bytes())
227 .await
228 .unwrap();
229
230 assert_eq!(decoded, sample());
231 }
232
233 #[tokio::test]
234 async fn decode_reads_full_four_byte_line_number_on_tds72_plus() {
235 use crate::sql_read_bytes::test_utils::IntoSqlReadBytes;
237 use bytes::{BufMut, BytesMut};
238
239 let mut body = BytesMut::new();
240 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();
249 buf.put_u16_le(body.len() as u16); buf.put_slice(&body);
251
252 let decoded = TokenError::decode(&mut buf.into_sql_read_bytes())
253 .await
254 .unwrap();
255
256 assert_eq!(decoded.line(), 0x0001_0001);
257 }
258}