use crate::{
db::{
DbSession, EntityResponse, PersistedRow,
predicate::Predicate,
query::{
explain::ExplainPlan,
expr::{FilterExpr, SortExpr},
intent::{CompiledQuery, PlannedQuery, Query, QueryError},
trace::QueryTracePlan,
},
response::ResponseError,
},
traits::{EntityKind, EntityValue, SingletonEntity},
types::Id,
};
pub struct FluentDeleteQuery<'a, E>
where
E: EntityKind,
{
session: &'a DbSession<E::Canister>,
query: Query<E>,
}
impl<'a, E> FluentDeleteQuery<'a, E>
where
E: PersistedRow,
{
pub(crate) const fn new(session: &'a DbSession<E::Canister>, query: Query<E>) -> Self {
Self { session, query }
}
#[must_use]
pub const fn query(&self) -> &Query<E> {
&self.query
}
fn map_query(mut self, map: impl FnOnce(Query<E>) -> Query<E>) -> Self {
self.query = map(self.query);
self
}
fn try_map_query(
mut self,
map: impl FnOnce(Query<E>) -> Result<Query<E>, QueryError>,
) -> Result<Self, QueryError> {
self.query = map(self.query)?;
Ok(self)
}
fn map_session_query_output<T>(
&self,
map: impl FnOnce(&DbSession<E::Canister>, &Query<E>) -> Result<T, QueryError>,
) -> Result<T, QueryError> {
map(self.session, self.query())
}
#[must_use]
pub fn by_id(self, id: Id<E>) -> Self {
self.map_query(|query| query.by_id(id.key()))
}
#[must_use]
pub fn by_ids<I>(self, ids: I) -> Self
where
I: IntoIterator<Item = Id<E>>,
{
self.map_query(|query| query.by_ids(ids.into_iter().map(|id| id.key())))
}
#[must_use]
pub fn filter(self, predicate: Predicate) -> Self {
self.map_query(|query| query.filter(predicate))
}
pub fn filter_expr(self, expr: FilterExpr) -> Result<Self, QueryError> {
self.try_map_query(|query| query.filter_expr(expr))
}
pub fn sort_expr(self, expr: SortExpr) -> Result<Self, QueryError> {
self.try_map_query(|query| query.sort_expr(expr))
}
#[must_use]
pub fn order_by(self, field: impl AsRef<str>) -> Self {
self.map_query(|query| query.order_by(field))
}
#[must_use]
pub fn order_by_desc(self, field: impl AsRef<str>) -> Self {
self.map_query(|query| query.order_by_desc(field))
}
#[must_use]
pub fn limit(self, limit: u32) -> Self {
self.map_query(|query| query.limit(limit))
}
pub fn explain(&self) -> Result<ExplainPlan, QueryError> {
self.map_session_query_output(DbSession::explain_query_with_visible_indexes)
}
pub fn plan_hash_hex(&self) -> Result<String, QueryError> {
self.map_session_query_output(DbSession::query_plan_hash_hex_with_visible_indexes)
}
pub fn trace(&self) -> Result<QueryTracePlan, QueryError> {
self.map_session_query_output(DbSession::trace_query)
}
pub fn planned(&self) -> Result<PlannedQuery<E>, QueryError> {
self.map_session_query_output(DbSession::planned_query_with_visible_indexes)
}
pub fn plan(&self) -> Result<CompiledQuery<E>, QueryError> {
self.map_session_query_output(DbSession::compile_query_with_visible_indexes)
}
pub fn execute(&self) -> Result<u32, QueryError>
where
E: EntityValue,
{
self.session.execute_delete_count(self.query())
}
pub fn execute_rows(&self) -> Result<EntityResponse<E>, QueryError>
where
E: EntityValue,
{
self.session.execute_query(self.query())
}
pub fn is_empty(&self) -> Result<bool, QueryError>
where
E: EntityValue,
{
Ok(self.execute()? == 0)
}
pub fn count(&self) -> Result<u32, QueryError>
where
E: EntityValue,
{
self.execute()
}
pub fn require_one(&self) -> Result<(), QueryError>
where
E: EntityValue,
{
match self.execute()? {
1 => Ok(()),
0 => Err(ResponseError::not_found(E::PATH).into()),
count => Err(ResponseError::not_unique(E::PATH, count).into()),
}
}
pub fn require_some(&self) -> Result<(), QueryError>
where
E: EntityValue,
{
if self.execute()? == 0 {
return Err(ResponseError::not_found(E::PATH).into());
}
Ok(())
}
}
impl<E> FluentDeleteQuery<'_, E>
where
E: PersistedRow + SingletonEntity,
E::Key: Default,
{
#[must_use]
pub fn only(self) -> Self {
self.map_query(Query::only)
}
}