use std::collections::HashMap;
use topcoat::{Result, context::Cx};
use crate::{
form::FieldErrors,
resource::{Mounted, Resource},
};
pub(super) async fn check_unique<R: Resource>(
cx: &Cx,
resource: &Mounted<R>,
schema: &crate::schema::Schema,
values: &HashMap<String, String>,
current: &HashMap<String, String>,
ex: &mut dyn toasty::Executor,
) -> Result<FieldErrors, topcoat::Error> {
let mut errors = FieldErrors::new();
let skip = schema.absent_fields(values);
for field in schema.fields() {
let name = field.name();
if !field.is_unique() || skip.contains(name) {
continue;
}
let Some(submitted) = values.get(name).map(|s| s.trim().to_string()) else {
continue;
};
if submitted.is_empty() {
continue;
}
let unchanged = current.get(name).is_some_and(|kept| {
matches!(
(field.normalize(kept), field.normalize(&submitted)),
(Ok(kept), Ok(submitted)) if kept == submitted
)
});
if unchanged {
continue;
}
let Some(filter) = field.eq_filter(&submitted) else {
continue;
};
let rows = resource
.scoped_query(cx)?
.filter(filter)
.limit(1)
.exec(&mut *ex)
.await
.map_err(crate::error::unavailable)?;
if !rows.is_empty() {
errors.add(
name,
format!("{} has already been taken", field.label_str()),
);
}
}
Ok(errors)
}
#[cfg(test)]
mod tests;