use async_trait::async_trait;
use crate::connection::client_context::VectorVersion;
use crate::core::TdsResult;
use crate::io::packet_writer::{PacketWriter, TdsPacketWriter};
use crate::message::login::{Feature, FeatureExtension};
#[derive(Debug, Clone, Copy)]
pub(crate) struct VectorFeature {
acknowledged: bool,
negotiated_version: u8,
client_version: u8,
}
impl VectorFeature {
pub const VERSION: u8 = 2;
pub fn new(client_version: u8) -> Self {
Self {
acknowledged: false,
negotiated_version: 0,
client_version,
}
}
#[allow(dead_code)]
pub fn negotiated_version(&self) -> u8 {
self.negotiated_version
}
}
impl Default for VectorFeature {
fn default() -> Self {
Self::new(Self::VERSION)
}
}
impl From<VectorVersion> for Option<VectorFeature> {
fn from(version: VectorVersion) -> Self {
match version {
VectorVersion::Off => None,
VectorVersion::V1 => Some(VectorFeature::new(1)),
VectorVersion::V2 => Some(VectorFeature::new(2)),
}
}
}
#[async_trait]
impl Feature for VectorFeature {
fn feature_identifier(&self) -> FeatureExtension {
FeatureExtension::Vector
}
fn is_requested(&self) -> bool {
true
}
fn data_length(&self) -> i32 {
(size_of::<u8>() + size_of::<i32>() + size_of::<u8>()) as i32
}
async fn serialize(&self, packet_writer: &mut PacketWriter) -> TdsResult<()> {
packet_writer
.write_byte_async(self.feature_identifier().as_u8())
.await?;
packet_writer.write_i32_async(1).await?;
packet_writer.write_byte_async(self.client_version).await?;
Ok(())
}
fn deserialize(&mut self, data: &[u8]) -> TdsResult<()> {
if data.len() != 1 {
return Err(crate::error::Error::ProtocolError(format!(
"Invalid data length {} for VectorSupport feature, expected 1 byte",
data.len()
)));
}
let server_supported_version = data[0];
if server_supported_version == 0 {
self.negotiated_version = 0;
return Ok(());
}
if server_supported_version > self.client_version {
return Err(crate::error::Error::ProtocolError(format!(
"Server VectorSupport version {} exceeds client requested version {}",
server_supported_version, self.client_version
)));
}
self.negotiated_version = server_supported_version;
Ok(())
}
fn is_acknowledged(&self) -> bool {
self.acknowledged
}
fn set_acknowledged(&mut self, acknowledged: bool) {
self.acknowledged = acknowledged;
if !acknowledged {
self.negotiated_version = 0;
}
}
fn clone_box(&self) -> Box<dyn Feature> {
Box::new(*self)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_feature_identifier() {
let feature = VectorFeature::default();
assert_eq!(
feature.feature_identifier().as_u8(),
0x0E,
"VectorSupport feature ID should be 0x0E"
);
}
#[test]
fn test_deserialize_invalid_version() {
let mut feature = VectorFeature::default();
let data = vec![3u8]; let result = feature.deserialize(&data);
assert!(result.is_err());
assert!(
result
.unwrap_err()
.to_string()
.contains("exceeds client requested version")
);
}
#[test]
fn test_deserialize_invalid_length() {
let mut feature = VectorFeature::default();
let data = vec![1u8, 0u8]; let result = feature.deserialize(&data);
assert!(result.is_err());
assert!(
result
.unwrap_err()
.to_string()
.contains("Invalid data length")
);
}
#[test]
fn test_negotiate_v1_client_v1_server() {
let mut feature = VectorFeature::new(1);
assert!(!feature.is_acknowledged());
assert_eq!(feature.negotiated_version(), 0);
feature.set_acknowledged(true);
feature.deserialize(&[1u8]).unwrap();
assert!(feature.is_acknowledged());
assert_eq!(feature.negotiated_version(), 1);
}
#[test]
fn test_negotiate_v2_client_v1_server() {
let mut feature = VectorFeature::default(); assert!(!feature.is_acknowledged());
assert_eq!(feature.negotiated_version(), 0);
feature.set_acknowledged(true);
feature.deserialize(&[1u8]).unwrap();
assert!(feature.is_acknowledged());
assert_eq!(feature.negotiated_version(), 1);
}
#[test]
fn test_negotiate_v2_client_v2_server() {
let mut feature = VectorFeature::new(2);
assert!(!feature.is_acknowledged());
assert_eq!(feature.negotiated_version(), 0);
feature.set_acknowledged(true);
feature.deserialize(&[2u8]).unwrap();
assert!(feature.is_acknowledged());
assert_eq!(feature.negotiated_version(), 2);
}
}