use std::{mem::MaybeUninit, ptr, thread::panicking};
use crate::{
Error,
buffers::{FetchRow, RowVec},
handles::{AsStatementRef, Statement as _},
};
use super::{Cursor, RowSetBuffer, error_handling_for_fetch, unbind_buffer_from_cursor};
pub struct BlockCursor<C: AsStatementRef, B> {
buffer: B,
cursor: C,
}
impl<C, B> BlockCursor<C, B>
where
C: Cursor,
{
pub(crate) fn new(buffer: B, cursor: C) -> Self {
Self { buffer, cursor }
}
pub fn fetch(&mut self) -> Result<Option<&B>, Error>
where
B: RowSetBuffer,
{
self.fetch_with_truncation_check(false)
}
pub fn fetch_with_truncation_check(
&mut self,
error_for_truncation: bool,
) -> Result<Option<&B>, Error>
where
B: RowSetBuffer,
{
let mut stmt = self.cursor.as_stmt_ref();
unsafe {
let result = stmt.fetch();
let has_row =
error_handling_for_fetch(result, stmt, &self.buffer, error_for_truncation)?;
Ok(has_row.then_some(&self.buffer))
}
}
pub fn unbind(self) -> Result<(C, B), Error> {
let dont_drop_me = MaybeUninit::new(self);
let self_ptr = dont_drop_me.as_ptr();
let mut cursor = unsafe { ptr::read(&(*self_ptr).cursor) };
let buffer = unsafe { ptr::read(&(*self_ptr).buffer) };
unbind_buffer_from_cursor(&mut cursor)?;
Ok((cursor, buffer))
}
}
impl<C, B> BlockCursor<C, B>
where
B: RowSetBuffer,
C: AsStatementRef,
{
pub fn row_array_size(&self) -> usize {
self.buffer.row_array_size()
}
}
impl<C, B> Drop for BlockCursor<C, B>
where
C: AsStatementRef,
{
fn drop(&mut self) {
if let Err(e) = unbind_buffer_from_cursor(&mut self.cursor) {
if !panicking() {
panic!("Unexpected error unbinding columns: {e:?}")
}
}
}
}
impl<C, R> IntoIterator for BlockCursor<C, RowVec<R>>
where
C: Cursor,
R: FetchRow,
{
type Item = Result<R, Error>;
type IntoIter = BlockCursorIterator<C, R>;
fn into_iter(self) -> Self::IntoIter {
BlockCursorIterator {
index: self.buffer.len(),
cursor: self,
}
}
}
pub struct BlockCursorIterator<C: AsStatementRef, R> {
cursor: BlockCursor<C, RowVec<R>>,
index: usize,
}
impl<C, R> BlockCursorIterator<C, R>
where
C: Cursor,
R: FetchRow,
{
fn next_row(&mut self) -> Result<Option<R>, Error> {
if self.index == self.cursor.buffer.len() {
if self.cursor.fetch()?.is_none() {
return Ok(None);
}
self.index = 0;
}
let row = self.cursor.buffer[self.index];
self.index += 1;
Ok(Some(row))
}
}
impl<C, R> Iterator for BlockCursorIterator<C, R>
where
C: Cursor,
R: FetchRow,
{
type Item = Result<R, Error>;
fn next(&mut self) -> Option<Result<R, Error>> {
self.next_row().transpose()
}
}