Repository: mitsuhiko/minijinja
Pull request #471 (2024-04-04 04:44:45)

Add 'Environment::templates()' iterator
A mechanism to iterate over all loaded templates


Patch:
diff --git a/minijinja/src/environment.rs b/minijinja/src/environment.rs
index d507a7a..066115f 100644
--- a/minijinja/src/environment.rs
+++ b/minijinja/src/environment.rs
@@ -339,11 +339,33 @@ impl<'source> Environment<'source> {
         self.templates.clear();
     }
 
+    /// Returns an iterator over the already loaded templates and their names.
+    ///
+    /// Only templates that are already loaded will be returned.
+    ///
+    /// ```
+    /// # use minijinja::{Environment, context};
+    /// let mut env = Environment::new();
+    /// env.add_template("hello.txt", "Hello {{ name }}!").unwrap();
+    /// env.add_template("goodbye.txt", "Goodbye {{ name }}!").unwrap();
+    ///
+    /// # assert_eq!(env.templates().count(), 2);
+    /// for (name, tmpl) in env.templates() {
+    ///     println!("{}", tmpl.render(context!{ name => "World" }).unwrap());
+    /// }
+    /// ```
+    pub fn templates(&self) -> impl Iterator<Item = (&str, Template<'_, '_>)> {
+        self.templates.iter().map(|(name, template)| {
+            let template = Template::new(self, CompiledTemplateRef::Borrowed(template));
+            (name, template)
+        })
+    }
+
     /// Fetches a template by name.
     ///
     /// This requires that the template has been loaded with
     /// [`add_template`](Environment::add_template) beforehand.  If the template was
-    /// not loaded an error of kind `TemplateNotFound` is returned.  If a loaded was
+    /// not loaded an error of kind `TemplateNotFound` is returned.  If a loader was
     /// added to the engine this can also dynamically load templates.
     ///
     /// ```
@@ -845,6 +867,10 @@ mod basic_store {
                 .map(|x| &**x)
                 .ok_or_else(|| Error::new_not_found(name))
         }
+
+        pub fn iter(&self) -> impl Iterator<Item = (&str, &CompiledTemplate<'source>)> {
+            self.map.iter().map(|(name, template)| (*name, &**template))
+        }
     }
 }
 
diff --git a/minijinja/src/loader.rs b/minijinja/src/loader.rs
index b7e4058..c023690 100644
--- a/minijinja/src/loader.rs
+++ b/minijinja/src/loader.rs
@@ -160,6 +160,20 @@ impl<'source> LoaderStore<'source> {
         )
         .map(Arc::new)
     }
+
+    pub fn iter(&self) -> impl Iterator<Item = (&str, &CompiledTemplate<'_>)> {
+        let borrowed = self
+            .borrowed_templates
+            .iter()
+            .map(|(name, template)| (*name, &**template));
+
+        let owned = self
+            .owned_templates
+            .iter()
+            .map(|(name, template)| (&**name, template.borrow_dependent()));
+
+        borrowed.chain(owned)
+    }
 }
 
 /// Safely joins two paths.


Test patch:
diff --git a/minijinja/tests/test_environment.rs b/minijinja/tests/test_environment.rs
index e3920f5..7136402 100644
--- a/minijinja/tests/test_environment.rs
+++ b/minijinja/tests/test_environment.rs
@@ -173,3 +173,21 @@ fn test_unknown_method_callback() {
     let rv = env.render_str("{{ {'x': 42}.items() }}", ()).unwrap();
     assert_snapshot!(rv, @r###"[["x", 42]]"###);
 }
+
+#[test]
+fn test_iter() {
+    let mut env = Environment::new();
+    env.add_template("hello", "Hello {{ name }}!").unwrap();
+    env.add_template("goodbye", "Goodbye {{ name }}!").unwrap();
+
+    let mut ctx = BTreeMap::new();
+    ctx.insert("name", Value::from("World"));
+    let renders = env
+        .templates()
+        .map(|(name, tmpl)| (name, tmpl.render(&ctx).unwrap()))
+        .collect::<Vec<_>>();
+
+    assert_eq!(renders.len(), 2);
+    assert!(renders.contains(&("hello", "Hello World!".into())));
+    assert!(renders.contains(&("goodbye", "Goodbye World!".into())));
+}