use polars::prelude::*;
use std::sync::Arc;
pub const INDEX: &str = "__datui_row";
pub const MAX_ROWS: usize = IdxSize::MAX as usize;
pub trait RowSource: Send + Sync + 'static {
fn height(&self) -> usize;
fn schema(&self) -> SchemaRef;
fn decode(&self, column: usize, index: &IdxCa) -> PolarsResult<Column>;
}
pub fn lazy<S: RowSource>(source: &Arc<S>) -> LazyFrame {
lazy_with_height(source, source.height())
}
pub fn lazy_with_height<S: RowSource>(source: &Arc<S>, height: usize) -> LazyFrame {
frame(source, height, false)
}
pub fn lazy_numbered<S: RowSource>(source: &Arc<S>, height: usize) -> LazyFrame {
frame(source, height, true)
}
fn frame<S: RowSource>(source: &Arc<S>, height: usize, numbered: bool) -> LazyFrame {
let height = DataFrame::empty_with_height(height.min(MAX_ROWS)).lazy();
frame_over(source, height, numbered)
}
pub fn lazy_numbered_over<S: RowSource>(source: &Arc<S>, height: LazyFrame) -> LazyFrame {
frame_over(source, height, true)
}
fn frame_over<S: RowSource>(source: &Arc<S>, height: LazyFrame, numbered: bool) -> LazyFrame {
let base = height.with_row_index(INDEX, None);
let mut exprs: Vec<Expr> = source
.schema()
.iter()
.enumerate()
.map(|(column, (name, dtype))| {
let source = Arc::clone(source);
let field = Field::new(name.clone(), dtype.clone());
col(INDEX)
.map(
move |c: Column| source.decode(column, c.as_materialized_series().idx()?),
move |_, _| Ok(field.clone()),
)
.alias(name.clone())
})
.collect();
if numbered {
exprs.push(col(INDEX));
}
base.select(exprs)
}
pub fn checked(index: &IdxCa, rows: usize) -> PolarsResult<std::borrow::Cow<'_, [IdxSize]>> {
polars_ensure!(
index.null_count() == 0,
ComputeError: "a row index has a missing row"
);
if let Some(max) = index.max() {
polars_ensure!(
(max as usize) < rows,
OutOfBounds: "row {max} is past the {rows} rows on hand"
);
}
Ok(match index.cont_slice() {
Ok(rows) => std::borrow::Cow::Borrowed(rows),
Err(_) => std::borrow::Cow::Owned(index.into_no_null_iter().collect()),
})
}
#[cfg(test)]
mod tests;