use std::collections::VecDeque;
use rich_ext::table::virtualized::{DEFAULT_SAMPLE, MAX_HEIGHT, MAX_SAMPLE};
use rich_ext::table::{VirtualRows, VirtualTable};
use crate::sql::typed_columns;
use crate::{DataError, Row, RowSource, Rows, Schema, Value};
impl VirtualRows for Rows {
fn row_count(&self) -> Option<usize> {
Some(self.len())
}
fn row(&self, index: usize) -> Option<Vec<Value>> {
self.rows().get(index).cloned()
}
fn rows(&self, start: usize, len: usize) -> Vec<Vec<Value>> {
let rows = Rows::rows(self);
let start = start.min(rows.len());
let end = start.saturating_add(len).min(rows.len());
rows[start..end].to_vec()
}
}
#[derive(Clone, Debug)]
pub struct WindowReader {
offset: usize,
len: usize,
sample: usize,
count: bool,
}
impl Default for WindowReader {
fn default() -> Self {
WindowReader {
offset: 0,
len: 20,
sample: DEFAULT_SAMPLE,
count: true,
}
}
}
impl WindowReader {
#[must_use]
pub fn offset(mut self, offset: usize) -> Self {
self.offset = offset;
self
}
#[must_use]
pub fn len(mut self, rows: usize) -> Self {
self.len = rows.min(MAX_HEIGHT);
self
}
#[must_use]
pub fn sample(mut self, rows: usize) -> Self {
self.sample = rows.min(MAX_SAMPLE);
self
}
#[must_use]
pub fn count(mut self, count: bool) -> Self {
self.count = count;
self
}
pub fn read(&self, mut source: impl RowSource) -> Result<RowWindow, DataError> {
let columns = source.columns().to_vec();
let schema = source.schema().cloned();
let width = columns.len();
let end = self.offset.saturating_add(self.len);
let mut head = Vec::new();
let mut ring: VecDeque<Row> = VecDeque::with_capacity(self.len);
let mut next = None;
let mut index = 0usize;
while let Some(row) = source.next_row() {
let mut row = row?;
row.resize(width, Value::Null);
if index < self.sample {
head.push(row.clone());
}
if index < end {
if self.len > 0 {
if ring.len() == self.len {
ring.pop_front();
}
ring.push_back(row);
}
} else if !self.count {
next = Some(row);
break;
}
index += 1;
}
let start = index.min(end) - ring.len();
Ok(RowWindow {
columns,
schema,
head,
start,
rows: ring.into(),
total: next.is_none().then_some(index),
next,
})
}
}
#[derive(Clone, Debug)]
pub struct RowWindow {
columns: Vec<String>,
schema: Option<Schema>,
head: Vec<Row>,
start: usize,
rows: Vec<Row>,
total: Option<usize>,
next: Option<Row>,
}
impl RowWindow {
pub fn reader() -> WindowReader {
WindowReader::default()
}
pub fn read(source: impl RowSource, offset: usize, len: usize) -> Result<Self, DataError> {
Self::reader().offset(offset).len(len).read(source)
}
pub fn columns(&self) -> &[String] {
&self.columns
}
pub fn schema(&self) -> Option<&Schema> {
self.schema.as_ref()
}
pub fn start(&self) -> usize {
self.start
}
pub fn rows(&self) -> &[Row] {
&self.rows
}
pub fn total(&self) -> Option<usize> {
self.total
}
pub fn to_virtual_table(&self) -> VirtualTable<&RowWindow> {
let sample = if self.head.is_empty() {
&self.rows
} else {
&self.head
};
VirtualTable::new(
typed_columns(&self.columns, self.schema.as_ref(), sample),
self,
)
.offset(self.start)
.height(self.rows.len())
.fit_window(true)
}
}
impl VirtualRows for RowWindow {
fn row_count(&self) -> Option<usize> {
self.total
}
fn row(&self, index: usize) -> Option<Vec<Value>> {
if let Some(offset) = index.checked_sub(self.start) {
if let Some(row) = self.rows.get(offset) {
return Some(row.clone());
}
if offset == self.rows.len() {
if let Some(next) = &self.next {
return Some(next.clone());
}
}
}
self.head.get(index).cloned()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn numbers(count: i64) -> Rows {
let mut rows = Rows::new(["n"]);
for n in 0..count {
rows.push([Value::Int(n)]);
}
rows
}
#[test]
fn a_window_past_the_end_is_the_last_full_window() {
let window = RowWindow::read(numbers(10).into_source(), 50, 3).unwrap();
assert_eq!(window.start(), 7);
assert_eq!(window.rows()[0], [Value::Int(7)]);
assert_eq!(window.total(), Some(10));
let short = RowWindow::read(numbers(2).into_source(), 0, 5).unwrap();
assert_eq!((short.start(), short.rows().len()), (0, 2));
}
#[test]
fn without_counting_one_row_past_the_window_is_kept() {
let window = RowWindow::reader()
.offset(4)
.len(2)
.count(false)
.read(numbers(1_000).into_source())
.unwrap();
assert_eq!(window.total(), None);
assert_eq!(window.row(6), Some(vec![Value::Int(6)]));
assert_eq!(window.row(7), None);
let table = window.to_virtual_table();
assert_eq!(table.page().position(false), "rows 5–6 of 7+");
}
#[test]
fn the_head_and_the_window_answer_by_index() {
let window = RowWindow::reader()
.offset(500)
.len(2)
.sample(3)
.read(numbers(1_000).into_source())
.unwrap();
assert_eq!(window.row(2), Some(vec![Value::Int(2)]));
assert_eq!(window.row(3), None);
assert_eq!(window.row(501), Some(vec![Value::Int(501)]));
assert_eq!(VirtualRows::rows(&window, 0, 10).len(), 3);
}
#[test]
fn read_errors_surface() {
struct Broken(usize);
impl RowSource for Broken {
fn columns(&self) -> &[String] {
&[]
}
fn next_row(&mut self) -> Option<Result<Row, DataError>> {
self.0 += 1;
Some(if self.0 < 3 {
Ok(Vec::new())
} else {
Err(DataError::at(self.0, "bad row"))
})
}
}
let error = RowWindow::read(Broken(0), 0, 1).unwrap_err();
assert_eq!(error.to_string(), "line 3: bad row");
}
}