rename tag spec intermediates to branches

This commit is contained in:
Josh Thomas 2025-01-04 15:45:57 -06:00
parent 2fe1bb8fd6
commit 43b5c78bfc
3 changed files with 26 additions and 14 deletions

View file

@ -155,17 +155,26 @@ impl Parser {
let specs = TagSpec::load_builtin_specs().unwrap_or_default(); 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() { for (_, spec) in specs.iter() {
if Some(&tag_name) == spec.closing.as_ref() if Some(&tag_name) == spec.closing.as_ref() {
|| spec.intermediates.as_ref() // let node = Node::Django(DjangoNode::Tag(TagNode::Closing {
.map(|ints| ints.iter().any(|i| i.name == tag_name)) // name: tag_name.clone(),
.unwrap_or(false) // bits: bits[1..].to_vec(),
{ // }));
return Err(ParserError::ErrorSignal(Signal::SpecialTag(tag_name))); 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 tag_spec = specs.get(tag_name.as_str()).cloned();
let mut children = Vec::new(); let mut children = Vec::new();
let mut current_branch: Option<(String, Vec<String>, Vec<Node>)> = None; let mut current_branch: Option<(String, Vec<String>, Vec<Node>)> = None;
@ -202,9 +211,8 @@ impl Parser {
}))); })));
} }
// Check if intermediate tag // Check if intermediate tag
if let Some(intermediates) = &spec.intermediates { if let Some(branches) = &spec.branches {
if let Some(intermediate) = intermediates.iter().find(|i| i.name == tag) if let Some(branch) = branches.iter().find(|i| i.name == tag) {
{
// If we have a current branch, add it to children // If we have a current branch, add it to children
if let Some((name, bits, branch_children)) = current_branch { if let Some((name, bits, branch_children)) = current_branch {
children.push(Node::Django(DjangoNode::Tag(TagNode::Branch { children.push(Node::Django(DjangoNode::Tag(TagNode::Branch {
@ -214,7 +222,7 @@ impl Parser {
}))); })));
} }
// Create new branch node // Create new branch node
let branch_bits = if intermediate.args { let branch_bits = if branch.args {
match &self.tokens[self.current - 1].token_type() { match &self.tokens[self.current - 1].token_type() {
TokenType::DjangoBlock(content) => content TokenType::DjangoBlock(content) => content
.split_whitespace() .split_whitespace()
@ -675,8 +683,7 @@ mod tests {
#[test] #[test]
fn test_parse_complex_if_elif() { fn test_parse_complex_if_elif() {
let source = let source = "{% if x > 0 %}Positive{% elif x < 0 %}Negative{% else %}Zero{% endif %}";
"{% if x > 0 %}Positive{% elif x < 0 %}Negative{% else %}Zero{% endif %}";
let tokens = Lexer::new(source).tokenize().unwrap(); let tokens = Lexer::new(source).tokenize().unwrap();
let mut parser = Parser::new(tokens); let mut parser = Parser::new(tokens);
let ast = parser.parse().unwrap(); let ast = parser.parse().unwrap();

View file

@ -10,12 +10,13 @@ pub struct TagSpec {
#[serde(rename = "type")] #[serde(rename = "type")]
pub tag_type: TagType, pub tag_type: TagType,
pub closing: Option<String>, pub closing: Option<String>,
pub intermediates: Option<Vec<IntermediateSpec>>, #[serde(rename = "intermediates")]
pub branches: Option<Vec<BranchSpec>>,
pub args: Option<Vec<ArgSpec>>, pub args: Option<Vec<ArgSpec>>,
} }
#[derive(Debug, Clone, Deserialize)] #[derive(Debug, Clone, Deserialize)]
pub struct IntermediateSpec { pub struct BranchSpec {
pub name: String, pub name: String,
pub args: bool, pub args: bool,
} }

View file

@ -2,6 +2,8 @@
type = "block" type = "block"
closing = "endif" 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]] [[django.template.defaulttags.if.intermediates]]
name = "elif" name = "elif"
args = true args = true
@ -18,6 +20,8 @@ required = true
type = "block" type = "block"
closing = "endfor" 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]] [[django.template.defaulttags.for.intermediates]]
name = "empty" name = "empty"
args = false args = false