use toasty::stmt::{Expr, Path};
use topcoat::{context::Cx, view::BoxView};
use crate::{
DeclarationErrorKind, Lens,
table::filter::{Filter, FilterInput, FilterValue},
toasty_compat::model::{self, ModelPath},
};
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum Trashed {
#[default]
Without,
With,
Only,
}
impl Trashed {
pub fn admits(self, trashed: bool) -> bool {
match self {
Self::Without => !trashed,
Self::With => true,
Self::Only => trashed,
}
}
pub(crate) fn from_filter(value: Option<&FilterValue>) -> Option<Self> {
match value.map(FilterValue::single) {
None => Some(Self::Without),
Some(Some(WITH)) => Some(Self::With),
Some(Some(ONLY)) => Some(Self::Only),
Some(_) => None,
}
}
}
pub(crate) struct SoftDelete<M> {
path: Path<M, Option<jiff::Timestamp>>,
read: fn(&M) -> &Option<jiff::Timestamp>,
field: Result<(usize, String), DeclarationErrorKind>,
}
impl<M: toasty::schema::Model + Send + Sync + 'static> SoftDelete<M> {
pub(crate) fn new(lens: Lens<M, Option<jiff::Timestamp>>) -> Self {
let field =
model::field::<M>(&ModelPath::of(lens.path())).map(|field| (field.index, field.name));
Self {
path: lens.path().clone(),
read: lens.reader(),
field,
}
}
}
impl<M> SoftDelete<M> {
pub(crate) fn filter(&self, trashed: Trashed) -> Option<Expr<bool>> {
match trashed {
Trashed::Without => Some(self.path.clone().is_none()),
Trashed::With => None,
Trashed::Only => Some(self.path.clone().is_some()),
}
}
pub(crate) fn is_trashed(&self, record: &M) -> bool {
(self.read)(record).is_some()
}
pub(crate) fn field(&self) -> Result<&(usize, String), &DeclarationErrorKind> {
self.field.as_ref()
}
}
pub(crate) const TRASH_FILTER: &str = "trashed";
const WITH: &str = "with";
const ONLY: &str = "only";
pub(crate) struct TrashFilter;
impl<M> Filter<M> for TrashFilter {
fn name(&self) -> &str {
TRASH_FILTER
}
fn label(&self) -> crate::Text {
crate::Text::message("tablo-trash")
}
fn reads(&self) -> Option<crate::lens::Reads<M>> {
Some(crate::lens::Reads::nothing())
}
fn to_expr(&self, _cx: &Cx, _value: &FilterValue) -> Option<Expr<bool>> {
None
}
fn is_noop_value(&self, value: &FilterValue) -> bool {
Trashed::from_filter(Some(value)).is_some()
}
fn control<'a>(&self, cx: &'a Cx, input: FilterInput) -> BoxView<'a> {
input.select_led_by(
cx,
&crate::t!(cx, "tablo-trash-without"),
vec![
(WITH.to_string(), crate::t!(cx, "tablo-trash-with")),
(ONLY.to_string(), crate::t!(cx, "tablo-trash-only")),
],
)
}
}