use topcoat::{context::Cx, router::Body};
use crate::{
form::{FieldErrors, FormInput, FormValues},
schema::Schema,
upload::{Capture, FormParts, parse_body},
};
pub(crate) async fn read_form(cx: &Cx, body: Body) -> topcoat::Result<FormParts> {
parse_body(cx, body, Capture::None).await
}
pub(super) async fn parse<I: FormInput>(
cx: &Cx,
values: &FormValues,
stored: &FormValues,
form: &str,
) -> topcoat::Result<Result<I, FieldErrors>> {
let schema = I::schema();
let mut errors = FieldErrors::new();
let parsed = match I::parse(cx, values) {
Ok(parsed) => Some(parsed),
Err(refused) => {
for error in refused {
errors.push(error);
}
None
}
};
schema.check_controls(cx, values, stored, &mut errors).await;
placed(&schema, &errors, &format!("the {form} input"), form)?;
Ok(parsed.filter(|_| errors.is_empty()).ok_or(errors))
}
pub(super) fn keep_disabled(schema: &Schema, values: &mut FormValues, stored: &FormValues) {
let keep = |values: &mut FormValues, key: &str| {
match stored.get(key) {
Some(value) => values.insert(key, value.clone()),
None => values.remove(key),
};
};
for key in schema.disabled_keys(true) {
keep(values, key);
}
for key in schema.condition_disabled(values) {
keep(values, &key);
}
}
pub(super) fn placed(
schema: &Schema,
errors: &FieldErrors,
who: &str,
form: &str,
) -> topcoat::Result<()> {
match schema.unplaced_error(errors) {
Some(key) => Err(crate::error::declaration(format!(
"{who} refused key `{key}`, which no {form} control renders"
))),
None => Ok(()),
}
}
pub(super) enum FieldFault {
Reserved,
File,
Relationship,
}
pub(super) fn field_faults<'a>(
schema: &'a Schema,
reserved: &'a [&str],
) -> impl Iterator<Item = (FieldFault, String)> + 'a {
schema.fields().filter_map(|field| {
let name = field.name().to_string();
let fault = if reserved.contains(&name.as_str()) {
FieldFault::Reserved
} else if field.is_file() {
FieldFault::File
} else if field
.as_choice()
.is_some_and(|choice| choice.is_relationship())
{
FieldFault::Relationship
} else {
return None;
};
Some((fault, name))
})
}