use toasty::stmt::{List, Query};
use topcoat::{
context::Cx,
router::error::{bad_request, forbidden, not_found, see_other},
};
use super::{
super::{
super::gate::landing_url,
fetch::{composite_pk_error, find_trashed_by_key},
},
Target,
};
use crate::{
notification::{Notification, set_notification},
resource::{ActionEntry, Mounted, Resource, Trashed},
table::TableState,
};
pub(super) async fn load_targets<R: Resource>(
cx: &Cx,
resource: &Mounted<R>,
ids: &[String],
target: Target,
trashed: Trashed,
tx: &mut toasty::Transaction<'_>,
) -> Result<Vec<R::Model>, topcoat::Error> {
let trashed = match target {
Target::Record => trashed,
Target::Bulk => Trashed::With,
};
if let Target::Record = target {
return Ok(vec![
find_trashed_by_key(cx, resource, &ids[0], trashed, tx).await?,
]);
}
let keys: Vec<&str> = ids.iter().map(String::as_str).collect();
let Some(pk_filter) = crate::toasty_compat::pk::pk_in_expr::<R::Model>(&keys) else {
if let Some(error) = composite_pk_error(resource) {
return Err(error);
}
return Err(not_found().into());
};
let rows = resource
.scoped_query_in(cx, trashed)?
.filter(pk_filter)
.exec(tx)
.await
.map_err(crate::error::unavailable)?;
if rows.len() != ids.len() {
return Err(not_found().into());
}
Ok(rows)
}
pub(super) async fn matching_query<R: Resource>(
cx: &Cx,
resource: &Mounted<R>,
query: &str,
) -> Result<Query<List<R::Model>>, topcoat::Error> {
let table = resource.table_for(cx);
let state = table.normalize_state(&TableState::from_query(query));
let refused = table.refused_filters(cx, &state).await?;
if !table.unapplied_filters(cx, &state, &refused).is_empty() {
return Err(bad_request("the selection names a filter the list does not apply").into());
}
let list = resource.list_query(cx, &state)?;
Ok(table.narrow(cx, &crate::db::db(cx)?, list, &state, &refused))
}
pub(super) async fn load_matching<M>(
query: Query<List<M>>,
max: usize,
tx: &mut toasty::Transaction<'_>,
) -> Result<Option<Vec<M>>, topcoat::Error>
where
M: toasty::schema::Model,
{
let rows = query
.limit(max.saturating_add(1))
.exec(tx)
.await
.map_err(crate::error::unavailable)?;
Ok((rows.len() <= max).then_some(rows))
}
pub(super) fn refuse<R: Resource>(
cx: &Cx,
resource: &Mounted<R>,
action: &ActionEntry<R>,
target: Target,
refused: usize,
) -> topcoat::Error {
match target {
Target::Record => forbidden().into(),
Target::Bulk => {
set_notification(
cx,
Notification::error(crate::t!(
cx,
"tablo-action-refused-all",
action = (action.label)(),
count = refused
)),
);
see_other(landing_url(cx, &resource.url)).into()
}
}
}