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;