1#![allow(clippy::expect_used)]
8
9use bytes::{BufMut, BytesMut};
10
11use crate::error::TypeError;
12use crate::value::SqlValue;
13
14pub trait TdsEncode {
16 fn encode(&self, buf: &mut BytesMut) -> Result<(), TypeError>;
18
19 fn type_id(&self) -> u8;
21}
22
23impl TdsEncode for SqlValue {
24 fn encode(&self, buf: &mut BytesMut) -> Result<(), TypeError> {
25 match self {
26 SqlValue::Null => {
27 Ok(())
30 }
31 SqlValue::Bool(v) => {
32 buf.put_u8(if *v { 1 } else { 0 });
33 Ok(())
34 }
35 SqlValue::TinyInt(v) => {
36 buf.put_u8(*v);
37 Ok(())
38 }
39 SqlValue::SmallInt(v) => {
40 buf.put_i16_le(*v);
41 Ok(())
42 }
43 SqlValue::Int(v) => {
44 buf.put_i32_le(*v);
45 Ok(())
46 }
47 SqlValue::BigInt(v) => {
48 buf.put_i64_le(*v);
49 Ok(())
50 }
51 SqlValue::Float(v) => {
52 buf.put_f32_le(*v);
53 Ok(())
54 }
55 SqlValue::Double(v) => {
56 buf.put_f64_le(*v);
57 Ok(())
58 }
59 SqlValue::String(s) => {
60 encode_utf16_string(s, buf);
62 Ok(())
63 }
64 SqlValue::Binary(b) => {
65 if b.len() > u16::MAX as usize {
67 return Err(TypeError::BufferTooSmall {
68 needed: b.len(),
69 available: u16::MAX as usize,
70 });
71 }
72 buf.put_u16_le(b.len() as u16);
73 buf.put_slice(b);
74 Ok(())
75 }
76 #[cfg(feature = "decimal")]
77 SqlValue::Decimal(d) => {
78 encode_decimal(*d, buf);
79 Ok(())
80 }
81 #[cfg(feature = "uuid")]
82 SqlValue::Uuid(u) => {
83 encode_uuid(*u, buf);
84 Ok(())
85 }
86 #[cfg(feature = "chrono")]
87 SqlValue::Date(d) => {
88 encode_date(*d, buf);
89 Ok(())
90 }
91 #[cfg(feature = "chrono")]
92 SqlValue::Time(t) => {
93 encode_time(*t, buf);
94 Ok(())
95 }
96 #[cfg(feature = "chrono")]
97 SqlValue::DateTime(dt) => {
98 encode_datetime2(*dt, buf);
99 Ok(())
100 }
101 #[cfg(feature = "chrono")]
102 SqlValue::DateTimeOffset(dto) => {
103 encode_datetimeoffset(*dto, buf);
104 Ok(())
105 }
106 #[cfg(feature = "json")]
107 SqlValue::Json(j) => {
108 let s = j.to_string();
110 encode_utf16_string(&s, buf);
111 Ok(())
112 }
113 SqlValue::Xml(x) => {
114 encode_utf16_string(x, buf);
116 Ok(())
117 }
118 }
119 }
120
121 fn type_id(&self) -> u8 {
122 match self {
123 SqlValue::Null => 0x1F, SqlValue::Bool(_) => 0x32, SqlValue::TinyInt(_) => 0x30, SqlValue::SmallInt(_) => 0x34, SqlValue::Int(_) => 0x38, SqlValue::BigInt(_) => 0x7F, SqlValue::Float(_) => 0x3B, SqlValue::Double(_) => 0x3E, SqlValue::String(_) => 0xE7, SqlValue::Binary(_) => 0xA5, #[cfg(feature = "decimal")]
134 SqlValue::Decimal(_) => 0x6C, #[cfg(feature = "uuid")]
136 SqlValue::Uuid(_) => 0x24, #[cfg(feature = "chrono")]
138 SqlValue::Date(_) => 0x28, #[cfg(feature = "chrono")]
140 SqlValue::Time(_) => 0x29, #[cfg(feature = "chrono")]
142 SqlValue::DateTime(_) => 0x2A, #[cfg(feature = "chrono")]
144 SqlValue::DateTimeOffset(_) => 0x2B, #[cfg(feature = "json")]
146 SqlValue::Json(_) => 0xE7, SqlValue::Xml(_) => 0xF1, }
149 }
150}
151
152pub fn encode_utf16_string(s: &str, buf: &mut BytesMut) {
154 let utf16: Vec<u16> = s.encode_utf16().collect();
155 let byte_len = utf16.len() * 2;
156
157 buf.put_u16_le(byte_len as u16);
159
160 for code_unit in utf16 {
162 buf.put_u16_le(code_unit);
163 }
164}
165
166pub fn encode_utf16_string_no_len(s: &str, buf: &mut BytesMut) {
168 for code_unit in s.encode_utf16() {
169 buf.put_u16_le(code_unit);
170 }
171}
172
173#[cfg(feature = "uuid")]
181pub fn encode_uuid(uuid: uuid::Uuid, buf: &mut BytesMut) {
182 let bytes = uuid.as_bytes();
183
184 buf.put_u8(bytes[3]);
186 buf.put_u8(bytes[2]);
187 buf.put_u8(bytes[1]);
188 buf.put_u8(bytes[0]);
189
190 buf.put_u8(bytes[5]);
192 buf.put_u8(bytes[4]);
193
194 buf.put_u8(bytes[7]);
196 buf.put_u8(bytes[6]);
197
198 buf.put_slice(&bytes[8..16]);
200}
201
202#[cfg(feature = "decimal")]
208pub fn encode_decimal(decimal: rust_decimal::Decimal, buf: &mut BytesMut) {
209 let sign = if decimal.is_sign_negative() { 0u8 } else { 1u8 };
210 buf.put_u8(sign);
211
212 let mantissa = decimal.mantissa().unsigned_abs();
214 buf.put_u128_le(mantissa);
215}
216
217#[cfg(feature = "chrono")]
221pub fn encode_date(date: chrono::NaiveDate, buf: &mut BytesMut) {
222 let base = chrono::NaiveDate::from_ymd_opt(1, 1, 1).expect("valid date");
224 let days = date.signed_duration_since(base).num_days() as u32;
225
226 buf.put_u8((days & 0xFF) as u8);
228 buf.put_u8(((days >> 8) & 0xFF) as u8);
229 buf.put_u8(((days >> 16) & 0xFF) as u8);
230}
231
232#[cfg(feature = "chrono")]
236pub fn encode_time(time: chrono::NaiveTime, buf: &mut BytesMut) {
237 use chrono::Timelike;
238
239 let nanos = time.num_seconds_from_midnight() as u64 * 1_000_000_000 + time.nanosecond() as u64;
242 let intervals = nanos / 100;
243
244 buf.put_u8((intervals & 0xFF) as u8);
246 buf.put_u8(((intervals >> 8) & 0xFF) as u8);
247 buf.put_u8(((intervals >> 16) & 0xFF) as u8);
248 buf.put_u8(((intervals >> 24) & 0xFF) as u8);
249 buf.put_u8(((intervals >> 32) & 0xFF) as u8);
250}
251
252#[cfg(feature = "chrono")]
256pub fn encode_datetime2(datetime: chrono::NaiveDateTime, buf: &mut BytesMut) {
257 encode_time(datetime.time(), buf);
258 encode_date(datetime.date(), buf);
259}
260
261#[cfg(feature = "chrono")]
265pub fn encode_datetimeoffset(datetime: chrono::DateTime<chrono::FixedOffset>, buf: &mut BytesMut) {
266 use chrono::Offset;
267
268 encode_time(datetime.time(), buf);
270 encode_date(datetime.date_naive(), buf);
271
272 let offset_seconds = datetime.offset().fix().local_minus_utc();
274 let offset_minutes = (offset_seconds / 60) as i16;
275 buf.put_i16_le(offset_minutes);
276}
277
278#[cfg(test)]
279#[allow(clippy::unwrap_used)]
280mod tests {
281 use super::*;
282
283 #[test]
284 fn test_encode_int() {
285 let mut buf = BytesMut::new();
286 SqlValue::Int(42).encode(&mut buf).unwrap();
287 assert_eq!(&buf[..], &[42, 0, 0, 0]);
288 }
289
290 #[test]
291 fn test_encode_bigint() {
292 let mut buf = BytesMut::new();
293 SqlValue::BigInt(0x0102030405060708)
294 .encode(&mut buf)
295 .unwrap();
296 assert_eq!(&buf[..], &[0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]);
297 }
298
299 #[test]
300 fn test_encode_utf16_string() {
301 let mut buf = BytesMut::new();
302 encode_utf16_string("AB", &mut buf);
303 assert_eq!(&buf[..], &[4, 0, 0x41, 0, 0x42, 0]);
305 }
306
307 #[cfg(feature = "uuid")]
308 #[test]
309 fn test_encode_uuid() {
310 let mut buf = BytesMut::new();
311 let uuid = uuid::Uuid::parse_str("12345678-1234-5678-1234-567812345678").unwrap();
312 encode_uuid(uuid, &mut buf);
313 assert_eq!(
315 &buf[..],
316 &[
317 0x78, 0x56, 0x34, 0x12, 0x34, 0x12, 0x78, 0x56, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78 ]
322 );
323 }
324
325 #[cfg(feature = "chrono")]
326 #[test]
327 fn test_encode_date() {
328 let mut buf = BytesMut::new();
329 let date = chrono::NaiveDate::from_ymd_opt(2024, 1, 15).unwrap();
330 encode_date(date, &mut buf);
331 assert_eq!(buf.len(), 3);
333 }
334}