use async_trait::async_trait;
use tracing::event;
use super::super::tokens::Tokens;
use super::common::TokenParser;
use crate::{core::TdsResult, io::packet_reader::TdsPacketReader};
use crate::{io::token_stream::ParserContext, token::tokens::InfoToken};
#[derive(Default)]
pub(crate) struct InfoTokenParser {
}
#[async_trait]
impl<T> TokenParser<T> for InfoTokenParser
where
T: TdsPacketReader + Send + Sync,
{
async fn parse(&self, reader: &mut T, _context: &ParserContext) -> TdsResult<Tokens> {
let _length = reader.read_uint16().await?;
let number = reader.read_uint32().await?;
let state = reader.read_byte().await?;
let severity = reader.read_byte().await?;
let message = reader.read_varchar_u16_length().await?;
let server_name = reader.read_varchar_u8_length().await?;
let proc_name = reader.read_varchar_u8_length().await?;
let line_number = reader.read_uint32().await?;
event!(tracing::Level::INFO, "Info message: {:?}", message);
Ok(Tokens::from(InfoToken {
number,
state,
severity,
message: message.unwrap_or_default(),
server_name,
proc_name,
line_number,
}))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::message::messages::PacketType;
use crate::test_packet_support::{TestPacketBuilder, create_network_transport_with_data};
fn encode_utf16_string(s: &str) -> Vec<u8> {
let utf16_units: Vec<u16> = s.encode_utf16().collect();
let mut bytes = Vec::with_capacity(utf16_units.len() * 2);
for unit in utf16_units {
bytes.push((unit & 0xFF) as u8);
bytes.push((unit >> 8) as u8);
}
bytes
}
#[tokio::test]
async fn test_parse_info_token_basic() {
let mut builder = TestPacketBuilder::new(PacketType::PreLogin);
let message = "Test info message";
let server_name = "TestServer";
let proc_name = "";
let message_bytes = encode_utf16_string(message);
let server_bytes = encode_utf16_string(server_name);
let proc_bytes = encode_utf16_string(proc_name);
let length = 4 + 1 + 1 + 2 + message_bytes.len() + 1 + server_bytes.len() + 1 + proc_bytes.len() + 4;
builder.append_u16(length as u16); builder.append_u32(5701); builder.append_byte(1); builder.append_byte(0);
builder.append_u16(message.len() as u16);
builder.append_bytes(&message_bytes);
builder.append_byte(server_name.len() as u8);
builder.append_bytes(&server_bytes);
builder.append_byte(proc_name.len() as u8);
builder.append_bytes(&proc_bytes);
builder.append_u32(1);
let mut packet_reader = create_network_transport_with_data(&builder.build());
let parser = InfoTokenParser::default();
let context = ParserContext::default();
let result = parser.parse(&mut packet_reader, &context).await.unwrap();
match result {
Tokens::Info(token) => {
assert_eq!(token.number, 5701);
assert_eq!(token.state, 1);
assert_eq!(token.severity, 0);
assert_eq!(token.message, message);
assert_eq!(token.server_name, server_name);
assert_eq!(token.proc_name, proc_name);
assert_eq!(token.line_number, 1);
}
_ => panic!("Expected Info token"),
}
}
#[tokio::test]
async fn test_parse_info_token_with_proc() {
let mut builder = TestPacketBuilder::new(PacketType::PreLogin);
let message = "Info from proc";
let server_name = "Server";
let proc_name = "sp_test";
let message_bytes = encode_utf16_string(message);
let server_bytes = encode_utf16_string(server_name);
let proc_bytes = encode_utf16_string(proc_name);
let length =
4 + 1 + 1 + 2 + message_bytes.len() + 1 + server_bytes.len() + 1 + proc_bytes.len() + 4;
builder.append_u16(length as u16);
builder.append_u32(0); builder.append_byte(1);
builder.append_byte(10);
builder.append_u16(message.len() as u16);
builder.append_bytes(&message_bytes);
builder.append_byte(server_name.len() as u8);
builder.append_bytes(&server_bytes);
builder.append_byte(proc_name.len() as u8);
builder.append_bytes(&proc_bytes);
builder.append_u32(42);
let mut packet_reader = create_network_transport_with_data(&builder.build());
let parser = InfoTokenParser::default();
let context = ParserContext::default();
let result = parser.parse(&mut packet_reader, &context).await.unwrap();
match result {
Tokens::Info(token) => {
assert_eq!(token.number, 0);
assert_eq!(token.state, 1);
assert_eq!(token.severity, 10);
assert_eq!(token.message, message);
assert_eq!(token.server_name, server_name);
assert_eq!(token.proc_name, proc_name);
assert_eq!(token.line_number, 42);
}
_ => panic!("Expected Info token"),
}
}
#[tokio::test]
async fn test_parse_info_token_empty_message() {
let mut builder = TestPacketBuilder::new(PacketType::PreLogin);
let message = "";
let server_name = "";
let proc_name = "";
let message_bytes = encode_utf16_string(message);
let server_bytes = encode_utf16_string(server_name);
let proc_bytes = encode_utf16_string(proc_name);
let length =
4 + 1 + 1 + 2 + message_bytes.len() + 1 + server_bytes.len() + 1 + proc_bytes.len() + 4;
builder.append_u16(length as u16);
builder.append_u32(0);
builder.append_byte(0);
builder.append_byte(0);
builder.append_u16(0); builder.append_byte(0); builder.append_byte(0); builder.append_u32(0);
let mut packet_reader = create_network_transport_with_data(&builder.build());
let parser = InfoTokenParser::default();
let context = ParserContext::default();
let result = parser.parse(&mut packet_reader, &context).await.unwrap();
match result {
Tokens::Info(token) => {
assert_eq!(token.message, "");
assert_eq!(token.server_name, "");
assert_eq!(token.proc_name, "");
}
_ => panic!("Expected Info token"),
}
}
}