use serde_json::Value;
use std::collections::HashMap;
pub use crate::types::{AdminWidget, FormFieldOverride, PrepopulatedField};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AdminFormMode {
Create,
Update,
}
pub type AdminFormData = HashMap<String, Value>;
pub type AdminFormResult<T> = Result<T, AdminFormErrors>;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AdminFormError {
field: Option<String>,
message: String,
}
impl AdminFormError {
pub fn field(&self) -> Option<&str> {
self.field.as_deref()
}
pub fn message(&self) -> &str {
&self.message
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct AdminFormErrors {
errors: Vec<AdminFormError>,
}
impl AdminFormErrors {
pub fn field(field: impl Into<String>, message: impl Into<String>) -> Self {
let mut errors = Self::default();
errors.push_field(field, message);
errors
}
pub fn global(message: impl Into<String>) -> Self {
let mut errors = Self::default();
errors.push_global(message);
errors
}
pub fn push_field(&mut self, field: impl Into<String>, message: impl Into<String>) {
self.errors.push(AdminFormError {
field: Some(field.into()),
message: message.into(),
});
}
pub fn push_global(&mut self, message: impl Into<String>) {
self.errors.push(AdminFormError {
field: None,
message: message.into(),
});
}
pub fn iter(&self) -> impl Iterator<Item = &AdminFormError> {
self.errors.iter()
}
pub fn is_empty(&self) -> bool {
self.errors.is_empty()
}
}
pub trait AdminForm: std::fmt::Debug + Send + Sync {
fn schema(&self) -> Vec<FormFieldOverride> {
Vec::new()
}
fn normalize(
&self,
_mode: AdminFormMode,
data: AdminFormData,
) -> AdminFormResult<AdminFormData> {
Ok(data)
}
fn validate(&self, _mode: AdminFormMode, _data: &AdminFormData) -> AdminFormResult<()> {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
use std::collections::HashMap;
#[derive(Debug)]
struct DefaultForm;
impl AdminForm for DefaultForm {}
#[test]
fn default_form_preserves_data_without_validation_errors() {
let form = DefaultForm;
let input = HashMap::from([(String::from("title"), json!("Rust"))]);
assert!(form.schema().is_empty());
assert_eq!(
form.normalize(AdminFormMode::Create, input.clone())
.unwrap(),
input,
);
assert!(form.validate(AdminFormMode::Update, &input).is_ok());
}
#[test]
fn form_errors_keep_field_and_global_messages_in_insertion_order() {
let mut errors = AdminFormErrors::field("title", "must not be empty");
errors.push_field("title", "must be unique");
errors.push_global("form is invalid");
let entries: Vec<_> = errors
.iter()
.map(|error| (error.field(), error.message()))
.collect();
assert_eq!(
entries,
vec![
(Some("title"), "must not be empty"),
(Some("title"), "must be unique"),
(None, "form is invalid"),
],
);
}
}