use crate::column::Column as RsColumn;
use crate::functions::SortOrder;
use polars::prelude::Expr;
use pyo3::prelude::*;
use pyo3::types::PyTuple;
use super::column::PyColumn;
#[pyclass]
#[derive(Clone)]
pub struct PySortOrder {
pub inner: SortOrder,
}
#[derive(Clone)]
#[pyclass(name = "Window")]
pub struct PyWindow {
pub(crate) partition_by: Vec<String>,
pub(crate) order_by: Option<RsColumn>,
}
#[pymethods]
impl PyWindow {
#[classmethod]
#[pyo3(name = "partitionBy")]
#[pyo3(signature = (*cols))]
fn partition_by_cls(
_cls: &Bound<'_, pyo3::types::PyType>,
cols: &Bound<'_, PyTuple>,
) -> PyResult<Self> {
let mut names = Vec::with_capacity(cols.len());
for item in cols.iter() {
names.push(item.extract::<String>()?);
}
Ok(Self {
partition_by: names,
order_by: None,
})
}
#[pyo3(name = "orderBy")]
#[pyo3(signature = (*cols))]
fn order_by(&self, cols: Vec<PyRef<PyColumn>>) -> PyResult<Self> {
if cols.is_empty() {
return Err(pyo3::exceptions::PyValueError::new_err(
"Window.orderBy requires at least one column",
));
}
if cols.len() > 1 {
return Err(pyo3::exceptions::PyNotImplementedError::new_err(
"Window.orderBy with multiple columns is not yet supported; use a single column",
));
}
Ok(Self {
partition_by: self.partition_by.clone(),
order_by: Some(cols[0].inner.clone()),
})
}
}
#[pyclass(name = "WhenThen")]
pub struct PyWhenThen {
pub(crate) condition: Expr,
pub(crate) then_value: Expr,
}
#[pymethods]
impl PyWhenThen {
fn otherwise(&self, value: &PyColumn) -> PyColumn {
let when_then = polars::prelude::when(self.condition.clone()).then(self.then_value.clone());
let expr = when_then.otherwise(value.inner.expr().clone());
PyColumn {
inner: RsColumn::from_expr(expr, None),
}
}
}
#[pyclass(name = "WhenBuilder")]
pub struct PyWhenBuilder {
pub condition: Expr,
}
#[pymethods]
impl PyWhenBuilder {
fn then(&self, value: &PyColumn) -> PyThenBuilder {
let when_then =
polars::prelude::when(self.condition.clone()).then(value.inner.expr().clone());
PyThenBuilder { when_then }
}
}
#[pyclass(name = "ThenBuilder")]
pub struct PyThenBuilder {
when_then: polars::prelude::Then,
}
#[pymethods]
impl PyThenBuilder {
fn otherwise(&self, value: &PyColumn) -> PyColumn {
let expr = self.when_then.clone().otherwise(value.inner.expr().clone());
PyColumn {
inner: RsColumn::from_expr(expr, None),
}
}
}
#[pyclass(name = "RowNumber")]
pub struct PyRowNumber {
pub(crate) descending: bool,
}
#[pymethods]
impl PyRowNumber {
fn over(&self, window: &PyWindow) -> PyResult<PyColumn> {
let order_col = window.order_by.as_ref().ok_or_else(|| {
pyo3::exceptions::PyValueError::new_err(
"Window.orderBy(...) must be called before row_number().over(window)",
)
})?;
let refs: Vec<&str> = window.partition_by.iter().map(|s| s.as_str()).collect();
let col = order_col.row_number(self.descending).over(&refs);
Ok(PyColumn { inner: col })
}
}