use derive_where::derive_where;
use toasty::stmt::{Expr, List, Query};
use topcoat::{Result, context::Cx};
use super::{Table, TableState};
use crate::{Text, lens::Reads};
#[derive_where(Clone)]
pub struct Summary<M> {
label: Text,
query: Option<Expr<bool>>,
_model: std::marker::PhantomData<fn() -> M>,
}
impl<M> Summary<M> {
pub fn count(label: impl Into<Text>) -> Self {
Self {
label: label.into(),
query: None,
_model: std::marker::PhantomData,
}
}
pub fn query(mut self, predicate: Expr<bool>) -> Self {
self.query = Some(predicate);
self
}
pub(crate) fn name(&self) -> String {
self.label.english()
}
pub(crate) fn reads(&self) -> Reads<M>
where
M: toasty::schema::Model,
{
self.query
.as_ref()
.map_or_else(Reads::nothing, Reads::of_predicate)
}
}
impl<M> Table<M> {
pub fn summary(mut self, summary: Summary<M>) -> Self {
self.summaries.push(summary);
self
}
pub(crate) fn declared_summaries(&self) -> &[Summary<M>] {
&self.summaries
}
pub(crate) async fn summary_counts(
&self,
cx: &Cx,
query: &Query<List<M>>,
state: &TableState,
refused: &[String],
total: Option<u64>,
) -> Result<Vec<(Text, u64)>>
where
M: toasty::schema::Model,
{
let mut counts = Vec::with_capacity(self.summaries.len());
if self.summaries.is_empty() {
return Ok(counts);
}
let mut db = crate::db::db(cx)?;
let narrowed = self.narrow(cx, &db, query.clone(), state, refused);
for summary in &self.summaries {
let rows = match (&summary.query, total) {
(None, Some(total)) => {
counts.push((summary.label.clone(), total));
continue;
}
(Some(predicate), _) => narrowed.clone().filter(predicate.clone()),
(None, None) => narrowed.clone(),
};
let count = crate::toasty_compat::count(rows, &mut db)
.await
.map_err(crate::error::unavailable)?;
counts.push((summary.label.clone(), count));
}
Ok(counts)
}
}