From 3dfa1de68b254c9fe3324c1ff0b361115c46b1bb Mon Sep 17 00:00:00 2001 From: RumovZ Date: Thu, 27 May 2021 12:01:05 +0200 Subject: [PATCH] Check for clozes when saving notetype Error if: - Cloze notetype lacks a cloze field on either template side. - Non-cloze notetype has a cloze field on any template. --- ftl/core/card-templates.ftl | 2 ++ rslib/src/error/mod.rs | 8 +++++- rslib/src/notetype/mod.rs | 52 +++++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/ftl/core/card-templates.ftl b/ftl/core/card-templates.ftl index 7a507fce8..50e4e61b5 100644 --- a/ftl/core/card-templates.ftl +++ b/ftl/core/card-templates.ftl @@ -22,6 +22,8 @@ card-templates-add-mobile-class = Add Mobile Class card-templates-preview-settings = Options card-templates-invalid-template-number = Card template { $number } in notetype '{ $notetype }' has a problem. card-templates-identical-front = Its front side is identical with the one of card template { $number }. +card-templates-missing-cloze = The 'cloze' filter must be used on both sides of a cloze template. +card-templates-extraneous-cloze = The 'cloze' filter can only be used on cloze templates. card-templates-see-preview = See the render preview for more information. card-templates-changes-saved = Changes saved. card-templates-discard-changes = Discard changes? diff --git a/rslib/src/error/mod.rs b/rslib/src/error/mod.rs index 914074368..da6c48375 100644 --- a/rslib/src/error/mod.rs +++ b/rslib/src/error/mod.rs @@ -67,6 +67,10 @@ impl AnkiError { let details = match err.details { TemplateSaveErrorDetails::TemplateError => tr.card_templates_see_preview(), TemplateSaveErrorDetails::Duplicate(i) => tr.card_templates_identical_front(i), + TemplateSaveErrorDetails::MissingCloze => tr.card_templates_missing_cloze(), + TemplateSaveErrorDetails::ExtraneousCloze => { + tr.card_templates_extraneous_cloze() + } }; format!("{}
{}", header, details) } @@ -146,6 +150,8 @@ pub struct TemplateSaveError { #[derive(Debug, PartialEq)] pub enum TemplateSaveErrorDetails { - Duplicate(usize), TemplateError, + Duplicate(usize), + MissingCloze, + ExtraneousCloze, } diff --git a/rslib/src/notetype/mod.rs b/rslib/src/notetype/mod.rs index 9e223d7af..f472f27a7 100644 --- a/rslib/src/notetype/mod.rs +++ b/rslib/src/notetype/mod.rs @@ -287,6 +287,28 @@ impl Notetype { Ok(()) } + fn ensure_cloze_if_and_only_if_cloze_notetype( + &self, + parsed_templates: &[(Option, Option)], + ) -> Result<()> { + if self.is_cloze() { + if missing_cloze_filter(parsed_templates) { + return Err(AnkiError::TemplateSaveError(TemplateSaveError { + notetype: self.name.clone(), + ordinal: 0, + details: TemplateSaveErrorDetails::MissingCloze, + })); + } + } else if let Some(i) = find_cloze_filter(parsed_templates) { + return Err(AnkiError::TemplateSaveError(TemplateSaveError { + notetype: self.name.clone(), + ordinal: i, + details: TemplateSaveErrorDetails::ExtraneousCloze, + })); + } + Ok(()) + } + pub(crate) fn normalize_names(&mut self) { ensure_string_in_nfc(&mut self.name); for f in &mut self.fields { @@ -349,6 +371,7 @@ impl Notetype { details: TemplateSaveErrorDetails::TemplateError, })); } + self.ensure_cloze_if_and_only_if_cloze_notetype(&parsed_templates)?; let reqs = self.updated_requirements(&parsed_templates); // handle renamed+deleted fields @@ -448,6 +471,35 @@ impl Notetype { } } +/// True if the slice is empty or either template of the first tuple doesn't have a cloze field. +fn missing_cloze_filter( + parsed_templates: &[(Option, Option)], +) -> bool { + parsed_templates + .get(0) + .map_or(true, |t| !has_cloze(&t.0) || !has_cloze(&t.1)) +} + +/// Return the index of the first tuple with a cloze field on either template. +fn find_cloze_filter( + parsed_templates: &[(Option, Option)], +) -> Option { + parsed_templates.iter().enumerate().find_map(|(i, t)| { + if has_cloze(&t.0) || has_cloze(&t.1) { + Some(i) + } else { + None + } + }) +} + +/// True if the template is non-empty and has a cloze field. +fn has_cloze(template: &Option) -> bool { + template + .as_ref() + .map_or(false, |t| !t.cloze_fields().is_empty()) +} + impl From for NotetypeProto { fn from(nt: Notetype) -> Self { NotetypeProto {