minijinja/environment.rs
1use std::borrow::Cow;
2use std::collections::BTreeMap;
3use std::fmt;
4use std::sync::Arc;
5
6use serde::Serialize;
7
8use crate::compiler::codegen::CodeGenerator;
9use crate::compiler::instructions::Instructions;
10use crate::compiler::parser::parse_expr;
11use crate::error::{attach_basic_debug_info, Error, ErrorKind};
12use crate::expression::Expression;
13use crate::output::Output;
14use crate::template::{CompiledTemplate, CompiledTemplateRef, Template, TemplateConfig};
15use crate::utils::{AutoEscape, BTreeMapKeysDebug, UndefinedBehavior};
16use crate::value::{FunctionArgs, FunctionResult, UndefinedType, Value, ValueRepr};
17use crate::vm::State;
18use crate::{defaults, functions};
19
20type FormatterFunc = dyn Fn(&mut Output, &State, &Value) -> Result<(), Error> + Sync + Send;
21type PathJoinFunc = dyn for<'s> Fn(&'s str, &'s str) -> Cow<'s, str> + Sync + Send;
22type UnknownMethodFunc =
23 dyn Fn(&State, &Value, &str, &[Value]) -> Result<Value, Error> + Sync + Send;
24
25/// The maximum recursion in the VM. Normally each stack frame
26/// adds one to this counter (eg: every time a frame is added).
27/// However in some situations more depth is pushed if the cost
28/// of the stack frame is higher. Raising this above this limit
29/// requires enabling the `stacker` feature.
30const MAX_RECURSION: usize = 500;
31
32/// An abstraction that holds the engine configuration.
33///
34/// This object holds the central configuration state for templates. It is also
35/// the container for all loaded templates.
36///
37/// The environment holds references to the source the templates were created from.
38/// This makes it very inconvenient to pass around unless the templates are static
39/// strings.
40///
41/// There are generally two ways to construct an environment:
42///
43/// * [`Environment::new`] creates an environment preconfigured with sensible
44/// defaults. It will contain all built-in filters, tests and globals as well
45/// as a callback for auto escaping based on file extension.
46/// * [`Environment::empty`] creates a completely blank environment.
47#[derive(Clone)]
48pub struct Environment<'source> {
49 templates: TemplateStore<'source>,
50 filters: BTreeMap<Cow<'source, str>, Value>,
51 tests: BTreeMap<Cow<'source, str>, Value>,
52 globals: BTreeMap<Cow<'source, str>, Value>,
53 path_join_callback: Option<Arc<PathJoinFunc>>,
54 pub(crate) unknown_method_callback: Option<Arc<UnknownMethodFunc>>,
55 undefined_behavior: UndefinedBehavior,
56 formatter: Arc<FormatterFunc>,
57 #[cfg(feature = "debug")]
58 debug: bool,
59 #[cfg(feature = "fuel")]
60 fuel: Option<u64>,
61 recursion_limit: usize,
62}
63
64impl Default for Environment<'_> {
65 fn default() -> Self {
66 Environment::empty()
67 }
68}
69
70impl fmt::Debug for Environment<'_> {
71 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72 f.debug_struct("Environment")
73 .field("globals", &self.globals)
74 .field("tests", &BTreeMapKeysDebug(&self.tests))
75 .field("filters", &BTreeMapKeysDebug(&self.filters))
76 .field("templates", &self.templates)
77 .finish()
78 }
79}
80
81impl<'source> Environment<'source> {
82 /// Creates a new environment with sensible defaults.
83 ///
84 /// This environment does not yet contain any templates but it will have all
85 /// the default filters, tests and globals loaded. If you do not want any
86 /// default configuration you can use the alternative
87 /// [`empty`](Environment::empty) method.
88 #[cfg_attr(
89 not(feature = "serde"),
90 deprecated(
91 since = "2.0.4",
92 note = "Attempted to instantiate an environment with serde. Future \
93 versions of MiniJinja will require enabling the 'serde' feature to use \
94 serde types. To silence this warning add 'serde' to the list of features of minijinja."
95 )
96 )]
97 pub fn new() -> Environment<'source> {
98 Environment {
99 templates: TemplateStore::new(TemplateConfig::new(Arc::new(
100 defaults::default_auto_escape_callback,
101 ))),
102 filters: defaults::get_builtin_filters(),
103 tests: defaults::get_builtin_tests(),
104 globals: defaults::get_globals(),
105 path_join_callback: None,
106 unknown_method_callback: None,
107 undefined_behavior: UndefinedBehavior::default(),
108 formatter: Arc::new(defaults::escape_formatter),
109 #[cfg(feature = "debug")]
110 debug: cfg!(debug_assertions),
111 #[cfg(feature = "fuel")]
112 fuel: None,
113 recursion_limit: MAX_RECURSION,
114 }
115 }
116
117 /// Creates a completely empty environment.
118 ///
119 /// This environment has no filters, no templates, no globals and no default
120 /// logic for auto escaping configured.
121 pub fn empty() -> Environment<'source> {
122 Environment {
123 templates: TemplateStore::new(TemplateConfig::new(Arc::new(defaults::no_auto_escape))),
124 filters: Default::default(),
125 tests: Default::default(),
126 globals: Default::default(),
127 path_join_callback: None,
128 unknown_method_callback: None,
129 undefined_behavior: UndefinedBehavior::default(),
130 formatter: Arc::new(defaults::escape_formatter),
131 #[cfg(feature = "debug")]
132 debug: cfg!(debug_assertions),
133 #[cfg(feature = "fuel")]
134 fuel: None,
135 recursion_limit: MAX_RECURSION,
136 }
137 }
138
139 /// Loads a template from a string into the environment.
140 ///
141 /// The `name` parameter defines the name of the template which identifies
142 /// it. To look up a loaded template use the [`get_template`](Self::get_template)
143 /// method.
144 ///
145 /// ```
146 /// # use minijinja::Environment;
147 /// let mut env = Environment::new();
148 /// env.add_template("index.html", "Hello {{ name }}!").unwrap();
149 /// ```
150 /// This method fails if the template has a syntax error.
151 ///
152 /// Note that there are situations where the interface of this method is
153 /// too restrictive as you need to hold on to the strings for the lifetime
154 /// of the environment. To avoid this restriction use
155 /// [`add_template_owned`](Self::add_template_owned), which is available
156 /// when the `loader` feature is enabled.
157 pub fn add_template(&mut self, name: &'source str, source: &'source str) -> Result<(), Error> {
158 self.templates.insert(name, source)
159 }
160
161 /// Adds a template without borrowing.
162 ///
163 /// This lets you place an owned [`String`] in the environment rather than the
164 /// borrowed `&str` without having to worry about lifetimes.
165 ///
166 /// ```
167 /// # use minijinja::Environment;
168 /// let mut env = Environment::new();
169 /// env.add_template_owned("index.html".to_string(), "Hello {{ name }}!".to_string()).unwrap();
170 /// ```
171 ///
172 /// **Note**: the name is a bit of a misnomer as this API also allows to borrow too as
173 /// the parameters are actually [`Cow`]. This method fails if the template has a syntax error.
174 #[cfg(feature = "loader")]
175 #[cfg_attr(docsrs, doc(cfg(feature = "loader")))]
176 pub fn add_template_owned<N, S>(&mut self, name: N, source: S) -> Result<(), Error>
177 where
178 N: Into<Cow<'source, str>>,
179 S: Into<Cow<'source, str>>,
180 {
181 self.templates.insert_cow(name.into(), source.into())
182 }
183
184 /// Register a template loader as source of templates.
185 ///
186 /// When a template loader is registered, the environment gains the ability
187 /// to dynamically load templates. The loader is invoked with the name of
188 /// the template. If this template exists `Ok(Some(template_source))` has
189 /// to be returned, otherwise `Ok(None)`. Once a template has been loaded
190 /// it's stored on the environment. This means the loader is only invoked
191 /// once per template name.
192 ///
193 /// For loading templates from the file system, you can use the
194 /// [`path_loader`](crate::path_loader) function.
195 ///
196 /// # Example
197 ///
198 /// ```rust
199 /// # use minijinja::Environment;
200 /// fn create_env() -> Environment<'static> {
201 /// let mut env = Environment::new();
202 /// env.set_loader(|name| {
203 /// if name == "layout.html" {
204 /// Ok(Some("...".into()))
205 /// } else {
206 /// Ok(None)
207 /// }
208 /// });
209 /// env
210 /// }
211 /// ```
212 #[cfg(feature = "loader")]
213 #[cfg_attr(docsrs, doc(cfg(feature = "loader")))]
214 pub fn set_loader<F>(&mut self, f: F)
215 where
216 F: Fn(&str) -> Result<Option<String>, Error> + Send + Sync + 'static,
217 {
218 self.templates.set_loader(f);
219 }
220
221 /// Preserve the trailing newline when rendering templates.
222 ///
223 /// The default is `false`, which causes a single newline, if present, to be
224 /// stripped from the end of the template.
225 ///
226 /// This setting is used whenever a template is loaded into the environment.
227 /// Changing it at a later point only affects future templates loaded.
228 pub fn set_keep_trailing_newline(&mut self, yes: bool) {
229 self.templates
230 .template_config
231 .ws_config
232 .keep_trailing_newline = yes;
233 }
234
235 /// Returns the value of the trailing newline preservation flag.
236 pub fn keep_trailing_newline(&self) -> bool {
237 self.templates
238 .template_config
239 .ws_config
240 .keep_trailing_newline
241 }
242
243 /// Remove the first newline after a block.
244 ///
245 /// If this is set to `true` then the first newline after a block is removed
246 /// (block, not variable tag!). Defaults to `false`.
247 pub fn set_trim_blocks(&mut self, yes: bool) {
248 self.templates.template_config.ws_config.trim_blocks = yes;
249 }
250
251 /// Returns the value of the trim blocks flag.
252 pub fn trim_blocks(&self) -> bool {
253 self.templates.template_config.ws_config.trim_blocks
254 }
255
256 /// Remove leading spaces and tabs from the start of a line to a block.
257 ///
258 /// If this is set to `true` then leading spaces and tabs from the start of a line
259 /// to the block tag are removed.
260 pub fn set_lstrip_blocks(&mut self, yes: bool) {
261 self.templates.template_config.ws_config.lstrip_blocks = yes;
262 }
263
264 /// Returns the value of the lstrip blocks flag.
265 pub fn lstrip_blocks(&self) -> bool {
266 self.templates.template_config.ws_config.lstrip_blocks
267 }
268
269 /// Removes a template by name.
270 pub fn remove_template(&mut self, name: &str) {
271 self.templates.remove(name);
272 }
273
274 /// Sets a callback to join template paths.
275 ///
276 /// By default this returns the template path unchanged, but it can be used
277 /// to implement relative path resolution between templates. The first
278 /// argument to the callback is the name of the template to be loaded, the
279 /// second argument is the parent path.
280 ///
281 /// The following example demonstrates how a basic path joining algorithm
282 /// can be implemented.
283 ///
284 /// ```
285 /// # let mut env = minijinja::Environment::new();
286 /// env.set_path_join_callback(|name, parent| {
287 /// let mut rv = parent.split('/').collect::<Vec<_>>();
288 /// rv.pop();
289 /// name.split('/').for_each(|segment| match segment {
290 /// "." => {}
291 /// ".." => { rv.pop(); }
292 /// _ => { rv.push(segment); }
293 /// });
294 /// rv.join("/").into()
295 /// });
296 /// ```
297 pub fn set_path_join_callback<F>(&mut self, f: F)
298 where
299 F: for<'s> Fn(&'s str, &'s str) -> Cow<'s, str> + Send + Sync + 'static,
300 {
301 self.path_join_callback = Some(Arc::new(f));
302 }
303
304 /// Sets a callback invoked for unknown methods on objects.
305 ///
306 /// This registers a function with the environment that is invoked when invoking a method
307 /// on a value results in a [`UnknownMethod`](crate::ErrorKind::UnknownMethod) error.
308 /// In that case the callback is invoked with [`State`], the [`Value`], the name of
309 /// the method as `&str` as well as all arguments in a slice.
310 ///
311 /// This for instance implements a `.items()` method that invokes the `|items` filter:
312 ///
313 /// ```rust
314 /// use minijinja::value::{ValueKind, from_args};
315 /// use minijinja::{Error, ErrorKind};
316 /// # let mut env = minijinja::Environment::new();
317 ///
318 /// env.set_unknown_method_callback(|state, value, method, args| {
319 /// if value.kind() == ValueKind::Map && method == "items" {
320 /// let _: () = from_args(args)?;
321 /// state.apply_filter("items", &[value.clone()])
322 /// } else {
323 /// Err(Error::from(ErrorKind::UnknownMethod))
324 /// }
325 /// });
326 /// ```
327 ///
328 /// This can be used to increase the compatibility with Jinja2 templates that might
329 /// call Python methods on objects which are not available in minijinja. A range of
330 /// common Python methods is implemented in `minijinja-contrib`. For more information
331 /// see [minijinja_contrib::pycompat](https://docs.rs/minijinja-contrib/latest/minijinja_contrib/pycompat/).
332 pub fn set_unknown_method_callback<F>(&mut self, f: F)
333 where
334 F: Fn(&State, &Value, &str, &[Value]) -> Result<Value, Error> + Sync + Send + 'static,
335 {
336 self.unknown_method_callback = Some(Arc::new(f));
337 }
338
339 /// Removes all stored templates.
340 ///
341 /// This method is mainly useful when combined with a loader as it causes
342 /// the loader to "reload" templates. By calling this method one can trigger
343 /// a reload.
344 pub fn clear_templates(&mut self) {
345 self.templates.clear();
346 }
347
348 /// Returns an iterator over the already loaded templates and their names.
349 ///
350 /// Only templates that are already loaded will be returned.
351 ///
352 /// ```
353 /// # use minijinja::{Environment, context};
354 /// let mut env = Environment::new();
355 /// env.add_template("hello.txt", "Hello {{ name }}!").unwrap();
356 /// env.add_template("goodbye.txt", "Goodbye {{ name }}!").unwrap();
357 ///
358 /// # assert_eq!(env.templates().count(), 2);
359 /// for (name, tmpl) in env.templates() {
360 /// println!("{}", tmpl.render(context!{ name => "World" }).unwrap());
361 /// }
362 /// ```
363 pub fn templates(&self) -> impl Iterator<Item = (&str, Template<'_, '_>)> {
364 self.templates.iter().map(|(name, template)| {
365 let template = Template::new(self, CompiledTemplateRef::Borrowed(template));
366 (name, template)
367 })
368 }
369
370 /// Fetches a template by name.
371 ///
372 /// This requires that the template has been loaded with
373 /// [`add_template`](Environment::add_template) beforehand. If the template was
374 /// not loaded an error of kind `TemplateNotFound` is returned. If a loader was
375 /// added to the engine this can also dynamically load templates.
376 ///
377 /// ```
378 /// # use minijinja::{Environment, context};
379 /// let mut env = Environment::new();
380 /// env.add_template("hello.txt", "Hello {{ name }}!").unwrap();
381 /// let tmpl = env.get_template("hello.txt").unwrap();
382 /// println!("{}", tmpl.render(context!{ name => "World" }).unwrap());
383 /// ```
384 pub fn get_template(&self, name: &str) -> Result<Template<'_, '_>, Error> {
385 let compiled = ok!(self.templates.get(name));
386 Ok(Template::new(self, CompiledTemplateRef::Borrowed(compiled)))
387 }
388
389 /// Loads a template from a string.
390 ///
391 /// In some cases you really only need to work with (eg: render) a template to be
392 /// rendered once only.
393 ///
394 /// ```
395 /// # use minijinja::{Environment, context};
396 /// let env = Environment::new();
397 /// let tmpl = env.template_from_named_str("template_name", "Hello {{ name }}").unwrap();
398 /// let rv = tmpl.render(context! { name => "World" });
399 /// println!("{}", rv.unwrap());
400 /// ```
401 pub fn template_from_named_str(
402 &self,
403 name: &'source str,
404 source: &'source str,
405 ) -> Result<Template<'_, 'source>, Error> {
406 Ok(Template::new(
407 self,
408 CompiledTemplateRef::Owned(Arc::new(ok!(CompiledTemplate::new(
409 name,
410 source,
411 &self.templates.template_config,
412 )))),
413 ))
414 }
415
416 /// Loads a template from a string, with name `<string>`.
417 ///
418 /// This is a shortcut to [`template_from_named_str`](Self::template_from_named_str)
419 /// with name set to `<string>`.
420 pub fn template_from_str(&self, source: &'source str) -> Result<Template<'_, 'source>, Error> {
421 self.template_from_named_str("<string>", source)
422 }
423
424 /// Parses and renders a template from a string in one go with name.
425 ///
426 /// Like [`render_str`](Self::render_str), but provide a name for the
427 /// template to be used instead of the default `<string>`. This is an
428 /// alias for [`template_from_named_str`](Self::template_from_named_str) paired with
429 /// [`render`](Template::render).
430 ///
431 /// ```
432 /// # use minijinja::{Environment, context};
433 /// let env = Environment::new();
434 /// let rv = env.render_named_str(
435 /// "template_name",
436 /// "Hello {{ name }}",
437 /// context!{ name => "World" }
438 /// );
439 /// println!("{}", rv.unwrap());
440 /// ```
441 ///
442 /// **Note on values:** The [`Value`] type implements `Serialize` and can be
443 /// efficiently passed to render. It does not undergo actual serialization.
444 pub fn render_named_str<S: Serialize>(
445 &self,
446 name: &str,
447 source: &str,
448 ctx: S,
449 ) -> Result<String, Error> {
450 ok!(self.template_from_named_str(name, source)).render(ctx)
451 }
452
453 /// Parses and renders a template from a string in one go.
454 ///
455 /// In some cases you really only need a template to be rendered once from
456 /// a string and returned. The internal name of the template is `<string>`.
457 ///
458 /// This is an alias for [`template_from_str`](Self::template_from_str) paired with
459 /// [`render`](Template::render).
460 ///
461 /// **Note on values:** The [`Value`] type implements `Serialize` and can be
462 /// efficiently passed to render. It does not undergo actual serialization.
463 pub fn render_str<S: Serialize>(&self, source: &str, ctx: S) -> Result<String, Error> {
464 // reduce total amount of code falling under mono morphization into
465 // this function, and share the rest in _eval.
466 ok!(self.template_from_str(source)).render(ctx)
467 }
468
469 /// Sets a new function to select the default auto escaping.
470 ///
471 /// This function is invoked when templates are loaded into the environment
472 /// to determine the default auto escaping behavior. The function is
473 /// invoked with the name of the template and can make an initial auto
474 /// escaping decision based on that. The default implementation
475 /// ([`default_auto_escape_callback`](defaults::default_auto_escape_callback))
476 /// turn on escaping depending on the file extension.
477 ///
478 /// ```
479 /// # use minijinja::{Environment, AutoEscape};
480 /// # let mut env = Environment::new();
481 /// env.set_auto_escape_callback(|name| {
482 /// if matches!(name.rsplit('.').next().unwrap_or(""), "html" | "htm" | "aspx") {
483 /// AutoEscape::Html
484 /// } else {
485 /// AutoEscape::None
486 /// }
487 /// });
488 /// ```
489 pub fn set_auto_escape_callback<F>(&mut self, f: F)
490 where
491 F: Fn(&str) -> AutoEscape + 'static + Sync + Send,
492 {
493 self.templates.template_config.default_auto_escape = Arc::new(f);
494 }
495
496 /// Changes the undefined behavior.
497 ///
498 /// This changes the runtime behavior of [`undefined`](Value::UNDEFINED) values in
499 /// the template engine. For more information see [`UndefinedBehavior`]. The
500 /// default is [`UndefinedBehavior::Lenient`].
501 pub fn set_undefined_behavior(&mut self, behavior: UndefinedBehavior) {
502 self.undefined_behavior = behavior;
503 }
504
505 /// Returns the current undefined behavior.
506 ///
507 /// This is particularly useful if a filter function or similar wants to change its
508 /// behavior with regards to undefined values.
509 #[inline(always)]
510 pub fn undefined_behavior(&self) -> UndefinedBehavior {
511 self.undefined_behavior
512 }
513
514 /// Sets a different formatter function.
515 ///
516 /// The formatter is invoked to format the given value into the provided
517 /// [`Output`]. The default implementation is
518 /// [`escape_formatter`](defaults::escape_formatter).
519 ///
520 /// When implementing a custom formatter it depends on if auto escaping
521 /// should be supported or not. If auto escaping should be supported then
522 /// it's easiest to just wrap the default formatter. The
523 /// following example swaps out `None` values before rendering for
524 /// `Undefined` which renders as an empty string instead.
525 ///
526 /// The current value of the auto escape flag can be retrieved directly
527 /// from the [`State`].
528 ///
529 /// ```
530 /// # use minijinja::Environment;
531 /// # let mut env = Environment::new();
532 /// use minijinja::escape_formatter;
533 /// use minijinja::value::Value;
534 ///
535 /// env.set_formatter(|out, state, value| {
536 /// escape_formatter(
537 /// out,
538 /// state,
539 /// if value.is_none() {
540 /// &Value::UNDEFINED
541 /// } else {
542 /// value
543 /// },
544 /// )
545 ///});
546 /// # assert_eq!(env.render_str("{{ none }}", ()).unwrap(), "");
547 /// ```
548 pub fn set_formatter<F>(&mut self, f: F)
549 where
550 F: Fn(&mut Output, &State, &Value) -> Result<(), Error> + 'static + Sync + Send,
551 {
552 self.formatter = Arc::new(f);
553 }
554
555 /// Enable or disable the debug mode.
556 ///
557 /// When the debug mode is enabled the engine will dump out some of the
558 /// execution state together with the source information of the executing
559 /// template when an error is created. The cost of this is relatively
560 /// high as the data including the template source is cloned.
561 ///
562 /// When this is enabled templates will print debug information with source
563 /// context when the error is printed.
564 ///
565 /// This requires the `debug` feature. This is enabled by default if
566 /// debug assertions are enabled and false otherwise.
567 #[cfg(feature = "debug")]
568 #[cfg_attr(docsrs, doc(cfg(feature = "debug")))]
569 pub fn set_debug(&mut self, enabled: bool) {
570 self.debug = enabled;
571 }
572
573 /// Returns the current value of the debug flag.
574 #[cfg(feature = "debug")]
575 pub fn debug(&self) -> bool {
576 self.debug
577 }
578
579 /// Sets the optional fuel of the engine.
580 ///
581 /// When MiniJinja is compiled with the `fuel` feature then every
582 /// instruction consumes a certain amount of fuel. Usually `1`, some will
583 /// consume no fuel. By default the engine has the fuel feature disabled
584 /// (`None`). To turn on fuel set something like `Some(50000)` which will
585 /// allow 50.000 instructions to execute before running out of fuel.
586 ///
587 /// To find out how much fuel is consumed, you can access the fuel levels
588 /// from the [`State`](crate::State).
589 ///
590 /// Fuel consumed per-render.
591 #[cfg(feature = "fuel")]
592 #[cfg_attr(docsrs, doc(cfg(feature = "fuel")))]
593 pub fn set_fuel(&mut self, fuel: Option<u64>) {
594 self.fuel = fuel;
595 }
596
597 /// Returns the configured fuel.
598 #[cfg(feature = "fuel")]
599 #[cfg_attr(docsrs, doc(cfg(feature = "fuel")))]
600 pub fn fuel(&self) -> Option<u64> {
601 self.fuel
602 }
603
604 /// Sets the syntax for the environment.
605 ///
606 /// This setting is used whenever a template is loaded into the environment.
607 /// Changing it at a later point only affects future templates loaded.
608 ///
609 /// See [`SyntaxConfig`](crate::syntax::SyntaxConfig) for more information.
610 #[cfg(feature = "custom_syntax")]
611 #[cfg_attr(docsrs, doc(cfg(feature = "custom_syntax")))]
612 pub fn set_syntax(&mut self, syntax: crate::syntax::SyntaxConfig) {
613 self.templates.template_config.syntax_config = syntax;
614 }
615
616 /// Returns the current syntax config.
617 #[cfg(feature = "custom_syntax")]
618 #[cfg_attr(docsrs, doc(cfg(feature = "custom_syntax")))]
619 pub fn syntax(&self) -> &crate::syntax::SyntaxConfig {
620 &self.templates.template_config.syntax_config
621 }
622
623 /// Reconfigures the runtime recursion limit.
624 ///
625 /// This defaults to `500`. Raising it above that level requires the `stacker`
626 /// feature to be enabled. Otherwise the limit is silently capped at that safe
627 /// maximum. Note that the maximum is not necessarily safe if the thread uses
628 /// a lot of stack space already, it's just a value that was validated once to
629 /// provide basic protection.
630 ///
631 /// Every operation that requires recursion in MiniJinja increments an internal
632 /// recursion counter. The actual cost attributed to that recursion depends on
633 /// the cost of the operation. If statements and for loops for instance only
634 /// increase the counter by 1, whereas template includes and macros might increase
635 /// it to 10 or more.
636 ///
637 /// **Note on stack growth:** even if the stacker feature is enabled it does not
638 /// mean that in all cases stack can grow to the limits desired. For instance in
639 /// WASM the maximum limits are additionally enforced by the runtime.
640 pub fn set_recursion_limit(&mut self, level: usize) {
641 #[cfg(not(feature = "stacker"))]
642 {
643 self.recursion_limit = level.min(MAX_RECURSION);
644 }
645 #[cfg(feature = "stacker")]
646 {
647 self.recursion_limit = level;
648 }
649 }
650
651 /// Returns the current max recursion limit.
652 pub fn recursion_limit(&self) -> usize {
653 self.recursion_limit
654 }
655
656 /// Compiles an expression.
657 ///
658 /// This lets one compile an expression in the template language and
659 /// receive the output. This lets one use the expressions of the language
660 /// be used as a minimal scripting language. For more information and an
661 /// example see [`Expression`].
662 pub fn compile_expression(&self, expr: &'source str) -> Result<Expression<'_, 'source>, Error> {
663 self._compile_expression(expr)
664 .map(|instr| Expression::new(self, instr))
665 }
666
667 /// Compiles an expression without capturing the lifetime.
668 ///
669 /// This works exactly like [`compile_expression`](Self::compile_expression) but
670 /// lets you pass an owned string without capturing the lifetime.
671 #[cfg(feature = "loader")]
672 #[cfg_attr(docsrs, doc(cfg(feature = "loader")))]
673 pub fn compile_expression_owned<E>(&self, expr: E) -> Result<Expression<'_, 'source>, Error>
674 where
675 E: Into<Cow<'source, str>>,
676 {
677 crate::loader::OwnedInstructions::try_new(Box::from(expr.into()), |expr| {
678 self._compile_expression(expr)
679 })
680 .map(|instr| Expression::new_owned(self, instr))
681 }
682
683 fn _compile_expression<'expr>(&self, expr: &'expr str) -> Result<Instructions<'expr>, Error> {
684 attach_basic_debug_info(
685 parse_expr(expr).map(|ast| {
686 let mut g = CodeGenerator::new("<expression>", expr);
687 g.compile_expr(&ast);
688 g.finish().0
689 }),
690 expr,
691 )
692 }
693
694 /// Adds a new filter function.
695 ///
696 /// Filter functions are functions that can be applied to values in
697 /// templates. For details about filters have a look at
698 /// [`filters`](crate::filters).
699 pub fn add_filter<N, F, Rv, Args>(&mut self, name: N, f: F)
700 where
701 N: Into<Cow<'source, str>>,
702 F: functions::Function<Rv, Args>,
703 Rv: FunctionResult,
704 Args: for<'a> FunctionArgs<'a>,
705 {
706 self.filters.insert(name.into(), Value::from_function(f));
707 }
708
709 /// Removes a filter by name.
710 pub fn remove_filter(&mut self, name: &str) {
711 self.filters.remove(name);
712 }
713
714 /// Adds a new test function.
715 ///
716 /// Test functions are similar to filters but perform a check on a value
717 /// where the return value is always true or false. For details about tests
718 /// have a look at [`tests`](crate::tests).
719 pub fn add_test<N, F, Rv, Args>(&mut self, name: N, f: F)
720 where
721 N: Into<Cow<'source, str>>,
722 F: functions::Function<Rv, Args>,
723 Rv: FunctionResult,
724 Args: for<'a> FunctionArgs<'a>,
725 {
726 self.tests.insert(name.into(), Value::from_function(f));
727 }
728
729 /// Removes a test by name.
730 pub fn remove_test(&mut self, name: &str) {
731 self.tests.remove(name);
732 }
733
734 /// Adds a new global function.
735 ///
736 /// For details about functions have a look at [`functions`]. Note that
737 /// functions and other global variables share the same namespace.
738 /// For more details about functions have a look at
739 /// [`Function`](crate::functions::Function).
740 ///
741 /// This is a shortcut for calling [`add_global`](Self::add_global) with the
742 /// function wrapped with [`Value::from_function`].
743 pub fn add_function<N, F, Rv, Args>(&mut self, name: N, f: F)
744 where
745 N: Into<Cow<'source, str>>,
746 F: functions::Function<Rv, Args>,
747 Rv: FunctionResult,
748 Args: for<'a> FunctionArgs<'a>,
749 {
750 self.add_global(name.into(), Value::from_function(f))
751 }
752
753 /// Adds a global variable.
754 pub fn add_global<N, V>(&mut self, name: N, value: V)
755 where
756 N: Into<Cow<'source, str>>,
757 V: Into<Value>,
758 {
759 self.globals.insert(name.into(), value.into());
760 }
761
762 /// Removes a global function or variable by name.
763 pub fn remove_global(&mut self, name: &str) {
764 self.globals.remove(name);
765 }
766
767 /// Returns an iterator of all global variables.
768 pub fn globals(&self) -> impl Iterator<Item = (&str, Value)> {
769 self.globals
770 .iter()
771 .map(|(key, value)| (key as &str, value.clone()))
772 }
773
774 /// Returns an empty [`State`] for testing purposes and similar.
775 pub fn empty_state(&self) -> State<'_, '_> {
776 State::new_for_env(self)
777 }
778
779 /// Looks up a global.
780 pub(crate) fn get_global(&self, name: &str) -> Option<Value> {
781 self.globals.get(name).cloned()
782 }
783
784 /// Looks up a filter.
785 pub(crate) fn get_filter(&self, name: &str) -> Option<&Value> {
786 self.filters.get(name)
787 }
788
789 /// Looks up a test function.
790 pub(crate) fn get_test(&self, name: &str) -> Option<&Value> {
791 self.tests.get(name)
792 }
793
794 pub(crate) fn initial_auto_escape(&self, name: &str) -> AutoEscape {
795 (self.templates.template_config.default_auto_escape)(name)
796 }
797
798 /// Formats a value into the final format.
799 ///
800 /// This step is called finalization in Jinja2 but since we are writing into
801 /// the output stream rather than converting values, it's renamed to format
802 /// here.
803 pub(crate) fn format(
804 &self,
805 value: &Value,
806 state: &State,
807 out: &mut Output,
808 ) -> Result<(), Error> {
809 match (self.undefined_behavior, &value.0) {
810 // this intentionally does not check for SilentUndefined. SilentUndefined is
811 // exclusively used in the missing else condition of an if expression to match
812 // Jinja2 behavior. Those go straight to the formatter.
813 (
814 UndefinedBehavior::Strict | UndefinedBehavior::SemiStrict,
815 &ValueRepr::Undefined(UndefinedType::Default),
816 ) => Err(Error::from(ErrorKind::UndefinedError)),
817 _ => (self.formatter)(out, state, value),
818 }
819 }
820
821 /// Performs a template path join.
822 pub(crate) fn join_template_path<'s>(&self, name: &'s str, parent: &'s str) -> Cow<'s, str> {
823 match self.path_join_callback {
824 Some(ref cb) => cb(name, parent),
825 None => Cow::Borrowed(name),
826 }
827 }
828}
829
830#[cfg(not(feature = "loader"))]
831mod basic_store {
832 use super::*;
833
834 #[derive(Clone)]
835 pub(crate) struct BasicStore<'source> {
836 pub template_config: TemplateConfig,
837 map: BTreeMap<&'source str, Arc<CompiledTemplate<'source>>>,
838 }
839
840 impl fmt::Debug for BasicStore<'_> {
841 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
842 BTreeMapKeysDebug(&self.map).fmt(f)
843 }
844 }
845
846 impl<'source> BasicStore<'source> {
847 pub fn new(template_config: TemplateConfig) -> BasicStore<'source> {
848 BasicStore {
849 template_config,
850 map: BTreeMap::default(),
851 }
852 }
853
854 pub fn insert(&mut self, name: &'source str, source: &'source str) -> Result<(), Error> {
855 self.map.insert(
856 name,
857 Arc::new(ok!(CompiledTemplate::new(
858 name,
859 source,
860 &self.template_config
861 ))),
862 );
863 Ok(())
864 }
865
866 pub fn remove(&mut self, name: &str) {
867 self.map.remove(name);
868 }
869
870 pub fn clear(&mut self) {
871 self.map.clear();
872 }
873
874 pub fn get(&self, name: &str) -> Result<&CompiledTemplate<'source>, Error> {
875 self.map
876 .get(name)
877 .map(|x| &**x)
878 .ok_or_else(|| Error::new_not_found(name))
879 }
880
881 pub fn iter(&self) -> impl Iterator<Item = (&str, &CompiledTemplate<'source>)> {
882 self.map.iter().map(|(name, template)| (*name, &**template))
883 }
884 }
885}
886
887#[cfg(not(feature = "loader"))]
888use self::basic_store::BasicStore as TemplateStore;
889
890#[cfg(feature = "loader")]
891use crate::loader::LoaderStore as TemplateStore;