diff --git a/crates/djls-template-ast/src/parser.rs b/crates/djls-template-ast/src/parser.rs index 72e0dba..3560bbd 100644 --- a/crates/djls-template-ast/src/parser.rs +++ b/crates/djls-template-ast/src/parser.rs @@ -155,17 +155,26 @@ impl Parser { let specs = TagSpec::load_builtin_specs().unwrap_or_default(); - // Check if this is a closing or branch tag + // Check if this is a closing tag for (_, spec) in specs.iter() { - if Some(&tag_name) == spec.closing.as_ref() - || spec.intermediates.as_ref() - .map(|ints| ints.iter().any(|i| i.name == tag_name)) - .unwrap_or(false) - { + if Some(&tag_name) == spec.closing.as_ref() { + // let node = Node::Django(DjangoNode::Tag(TagNode::Closing { + // name: tag_name.clone(), + // bits: bits[1..].to_vec(), + // })); return Err(ParserError::ErrorSignal(Signal::SpecialTag(tag_name))); } } + // Check if this is a branch tag according to any spec + for (_, spec) in specs.iter() { + if let Some(branches) = &spec.branches { + if branches.iter().any(|i| i.name == tag_name) { + return Err(ParserError::ErrorSignal(Signal::SpecialTag(tag_name))); + } + } + } + let tag_spec = specs.get(tag_name.as_str()).cloned(); let mut children = Vec::new(); let mut current_branch: Option<(String, Vec, Vec)> = None; @@ -202,9 +211,8 @@ impl Parser { }))); } // Check if intermediate tag - if let Some(intermediates) = &spec.intermediates { - if let Some(intermediate) = intermediates.iter().find(|i| i.name == tag) - { + if let Some(branches) = &spec.branches { + if let Some(branch) = branches.iter().find(|i| i.name == tag) { // If we have a current branch, add it to children if let Some((name, bits, branch_children)) = current_branch { children.push(Node::Django(DjangoNode::Tag(TagNode::Branch { @@ -214,7 +222,7 @@ impl Parser { }))); } // Create new branch node - let branch_bits = if intermediate.args { + let branch_bits = if branch.args { match &self.tokens[self.current - 1].token_type() { TokenType::DjangoBlock(content) => content .split_whitespace() @@ -675,8 +683,7 @@ mod tests { #[test] fn test_parse_complex_if_elif() { - let source = - "{% if x > 0 %}Positive{% elif x < 0 %}Negative{% else %}Zero{% endif %}"; + let source = "{% if x > 0 %}Positive{% elif x < 0 %}Negative{% else %}Zero{% endif %}"; let tokens = Lexer::new(source).tokenize().unwrap(); let mut parser = Parser::new(tokens); let ast = parser.parse().unwrap(); diff --git a/crates/djls-template-ast/src/tagspecs.rs b/crates/djls-template-ast/src/tagspecs.rs index 4f6201d..b82b42a 100644 --- a/crates/djls-template-ast/src/tagspecs.rs +++ b/crates/djls-template-ast/src/tagspecs.rs @@ -10,12 +10,13 @@ pub struct TagSpec { #[serde(rename = "type")] pub tag_type: TagType, pub closing: Option, - pub intermediates: Option>, + #[serde(rename = "intermediates")] + pub branches: Option>, pub args: Option>, } #[derive(Debug, Clone, Deserialize)] -pub struct IntermediateSpec { +pub struct BranchSpec { pub name: String, pub args: bool, } diff --git a/crates/djls-template-ast/tagspecs/django.toml b/crates/djls-template-ast/tagspecs/django.toml index b934466..2e37383 100644 --- a/crates/djls-template-ast/tagspecs/django.toml +++ b/crates/djls-template-ast/tagspecs/django.toml @@ -2,6 +2,8 @@ type = "block" closing = "endif" +# We keep the intermediates field name in TOML since we're using serde rename +# to map it to branches in the Rust code [[django.template.defaulttags.if.intermediates]] name = "elif" args = true @@ -18,6 +20,8 @@ required = true type = "block" closing = "endfor" +# We keep the intermediates field name in TOML since we're using serde rename +# to map it to branches in the Rust code [[django.template.defaulttags.for.intermediates]] name = "empty" args = false