mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 22:12:21 -04:00
Check for invalid conditionals on templates
This commit is contained in:
parent
156970c1b4
commit
4d7fcf585a
3 changed files with 42 additions and 7 deletions
|
@ -21,6 +21,9 @@ card-template-rendering-conditional-not-open = Found '{ $found }', but missing '
|
||||||
# when the user referenced a field that doesn't exist
|
# when the user referenced a field that doesn't exist
|
||||||
# eg, Found '{{Field}}', but there is not field called 'Field'
|
# eg, Found '{{Field}}', but there is not field called 'Field'
|
||||||
card-template-rendering-no-such-field = Found '{ $found }', but there is no field called '{ $field }'
|
card-template-rendering-no-such-field = Found '{ $found }', but there is no field called '{ $field }'
|
||||||
|
# when the user referenced a field that doesn't exist in a conditional, excepting card numbers like 'c1'
|
||||||
|
# eg, Found '{{#Field}}', but neither is it a card number nor is there a field called 'Field'
|
||||||
|
card-template-rendering-no-such-conditional = Found '{ $found }', but neither is it a card number nor is there a field called '{ $key }'
|
||||||
# This message is shown when the front side of the card is blank,
|
# This message is shown when the front side of the card is blank,
|
||||||
# either due to a badly-designed template, or because required fields
|
# either due to a badly-designed template, or because required fields
|
||||||
# are missing.
|
# are missing.
|
||||||
|
|
|
@ -119,6 +119,7 @@ pub enum TemplateError {
|
||||||
filters: String,
|
filters: String,
|
||||||
field: String,
|
field: String,
|
||||||
},
|
},
|
||||||
|
NoSuchConditional(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<io::Error> for AnkiError {
|
impl From<io::Error> for AnkiError {
|
||||||
|
|
|
@ -297,6 +297,12 @@ fn localized_template_error(tr: &I18n, err: TemplateError) -> String {
|
||||||
TemplateError::FieldNotFound { field, filters } => tr
|
TemplateError::FieldNotFound { field, filters } => tr
|
||||||
.card_template_rendering_no_such_field(format!("{{{{{}{}}}}}", filters, field), field)
|
.card_template_rendering_no_such_field(format!("{{{{{}{}}}}}", filters, field), field)
|
||||||
.into(),
|
.into(),
|
||||||
|
TemplateError::NoSuchConditional(condition) => tr
|
||||||
|
.card_template_rendering_no_such_conditional(
|
||||||
|
format!("{{{{{}}}}}", condition),
|
||||||
|
&condition[1..],
|
||||||
|
)
|
||||||
|
.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -467,12 +473,12 @@ fn render_into(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Conditional { key, children } => {
|
Conditional { key, children } => {
|
||||||
if context.nonempty_fields.contains(key.as_str()) {
|
if context.evaluate_conditional(key.as_str(), false)? {
|
||||||
render_into(rendered_nodes, children.as_ref(), context, tr)?;
|
render_into(rendered_nodes, children.as_ref(), context, tr)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NegatedConditional { key, children } => {
|
NegatedConditional { key, children } => {
|
||||||
if !context.nonempty_fields.contains(key.as_str()) {
|
if context.evaluate_conditional(key.as_str(), true)? {
|
||||||
render_into(rendered_nodes, children.as_ref(), context, tr)?;
|
render_into(rendered_nodes, children.as_ref(), context, tr)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -482,6 +488,24 @@ fn render_into(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> RenderContext<'a> {
|
||||||
|
fn evaluate_conditional(&self, key: &str, negated: bool) -> TemplateResult<bool> {
|
||||||
|
if self.nonempty_fields.contains(key) {
|
||||||
|
Ok(true ^ negated)
|
||||||
|
} else if self.fields.contains_key(key)
|
||||||
|
|| (key.starts_with('c') && key[1..].parse::<u32>().is_ok())
|
||||||
|
{
|
||||||
|
Ok(false ^ negated)
|
||||||
|
} else {
|
||||||
|
let prefix = if negated { "^" } else { "#" };
|
||||||
|
Err(TemplateError::NoSuchConditional(format!(
|
||||||
|
"{}{}",
|
||||||
|
prefix, key
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Append to last node if last node is a string, else add new node.
|
/// Append to last node if last node is a string, else add new node.
|
||||||
fn append_str_to_nodes(nodes: &mut Vec<RenderedNode>, text: &str) {
|
fn append_str_to_nodes(nodes: &mut Vec<RenderedNode>, text: &str) {
|
||||||
if let Some(RenderedNode::Text {
|
if let Some(RenderedNode::Text {
|
||||||
|
@ -1015,7 +1039,7 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn render_single() {
|
fn render_single() {
|
||||||
let map: HashMap<_, _> = vec![("F", "f"), ("B", "b"), ("E", " ")]
|
let map: HashMap<_, _> = vec![("F", "f"), ("B", "b"), ("E", " "), ("c1", "1")]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|r| (r.0, r.1.into()))
|
.map(|r| (r.0, r.1.into()))
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -1044,10 +1068,8 @@ mod test {
|
||||||
// missing
|
// missing
|
||||||
tmpl = PT::from_text("{{^M}}A{{/M}}").unwrap();
|
tmpl = PT::from_text("{{^M}}A{{/M}}").unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
tmpl.render(&ctx, &tr).unwrap(),
|
tmpl.render(&ctx, &tr).unwrap_err(),
|
||||||
vec![FN::Text {
|
TemplateError::NoSuchConditional("^M".to_string())
|
||||||
text: "A".to_owned()
|
|
||||||
},]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// nested
|
// nested
|
||||||
|
@ -1059,6 +1081,15 @@ mod test {
|
||||||
},]
|
},]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// card conditionals
|
||||||
|
tmpl = PT::from_text("{{^c2}}1{{#c1}}2{{/c1}}{{/c2}}").unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
tmpl.render(&ctx, &tr).unwrap(),
|
||||||
|
vec![FN::Text {
|
||||||
|
text: "12".to_owned()
|
||||||
|
},]
|
||||||
|
);
|
||||||
|
|
||||||
// unknown filters
|
// unknown filters
|
||||||
tmpl = PT::from_text("{{one:two:B}}").unwrap();
|
tmpl = PT::from_text("{{one:two:B}}").unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
Loading…
Reference in a new issue