use std::mem::size_of;
use zerocopy::byteorder::big_endian::{I16 as I16BE, I32 as I32BE, U16 as U16BE, U32 as U32BE};
use zerocopy::{FromBytes, Immutable, KnownLayout};
use crate::error::{Error, Result};
use crate::protocol::codec::read_cstr;
use crate::protocol::types::{FormatCode, Oid};
#[derive(Debug, Clone, Copy, FromBytes, KnownLayout, Immutable)]
#[repr(C, packed)]
pub struct FieldDescriptionTail {
pub table_oid: U32BE,
pub column_id: I16BE,
pub type_oid: U32BE,
pub type_size: I16BE,
pub type_modifier: I32BE,
pub format: U16BE,
}
#[derive(Debug, Clone, Copy)]
pub struct FieldDescription<'a> {
pub name: &'a str,
pub tail: &'a FieldDescriptionTail,
}
impl FieldDescription<'_> {
pub fn table_oid(&self) -> Oid {
self.tail.table_oid.get()
}
pub fn column_id(&self) -> i16 {
self.tail.column_id.get()
}
pub fn type_oid(&self) -> Oid {
self.tail.type_oid.get()
}
pub fn type_size(&self) -> i16 {
self.tail.type_size.get()
}
pub fn type_modifier(&self) -> i32 {
self.tail.type_modifier.get()
}
pub fn format(&self) -> FormatCode {
FormatCode::from_u16(self.tail.format.get())
}
}
#[derive(Debug)]
pub struct RowDescription<'a> {
fields: Vec<FieldDescription<'a>>,
}
impl<'a> RowDescription<'a> {
pub fn parse(payload: &'a [u8]) -> Result<Self> {
let num_fields = U16BE::ref_from_bytes(&payload[..2])
.map_err(|e| Error::LibraryBug(format!("RowDescription header: {e:?}")))?
.get() as usize;
let mut fields = Vec::with_capacity(num_fields);
let mut data = &payload[2..];
const TAIL_SIZE: usize = size_of::<FieldDescriptionTail>();
for _ in 0..num_fields {
let (name, rest) = read_cstr(data)?;
let tail = FieldDescriptionTail::ref_from_bytes(&rest[..TAIL_SIZE])
.map_err(|e| Error::LibraryBug(format!("FieldDescription tail: {e:?}")))?;
fields.push(FieldDescription { name, tail });
data = &rest[TAIL_SIZE..];
}
Ok(Self { fields })
}
pub fn len(&self) -> usize {
self.fields.len()
}
pub fn is_empty(&self) -> bool {
self.fields.is_empty()
}
pub fn fields(&self) -> &[FieldDescription<'a>] {
&self.fields
}
pub fn iter(&self) -> impl Iterator<Item = &FieldDescription<'a>> {
self.fields.iter()
}
}
#[derive(Debug, Clone, Copy, FromBytes, KnownLayout, Immutable)]
#[repr(C, packed)]
pub struct DataRowHead {
pub num_columns: U16BE,
}
#[derive(Debug, Clone, Copy)]
pub struct DataRow<'a> {
num_columns: u16,
columns_data: &'a [u8],
}
impl<'a> DataRow<'a> {
pub fn parse(payload: &'a [u8]) -> Result<Self> {
let head = DataRowHead::ref_from_bytes(&payload[..2])
.map_err(|e| Error::LibraryBug(format!("DataRow header: {e:?}")))?;
Ok(Self {
num_columns: head.num_columns.get(),
columns_data: &payload[2..],
})
}
pub fn len(&self) -> usize {
self.num_columns as usize
}
pub fn is_empty(&self) -> bool {
self.num_columns == 0
}
pub fn iter(&self) -> DataRowIter<'a> {
DataRowIter {
remaining: self.columns_data,
}
}
pub fn get(&self, index: usize) -> Option<Option<&'a [u8]>> {
self.iter().nth(index)
}
pub fn raw_data(&self) -> &'a [u8] {
self.columns_data
}
}
#[derive(Debug, Clone)]
pub struct DataRowIter<'a> {
remaining: &'a [u8],
}
impl<'a> Iterator for DataRowIter<'a> {
type Item = Option<&'a [u8]>;
fn next(&mut self) -> Option<Self::Item> {
let (len_bytes, remaining) = self.remaining.split_first_chunk::<4>()?;
self.remaining = remaining;
let len = i32::from_be_bytes(*len_bytes);
if len == -1 {
Some(None)
} else {
let len = len as usize;
if self.remaining.len() < len {
return None;
}
let value;
(value, self.remaining) = self.remaining.split_at_checked(len)?;
Some(Some(value))
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct CommandComplete<'a> {
pub tag: &'a str,
}
impl<'a> CommandComplete<'a> {
pub fn parse(payload: &'a [u8]) -> Result<Self> {
let (tag, _) = read_cstr(payload)?;
Ok(Self { tag })
}
pub fn rows_affected(&self) -> Option<u64> {
let mut iter = self.tag.split_whitespace();
match iter.next()? {
"SELECT" | "UPDATE" | "DELETE" | "COPY" | "MOVE" | "FETCH" => (),
"INSERT" => _ = iter.next(), _ => return None,
}
iter.next()?.parse().ok()
}
pub fn command(&self) -> Option<&str> {
self.tag.split_whitespace().next()
}
}
#[derive(Debug, Clone, Copy)]
pub struct EmptyQueryResponse;
impl EmptyQueryResponse {
pub fn parse(_payload: &[u8]) -> Result<Self> {
Ok(Self)
}
}