use crate::constant::{ColumnFlags, ColumnType};
use crate::error::{Error, Result, eyre};
use crate::protocol::primitive::*;
use zerocopy::byteorder::little_endian::{U16 as U16LE, U32 as U32LE};
use zerocopy::{FromBytes, Immutable, KnownLayout};
#[derive(Debug, Clone, Copy)]
pub struct ColumnDefinitionBytes<'a>(pub &'a [u8]);
impl<'a> ColumnDefinitionBytes<'a> {
pub fn tail(&self) -> Result<&'a ColumnDefinitionTail> {
if self.0.len() < 12 {
return Err(Error::LibraryBug(eyre!(
"column definition too short: {} < 12",
self.0.len()
)));
}
let tail_bytes = &self.0[self.0.len() - 12..];
Ok(ColumnDefinitionTail::ref_from_bytes(tail_bytes)?)
}
}
#[derive(Debug, Clone)]
pub struct ColumnDefinition<'a> {
pub schema: &'a [u8],
pub table_alias: &'a [u8],
pub table_original: &'a [u8],
pub name_alias: &'a [u8],
pub name_original: &'a [u8],
pub tail: &'a ColumnDefinitionTail,
}
impl<'a> TryFrom<ColumnDefinitionBytes<'a>> for ColumnDefinition<'a> {
type Error = Error;
fn try_from(bytes: ColumnDefinitionBytes<'a>) -> Result<Self> {
let data = bytes.0;
let (_catalog, data) = read_string_lenenc(data)?;
let (schema, data) = read_string_lenenc(data)?;
let (table_alias, data) = read_string_lenenc(data)?;
let (table_original, data) = read_string_lenenc(data)?;
let (name_alias, data) = read_string_lenenc(data)?;
let (name_original, data) = read_string_lenenc(data)?;
let (_length, data) = read_int_lenenc(data)?;
let tail = ColumnDefinitionTail::ref_from_bytes(data)?;
Ok(Self {
schema,
table_alias,
table_original,
name_alias,
name_original,
tail,
})
}
}
#[repr(C, packed)]
#[derive(Debug, Clone, Copy, FromBytes, KnownLayout, Immutable)]
pub struct ColumnDefinitionTail {
charset: U16LE,
column_length: U32LE,
column_type: u8,
flags: U16LE,
decimals: u8,
reserved: U16LE,
}
impl ColumnDefinitionTail {
pub fn charset(&self) -> u16 {
self.charset.get()
}
pub fn column_length(&self) -> u32 {
self.column_length.get()
}
pub fn column_type(&self) -> Result<ColumnType> {
ColumnType::from_u8(self.column_type).ok_or_else(|| {
Error::LibraryBug(eyre!("unknown column type: 0x{:02X}", self.column_type))
})
}
pub fn flags(&self) -> Result<ColumnFlags> {
ColumnFlags::from_bits(self.flags.get()).ok_or_else(|| {
Error::LibraryBug(eyre!("invalid column flags: 0x{:04X}", self.flags.get()))
})
}
}
pub struct ColumnDefinitions {
_packets: Vec<u8>, definitions: Vec<ColumnDefinition<'static>>,
}
impl ColumnDefinitions {
pub fn new(num_columns: usize, packets: Vec<u8>) -> Result<Self> {
let definitions = {
let mut buf = packets.as_slice();
let mut definitions = Vec::with_capacity(num_columns);
for _ in 0..num_columns {
let (len_bytes, rest) = buf.split_first_chunk::<4>().ok_or_else(|| {
Error::LibraryBug(eyre!("column definition: truncated length prefix"))
})?;
let len = u32::from_ne_bytes(*len_bytes) as usize;
let (payload, rest) = rest.split_at_checked(len).ok_or_else(|| {
Error::LibraryBug(eyre!("column definition: truncated payload"))
})?;
definitions.push(ColumnDefinition::try_from(ColumnDefinitionBytes(payload))?);
buf = rest;
}
unsafe {
std::mem::transmute::<Vec<ColumnDefinition<'_>>, Vec<ColumnDefinition<'static>>>(
definitions,
)
}
};
Ok(Self {
_packets: packets,
definitions,
})
}
pub fn definitions<'a>(&'a self) -> &'a [ColumnDefinition<'a>] {
self.definitions.as_slice()
}
}