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