use std::{
borrow::{Borrow, BorrowMut},
ffi::c_void,
marker::PhantomData,
};
use odbc_sys::{CDataType, NULL_DATA};
use crate::{
buffers::Indicator,
handles::{CData, CDataMut, HasDataType},
DataType, OutputParameter,
};
use super::CElement;
pub unsafe trait VarKind {
const TERMINATING_ZEROES: usize;
const C_DATA_TYPE: CDataType;
fn relational_type(length: usize) -> DataType;
}
pub struct Text;
unsafe impl VarKind for Text {
const TERMINATING_ZEROES: usize = 1;
const C_DATA_TYPE: CDataType = CDataType::Char;
fn relational_type(length: usize) -> DataType {
DataType::Varchar { length }
}
}
pub struct Binary;
unsafe impl VarKind for Binary {
const TERMINATING_ZEROES: usize = 0;
const C_DATA_TYPE: CDataType = CDataType::Binary;
fn relational_type(length: usize) -> DataType {
DataType::Varbinary { length }
}
}
#[derive(Debug, Clone, Copy)]
pub struct VarCell<B, K> {
buffer: B,
indicator: isize,
kind: PhantomData<K>,
}
pub type VarBinary<B> = VarCell<B, Binary>;
pub type VarChar<B> = VarCell<B, Text>;
pub type VarCharBox = VarChar<Box<[u8]>>;
pub type VarBinaryBox = VarBinary<Box<[u8]>>;
impl<K> VarCell<Box<[u8]>, K>
where
K: VarKind,
{
pub fn null() -> Self {
Self::from_buffer(Box::new([0]), Indicator::Null)
}
pub fn from_string(val: String) -> Self {
Self::from_vec(val.into_bytes())
}
pub fn from_vec(val: Vec<u8>) -> Self {
let indicator = Indicator::Length(val.len());
let buffer = val.into_boxed_slice();
Self::from_buffer(buffer, indicator)
}
}
impl<B, K> VarCell<B, K>
where
B: Borrow<[u8]>,
K: VarKind,
{
pub fn from_buffer(buffer: B, indicator: Indicator) -> Self {
let buf = buffer.borrow();
if indicator.is_truncated(buf.len()) {
if !ends_in_zeroes(buf, K::TERMINATING_ZEROES) {
panic!("Truncated value must be terminated with zero.")
}
}
Self {
buffer,
indicator: indicator.to_isize(),
kind: PhantomData,
}
}
pub fn as_bytes(&self) -> Option<&[u8]> {
let slice = self.buffer.borrow();
match self.indicator() {
Indicator::Null => None,
Indicator::NoTotal => Some(&slice[..(slice.len() - K::TERMINATING_ZEROES)]),
Indicator::Length(len) => {
if self.is_complete() {
Some(&slice[..len])
} else {
Some(&slice[..(slice.len() - K::TERMINATING_ZEROES)])
}
}
}
}
pub fn is_complete(&self) -> bool {
let slice = self.buffer.borrow();
let max_value_length = if ends_in_zeroes(slice, K::TERMINATING_ZEROES) {
slice.len() - K::TERMINATING_ZEROES
} else {
slice.len()
};
!self.indicator().is_truncated(max_value_length)
}
pub fn indicator(&self) -> Indicator {
Indicator::from_isize(self.indicator)
}
pub fn capacity(&self) -> usize {
self.buffer.borrow().len()
}
}
impl<B, K> VarCell<B, K>
where
B: Borrow<[u8]>,
K: VarKind,
{
pub fn hide_truncation(&mut self) {
if !self.is_complete() {
self.indicator = (self.buffer.borrow().len() - K::TERMINATING_ZEROES)
.try_into()
.unwrap();
}
}
}
unsafe impl<B, K> CData for VarCell<B, K>
where
B: Borrow<[u8]>,
K: VarKind,
{
fn cdata_type(&self) -> CDataType {
K::C_DATA_TYPE
}
fn indicator_ptr(&self) -> *const isize {
&self.indicator as *const isize
}
fn value_ptr(&self) -> *const c_void {
self.buffer.borrow().as_ptr() as *const c_void
}
fn buffer_length(&self) -> isize {
self.buffer.borrow().len().try_into().unwrap()
}
}
impl<B, K> HasDataType for VarCell<B, K>
where
B: Borrow<[u8]>,
K: VarKind,
{
fn data_type(&self) -> DataType {
K::relational_type(self.buffer.borrow().len())
}
}
unsafe impl<B, K> CDataMut for VarCell<B, K>
where
B: BorrowMut<[u8]>,
K: VarKind,
{
fn mut_indicator_ptr(&mut self) -> *mut isize {
&mut self.indicator as *mut isize
}
fn mut_value_ptr(&mut self) -> *mut c_void {
self.buffer.borrow_mut().as_mut_ptr() as *mut c_void
}
}
pub type VarCharSlice<'a> = VarChar<&'a [u8]>;
pub type VarBinarySlice<'a> = VarBinary<&'a [u8]>;
impl<'a, K> VarCell<&'a [u8], K>
where
K: VarKind,
{
pub const NULL: Self = Self {
buffer: &[0],
indicator: NULL_DATA,
kind: PhantomData,
};
pub fn new(value: &'a [u8]) -> Self {
Self::from_buffer(value, Indicator::Length(value.len()))
}
}
pub type VarCharSliceMut<'a> = VarChar<&'a mut [u8]>;
pub type VarBinarySliceMut<'a> = VarBinary<&'a mut [u8]>;
pub type VarCharArray<const LENGTH: usize> = VarChar<[u8; LENGTH]>;
pub type VarBinaryArray<const LENGTH: usize> = VarBinary<[u8; LENGTH]>;
impl<const LENGTH: usize, K: VarKind> VarCell<[u8; LENGTH], K> {
pub const NULL: Self = Self {
buffer: [0; LENGTH],
indicator: NULL_DATA,
kind: PhantomData,
};
pub fn new(bytes: &[u8]) -> Self {
let indicator = bytes.len().try_into().unwrap();
let mut buffer = [0u8; LENGTH];
if bytes.len() > LENGTH {
buffer.copy_from_slice(&bytes[..LENGTH]);
*buffer.last_mut().unwrap() = 0;
} else {
buffer[..bytes.len()].copy_from_slice(bytes);
};
Self {
buffer,
indicator,
kind: PhantomData,
}
}
}
fn ends_in_zeroes(buffer: &[u8], number_of_zeroes: usize) -> bool {
buffer.len() >= number_of_zeroes
&& buffer
.iter()
.rev()
.copied()
.take(number_of_zeroes)
.all(|byte| byte == 0)
}
unsafe impl<K: VarKind> CElement for VarCell<&'_ [u8], K> {}
unsafe impl<const LENGTH: usize, K: VarKind> CElement for VarCell<[u8; LENGTH], K> {}
unsafe impl<const LENGTH: usize, K: VarKind> OutputParameter for VarCell<[u8; LENGTH], K> {}
unsafe impl<K: VarKind> CElement for VarCell<&'_ mut [u8], K> {}
unsafe impl<K: VarKind> OutputParameter for VarCell<&'_ mut [u8], K> {}
unsafe impl<K: VarKind> CElement for VarCell<Box<[u8]>, K> {}
unsafe impl<K: VarKind> OutputParameter for VarCell<Box<[u8]>, K> {}
#[cfg(test)]
mod tests {
use super::{Indicator, VarCharSlice};
#[test]
fn must_accept_fitting_values_and_correctly_truncated_ones() {
VarCharSlice::from_buffer(b"12345", Indicator::Length(5));
VarCharSlice::from_buffer(b"1234\0", Indicator::Length(10));
}
#[test]
#[should_panic]
fn must_ensure_truncated_values_are_terminated() {
VarCharSlice::from_buffer(b"12345", Indicator::Length(10));
}
}