use std::{
borrow::{Borrow, BorrowMut},
ffi::c_void,
};
use odbc_sys::{CDataType, NULL_DATA};
use crate::{
buffers::Indicator,
handles::{CData, CDataMut, HasDataType},
DataType, OutputParameter,
};
use super::CElement;
#[derive(Debug, Clone, Copy)]
pub struct VarChar<B> {
buffer: B,
indicator: isize,
}
pub type VarCharBox = VarChar<Box<[u8]>>;
impl VarCharBox {
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> VarChar<B>
where
B: Borrow<[u8]>,
{
pub fn from_buffer(buffer: B, indicator: Indicator) -> Self {
let buf = buffer.borrow();
match indicator {
Indicator::Null => (),
Indicator::NoTotal => {
if buf.is_empty() || *buf.last().unwrap() != 0 {
panic!("Truncated value must be terminated with zero.")
}
}
Indicator::Length(len) => {
if len > buf.len() && (buf.is_empty() || *buf.last().unwrap() != 0) {
panic!("Truncated value must be terminated with zero.")
}
}
};
Self {
buffer,
indicator: indicator.to_isize(),
}
}
pub fn as_bytes(&self) -> Option<&[u8]> {
let slice = self.buffer.borrow();
match self.indicator() {
Indicator::Null => None,
Indicator::NoTotal => Some(&slice[..(slice.len() - 1)]),
Indicator::Length(len) => {
if self.is_complete() {
Some(&slice[..len])
} else {
Some(&slice[..(slice.len() - 1)])
}
}
}
}
pub fn is_complete(&self) -> bool {
let slice = self.buffer.borrow();
match self.indicator() {
Indicator::Null => true,
Indicator::NoTotal => false,
Indicator::Length(len) => {
len < slice.len() || slice.is_empty() || *slice.last().unwrap() != 0
}
}
}
pub fn indicator(&self) -> Indicator {
Indicator::from_isize(self.indicator)
}
}
impl<B> VarChar<B>
where
B: Borrow<[u8]>,
{
pub fn hide_truncation(&mut self) {
if !self.is_complete() {
self.indicator = (self.buffer.borrow().len() - 1).try_into().unwrap();
}
}
}
unsafe impl<B> CData for VarChar<B>
where
B: Borrow<[u8]>,
{
fn cdata_type(&self) -> CDataType {
CDataType::Char
}
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> HasDataType for VarChar<B>
where
B: Borrow<[u8]>,
{
fn data_type(&self) -> DataType {
DataType::Varchar {
length: self.buffer.borrow().len(),
}
}
}
unsafe impl<B> CDataMut for VarChar<B>
where
B: BorrowMut<[u8]>,
{
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]>;
impl<'a> VarCharSlice<'a> {
pub const NULL: Self = Self {
buffer: &[0],
indicator: NULL_DATA,
};
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 VarCharArray<const LENGTH: usize> = VarChar<[u8; LENGTH]>;
impl<const LENGTH: usize> VarCharArray<LENGTH> {
pub const NULL: Self = VarCharArray {
buffer: [0; LENGTH],
indicator: NULL_DATA,
};
pub fn new(text: &[u8]) -> Self {
let indicator = text.len().try_into().unwrap();
let mut buffer = [0u8; LENGTH];
if text.len() > LENGTH {
buffer.copy_from_slice(&text[..LENGTH]);
*buffer.last_mut().unwrap() = 0;
} else {
buffer[..text.len()].copy_from_slice(text);
};
Self { buffer, indicator }
}
}
unsafe impl CElement for VarCharSlice<'_> {}
unsafe impl<const LENGTH: usize> CElement for VarCharArray<LENGTH> {}
unsafe impl<const LENGTH: usize> OutputParameter for VarCharArray<LENGTH> {}
unsafe impl CElement for VarCharSliceMut<'_> {}
unsafe impl OutputParameter for VarCharSliceMut<'_> {}
unsafe impl CElement for VarCharBox {}
unsafe impl OutputParameter for VarCharBox {}