use indexmap::IndexMap;
use serde::{Deserialize, Serialize};
use quillmark_core::quill::CardSchema;
use quillmark_core::{Diagnostic, Document, QuillValue, Severity};
use crate::Quill;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum FormFieldSource {
Document,
Default,
Missing,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FormFieldValue {
pub value: Option<QuillValue>,
pub default: Option<QuillValue>,
pub source: FormFieldSource,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FormCard {
pub schema: CardSchema,
pub values: IndexMap<String, FormFieldValue>,
}
impl FormCard {
pub fn blank(schema: &CardSchema) -> Self {
project_card(schema, &IndexMap::new())
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Form {
pub main: FormCard,
pub cards: Vec<FormCard>,
pub diagnostics: Vec<Diagnostic>,
}
pub(crate) fn build_form(quill: &Quill, doc: &Document) -> Form {
let mut diagnostics: Vec<Diagnostic> = Vec::new();
let main_schema = &quill.source().config().main;
let main_fields = doc.main().frontmatter().to_index_map();
let main = project_card(main_schema, &main_fields);
let mut cards: Vec<FormCard> = Vec::new();
for (index, card) in doc.cards().iter().enumerate() {
let tag = card.tag();
match quill.source().config().card_type(&tag) {
Some(card_schema) => {
let card_fields = card.frontmatter().to_index_map();
cards.push(project_card(card_schema, &card_fields));
}
None => {
diagnostics.push(
Diagnostic::new(
Severity::Warning,
format!(
"card at index {index} has unknown tag \"{tag}\"; \
it is not declared in the quill schema and has been \
excluded from the form view"
),
)
.with_code("form::unknown_card_tag".to_string()),
);
}
}
}
if let Err(validation_errors) = quill.source().config().validate_document(doc) {
for err in validation_errors {
diagnostics.push(
Diagnostic::new(Severity::Error, err.to_string())
.with_code("form::validation_error".to_string()),
);
}
}
Form {
main,
cards,
diagnostics,
}
}
pub(crate) fn blank_card_for_tag(quill: &Quill, card_type: &str) -> Option<FormCard> {
quill
.source()
.config()
.card_type(card_type)
.map(FormCard::blank)
}
fn project_card(schema: &CardSchema, fields: &IndexMap<String, QuillValue>) -> FormCard {
let mut values: IndexMap<String, FormFieldValue> = IndexMap::new();
let mut field_names: Vec<&str> = schema.fields.keys().map(String::as_str).collect();
field_names.sort_by_key(|name| {
schema
.fields
.get(*name)
.and_then(|fs| fs.ui.as_ref())
.and_then(|ui| ui.order)
.unwrap_or(i32::MAX)
});
for field_name in field_names {
let field_schema = &schema.fields[field_name];
let default = field_schema.default.clone();
let ffv = match fields.get(field_name) {
Some(v) => FormFieldValue {
value: Some(v.clone()),
default,
source: FormFieldSource::Document,
},
None => match default {
Some(ref d) => FormFieldValue {
value: None,
default: Some(d.clone()),
source: FormFieldSource::Default,
},
None => FormFieldValue {
value: None,
default: None,
source: FormFieldSource::Missing,
},
},
};
values.insert(field_name.to_string(), ffv);
}
FormCard {
schema: schema.clone(),
values,
}
}
#[cfg(test)]
mod tests;