use std::collections::VecDeque;
use std::marker::PhantomData;
use super::{
select_rows_pool_with_related, ExecError, LoadRelated, MaybeMyFromRow, MaybeMyLoadRelated,
MaybePgFromRow, MaybeSqliteFromRow, MaybeSqliteLoadRelated,
};
use crate::core::Model;
use crate::sql::Pool;
pub struct ChunkedIter<T> {
pub(super) query: crate::core::SelectQuery,
pub(super) chunk_size: i64,
pub(super) offset: i64,
pub(super) exhausted: bool,
pub(super) buffer: VecDeque<T>,
pub(super) seen: i64,
pub(super) _model: PhantomData<fn() -> T>,
}
impl<T> ChunkedIter<T>
where
T: Model
+ MaybePgFromRow
+ MaybeMyFromRow
+ MaybeSqliteFromRow
+ LoadRelated
+ MaybeMyLoadRelated
+ MaybeSqliteLoadRelated
+ Send
+ Unpin,
{
pub async fn next_chunk(&mut self, pool: &Pool) -> Result<Option<Vec<T>>, ExecError> {
if !self.buffer.is_empty() {
let buffered: Vec<T> = self.buffer.drain(..).collect();
self.seen += buffered.len() as i64;
return Ok(Some(buffered));
}
match self.fetch_next_chunk(pool).await? {
Some(rows) => {
self.seen += rows.len() as i64;
Ok(Some(rows))
}
None => Ok(None),
}
}
pub async fn next_row(&mut self, pool: &Pool) -> Result<Option<T>, ExecError> {
if let Some(row) = self.buffer.pop_front() {
self.seen += 1;
return Ok(Some(row));
}
match self.fetch_next_chunk(pool).await? {
Some(rows) => {
self.buffer.extend(rows);
let row = self.buffer.pop_front();
if row.is_some() {
self.seen += 1;
}
Ok(row)
}
None => Ok(None),
}
}
async fn fetch_next_chunk(&mut self, pool: &Pool) -> Result<Option<Vec<T>>, ExecError> {
if self.exhausted {
return Ok(None);
}
let mut q = self.query.clone();
q.limit = Some(self.chunk_size);
q.offset = Some(self.offset);
let rows = select_rows_pool_with_related::<T>(pool, &q).await?;
let n = rows.len() as i64;
if rows.is_empty() {
self.exhausted = true;
return Ok(None);
}
if n < self.chunk_size {
self.exhausted = true;
}
self.offset += n;
Ok(Some(rows))
}
#[must_use]
pub fn rows_seen(&self) -> i64 {
self.seen
}
#[must_use]
pub fn is_exhausted(&self) -> bool {
self.exhausted && self.buffer.is_empty()
}
}