Skip to main content

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};