Repository: mitsuhiko/minijinja
Pull request #56 (2022-03-04 21:47:37)

Added support for ignore missing
This adds support for the `include missing` flag on include blocks to make include blocks fail silently if templates are missing.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dfb415f..9bd69f8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,7 @@ All notable changes to MiniJinja are documented here.
 
 - Added `bool` filter.
 - Added `meta` API. (#55)
+- Added support for `ignore missing` in include tags. (#56)
 
 # 0.13.0
 
diff --git a/minijinja/src/ast.rs b/minijinja/src/ast.rs
index 7690a49..99776b0 100644
--- a/minijinja/src/ast.rs
+++ b/minijinja/src/ast.rs
@@ -168,6 +168,7 @@ pub struct Extends<'a> {
 #[cfg_attr(feature = "internal_debug", derive(Debug))]
 pub struct Include<'a> {
     pub name: Expr<'a>,
+    pub ignore_missing: bool,
 }
 
 /// An auto escape control block.
diff --git a/minijinja/src/compiler.rs b/minijinja/src/compiler.rs
index 07ac587..c07bbec 100644
--- a/minijinja/src/compiler.rs
+++ b/minijinja/src/compiler.rs
@@ -284,7 +284,7 @@ impl<'source> Compiler<'source> {
             ast::Stmt::Include(include) => {
                 self.set_location_from_span(include.span());
                 self.compile_expr(&include.name)?;
-                self.add(Instruction::Include);
+                self.add(Instruction::Include(include.ignore_missing));
             }
             ast::Stmt::AutoEscape(auto_escape) => {
                 self.set_location_from_span(auto_escape.span());
diff --git a/minijinja/src/environment.rs b/minijinja/src/environment.rs
index b256a31..0833bbc 100644
--- a/minijinja/src/environment.rs
+++ b/minijinja/src/environment.rs
@@ -437,7 +437,7 @@ impl<'source> Environment<'source> {
         .ok_or_else(|| {
             Error::new(
                 ErrorKind::TemplateNotFound,
-                format!("template name {:?}", name),
+                format!("template {:?} does not exist", name),
             )
         })
     }
diff --git a/minijinja/src/instructions.rs b/minijinja/src/instructions.rs
index 7da9eaf..74f644b 100644
--- a/minijinja/src/instructions.rs
+++ b/minijinja/src/instructions.rs
@@ -142,7 +142,7 @@ pub enum Instruction<'source> {
     LoadBlocks,
 
     /// Includes another template.
-    Include,
+    Include(bool),
 
     /// Sets the auto escape flag to the current value.
     PushAutoEscape,
@@ -238,7 +238,7 @@ impl<'source> fmt::Debug for Instruction<'source> {
             Instruction::JumpIfTrueOrPop(t) => write!(f, "JUMP_IF_TRUE_OR_POP (to {:>05x})", t),
             Instruction::CallBlock(n) => write!(f, "CALL_BLOCK (name {:?})", n),
             Instruction::LoadBlocks => write!(f, "LOAD_BLOCKS"),
-            Instruction::Include => write!(f, "INCLUDE"),
+            Instruction::Include(b) => write!(f, "INCLUDE (ignore missing {:?})", b),
             Instruction::PushAutoEscape => write!(f, "PUSH_AUTO_ESCAPE"),
             Instruction::PopAutoEscape => write!(f, "POP_AUTO_ESCAPE"),
             Instruction::BeginCapture => write!(f, "BEGIN_CAPTURE"),
diff --git a/minijinja/src/parser.rs b/minijinja/src/parser.rs
index f988bd0..7b19275 100644
--- a/minijinja/src/parser.rs
+++ b/minijinja/src/parser.rs
@@ -741,7 +741,18 @@ impl<'a> Parser<'a> {
 
     fn parse_include(&mut self) -> Result<ast::Include<'a>, Error> {
         let name = self.parse_expr()?;
-        Ok(ast::Include { name })
+        let ignore_missing = if matches!(self.stream.current()?, Some((Token::Ident("ignore"), _)))
+        {
+            self.stream.next()?;
+            expect_token!(self, Token::Ident("missing"), "missing keyword")?;
+            true
+        } else {
+            false
+        };
+        Ok(ast::Include {
+            name,
+            ignore_missing,
+        })
     }
 
     fn parse_auto_escape(&mut self) -> Result<ast::AutoEscape<'a>, Error> {
diff --git a/minijinja/src/syntax.rs b/minijinja/src/syntax.rs
index 7232af9..e1ccd23 100644
--- a/minijinja/src/syntax.rs
+++ b/minijinja/src/syntax.rs
@@ -364,6 +364,13 @@
 //!   Body
 //! {% include 'footer.html' %}
 //! ```
+//!
+//! Optionally `ignore missing` can be added in which case non existing templates
+//! are silently ignored.
+//!
+//! ```jinja
+//! {% include 'customization.html' ignore missing %}
+//! ```
 //!  
 //! Included templates have access to the variables of the active context.
 //!
diff --git a/minijinja/src/vm.rs b/minijinja/src/vm.rs
index 99686b7..8e7da1d 100644
--- a/minijinja/src/vm.rs
+++ b/minijinja/src/vm.rs
@@ -801,7 +801,7 @@ impl<'env> Vm<'env> {
                     pc = 0;
                     continue;
                 }
-                Instruction::Include => {
+                Instruction::Include(ignore_missing) => {
                     let name = stack.pop();
                     let name = try_ctx!(name.as_str().ok_or_else(|| {
                         Error::new(
@@ -809,7 +809,17 @@ impl<'env> Vm<'env> {
                             "template name was not a string",
                         )
                     }));
-                    let tmpl = try_ctx!(self.env.get_template(name));
+                    let tmpl = match self.env.get_template(name) {
+                        Ok(tmpl) => tmpl,
+                        Err(err) => {
+                            if *ignore_missing {
+                                pc += 1;
+                                continue;
+                            } else {
+                                bail!(err);
+                            }
+                        }
+                    };
                     let instructions = tmpl.instructions();
                     let mut referenced_blocks = BTreeMap::new();
                     for (&name, instr) in tmpl.blocks().iter() {


Test patch:
diff --git a/minijinja/tests/snapshots/test_parser__parser@include.txt.snap b/minijinja/tests/snapshots/test_parser__parser@include.txt.snap
index c7ccaea..8d5693b 100644
--- a/minijinja/tests/snapshots/test_parser__parser@include.txt.snap
+++ b/minijinja/tests/snapshots/test_parser__parser@include.txt.snap
@@ -1,5 +1,6 @@
 ---
 source: minijinja/tests/test_parser.rs
+assertion_line: 10
 expression: "&ast"
 input_file: minijinja/tests/parser-inputs/include.txt
 
@@ -11,6 +12,7 @@ Ok(
                 name: Const {
                     value: "foo.txt",
                 } @ 1:11-1:20,
+                ignore_missing: false,
             } @ 1:3-1:20,
         ],
     } @ 0:0-1:23,