minijinja/
defaults.rs

1use std::borrow::Cow;
2use std::collections::BTreeMap;
3
4use crate::error::Error;
5use crate::filters;
6use crate::output::Output;
7use crate::tests;
8use crate::utils::{write_escaped, AutoEscape};
9use crate::value::Value;
10use crate::vm::State;
11
12pub(crate) fn no_auto_escape(_: &str) -> AutoEscape {
13    AutoEscape::None
14}
15
16/// The default logic for auto escaping based on file extension.
17///
18/// * [`Html`](AutoEscape::Html): `.html`, `.htm`, `.xml`
19#[cfg_attr(
20    feature = "json",
21    doc = r" * [`Json`](AutoEscape::Json): `.json`, `.json5`, `.js`, `.yaml`, `.yml`"
22)]
23/// * [`None`](AutoEscape::None): _all others_
24///
25/// Additionally `.j2` as final extension is ignored. So `.html.j2` is the
26/// considered the same as `.html`.
27pub fn default_auto_escape_callback(name: &str) -> AutoEscape {
28    match name.strip_suffix(".j2").unwrap_or(name).rsplit('.').next() {
29        Some("html" | "htm" | "xml") => AutoEscape::Html,
30        #[cfg(feature = "json")]
31        Some("json" | "json5" | "js" | "yaml" | "yml") => AutoEscape::Json,
32        _ => AutoEscape::None,
33    }
34}
35
36/// The default formatter.
37///
38/// This formatter takes a value and directly writes it into the output format
39/// while honoring the requested auto escape format of the state.  If the
40/// value is already marked as safe, it's handled as if no auto escaping
41/// was requested.
42///
43/// * [`Html`](AutoEscape::Html): performs HTML escaping
44#[cfg_attr(
45    feature = "json",
46    doc = r" * [`Json`](AutoEscape::Json): serializes values to JSON"
47)]
48/// * [`None`](AutoEscape::None): no escaping
49/// * [`Custom(..)`](AutoEscape::Custom): results in an error
50pub fn escape_formatter(out: &mut Output, state: &State, value: &Value) -> Result<(), Error> {
51    write_escaped(out, state.auto_escape(), value)
52}
53
54pub(crate) fn get_builtin_filters() -> BTreeMap<Cow<'static, str>, Value> {
55    let mut rv = BTreeMap::new();
56    rv.insert("safe".into(), Value::from_function(filters::safe));
57    let escape = Value::from_function(filters::escape);
58    rv.insert("escape".into(), escape.clone());
59    rv.insert("e".into(), escape);
60    #[cfg(feature = "builtins")]
61    {
62        rv.insert("lower".into(), Value::from_function(filters::lower));
63        rv.insert("upper".into(), Value::from_function(filters::upper));
64        rv.insert("title".into(), Value::from_function(filters::title));
65        rv.insert(
66            "capitalize".into(),
67            Value::from_function(filters::capitalize),
68        );
69        rv.insert("replace".into(), Value::from_function(filters::replace));
70        let length = Value::from_function(filters::length);
71        rv.insert("length".into(), length.clone());
72        rv.insert("count".into(), length);
73        rv.insert("dictsort".into(), Value::from_function(filters::dictsort));
74        rv.insert("items".into(), Value::from_function(filters::items));
75        rv.insert("reverse".into(), Value::from_function(filters::reverse));
76        rv.insert("trim".into(), Value::from_function(filters::trim));
77        rv.insert("join".into(), Value::from_function(filters::join));
78        rv.insert("split".into(), Value::from_function(filters::split));
79        rv.insert("lines".into(), Value::from_function(filters::lines));
80        rv.insert("default".into(), Value::from_function(filters::default));
81        rv.insert("d".into(), Value::from_function(filters::default));
82        rv.insert("round".into(), Value::from_function(filters::round));
83        rv.insert("abs".into(), Value::from_function(filters::abs));
84        rv.insert("int".into(), Value::from_function(filters::int));
85        rv.insert("float".into(), Value::from_function(filters::float));
86        rv.insert("attr".into(), Value::from_function(filters::attr));
87        rv.insert("first".into(), Value::from_function(filters::first));
88        rv.insert("last".into(), Value::from_function(filters::last));
89        rv.insert("min".into(), Value::from_function(filters::min));
90        rv.insert("max".into(), Value::from_function(filters::max));
91        rv.insert("sort".into(), Value::from_function(filters::sort));
92        rv.insert("d".into(), Value::from_function(filters::default));
93        rv.insert("list".into(), Value::from_function(filters::list));
94        rv.insert("string".into(), Value::from_function(filters::string));
95        rv.insert("bool".into(), Value::from_function(filters::bool));
96        rv.insert("batch".into(), Value::from_function(filters::batch));
97        rv.insert("slice".into(), Value::from_function(filters::slice));
98        rv.insert("sum".into(), Value::from_function(filters::sum));
99        rv.insert("indent".into(), Value::from_function(filters::indent));
100        rv.insert("select".into(), Value::from_function(filters::select));
101        rv.insert("reject".into(), Value::from_function(filters::reject));
102        rv.insert(
103            "selectattr".into(),
104            Value::from_function(filters::selectattr),
105        );
106        rv.insert(
107            "rejectattr".into(),
108            Value::from_function(filters::rejectattr),
109        );
110        rv.insert("map".into(), Value::from_function(filters::map));
111        rv.insert("groupby".into(), Value::from_function(filters::groupby));
112        rv.insert("unique".into(), Value::from_function(filters::unique));
113        rv.insert("pprint".into(), Value::from_function(filters::pprint));
114
115        #[cfg(feature = "json")]
116        {
117            rv.insert("tojson".into(), Value::from_function(filters::tojson));
118        }
119        #[cfg(feature = "urlencode")]
120        {
121            rv.insert("urlencode".into(), Value::from_function(filters::urlencode));
122        }
123    }
124
125    rv
126}
127
128pub(crate) fn get_builtin_tests() -> BTreeMap<Cow<'static, str>, Value> {
129    let mut rv = BTreeMap::new();
130    rv.insert(
131        "undefined".into(),
132        Value::from_function(tests::is_undefined),
133    );
134    rv.insert("defined".into(), Value::from_function(tests::is_defined));
135    rv.insert("none".into(), Value::from_function(tests::is_none));
136    let is_safe = Value::from_function(tests::is_safe);
137    rv.insert("safe".into(), is_safe.clone());
138    rv.insert("escaped".into(), is_safe);
139    #[cfg(feature = "builtins")]
140    {
141        rv.insert("boolean".into(), Value::from_function(tests::is_boolean));
142        rv.insert("odd".into(), Value::from_function(tests::is_odd));
143        rv.insert("even".into(), Value::from_function(tests::is_even));
144        rv.insert(
145            "divisibleby".into(),
146            Value::from_function(tests::is_divisibleby),
147        );
148        rv.insert("number".into(), Value::from_function(tests::is_number));
149        rv.insert("integer".into(), Value::from_function(tests::is_integer));
150        rv.insert("int".into(), Value::from_function(tests::is_integer));
151        rv.insert("float".into(), Value::from_function(tests::is_float));
152        rv.insert("string".into(), Value::from_function(tests::is_string));
153        rv.insert("sequence".into(), Value::from_function(tests::is_sequence));
154        rv.insert("iterable".into(), Value::from_function(tests::is_iterable));
155        rv.insert("mapping".into(), Value::from_function(tests::is_mapping));
156        rv.insert(
157            "startingwith".into(),
158            Value::from_function(tests::is_startingwith),
159        );
160        rv.insert(
161            "endingwith".into(),
162            Value::from_function(tests::is_endingwith),
163        );
164        rv.insert("lower".into(), Value::from_function(tests::is_lower));
165        rv.insert("upper".into(), Value::from_function(tests::is_upper));
166        rv.insert("sameas".into(), Value::from_function(tests::is_sameas));
167
168        // operators
169        let is_eq = Value::from_function(tests::is_eq);
170        rv.insert("eq".into(), is_eq.clone());
171        rv.insert("equalto".into(), is_eq.clone());
172        rv.insert("==".into(), is_eq);
173        let is_ne = Value::from_function(tests::is_ne);
174        rv.insert("ne".into(), is_ne.clone());
175        rv.insert("!=".into(), is_ne);
176        let is_lt = Value::from_function(tests::is_lt);
177        rv.insert("lt".into(), is_lt.clone());
178        rv.insert("lessthan".into(), is_lt.clone());
179        rv.insert("<".into(), is_lt);
180        let is_le = Value::from_function(tests::is_le);
181        rv.insert("le".into(), is_le.clone());
182        rv.insert("<=".into(), is_le);
183        let is_gt = Value::from_function(tests::is_gt);
184        rv.insert("gt".into(), is_gt.clone());
185        rv.insert("greaterthan".into(), is_gt.clone());
186        rv.insert(">".into(), is_gt);
187        let is_ge = Value::from_function(tests::is_ge);
188        rv.insert("ge".into(), is_ge.clone());
189        rv.insert(">=".into(), is_ge);
190        rv.insert("in".into(), Value::from_function(tests::is_in));
191        rv.insert("true".into(), Value::from_function(tests::is_true));
192        rv.insert("false".into(), Value::from_function(tests::is_false));
193        rv.insert("filter".into(), Value::from_function(tests::is_filter));
194        rv.insert("test".into(), Value::from_function(tests::is_test));
195    }
196    rv
197}
198
199pub(crate) fn get_globals() -> BTreeMap<Cow<'static, str>, Value> {
200    #[allow(unused_mut)]
201    let mut rv = BTreeMap::new();
202    #[cfg(feature = "builtins")]
203    {
204        use crate::functions::{self, BoxedFunction};
205        rv.insert(
206            "range".into(),
207            BoxedFunction::new(functions::range).to_value(),
208        );
209        rv.insert(
210            "dict".into(),
211            BoxedFunction::new(functions::dict).to_value(),
212        );
213        rv.insert(
214            "debug".into(),
215            BoxedFunction::new(functions::debug).to_value(),
216        );
217        rv.insert(
218            "namespace".into(),
219            BoxedFunction::new(functions::namespace).to_value(),
220        );
221    }
222
223    rv
224}
225
226#[cfg(test)]
227mod unit_tests {
228    use super::*;
229
230    use similar_asserts::assert_eq;
231
232    #[test]
233    fn test_default_uto_escape() {
234        assert_eq!(default_auto_escape_callback("foo.html"), AutoEscape::Html);
235        assert_eq!(
236            default_auto_escape_callback("foo.html.j2"),
237            AutoEscape::Html
238        );
239        assert_eq!(default_auto_escape_callback("foo.htm"), AutoEscape::Html);
240        assert_eq!(default_auto_escape_callback("foo.htm.j2"), AutoEscape::Html);
241        assert_eq!(default_auto_escape_callback("foo.txt"), AutoEscape::None);
242        assert_eq!(default_auto_escape_callback("foo.txt.j2"), AutoEscape::None);
243
244        #[cfg(feature = "json")]
245        {
246            assert_eq!(default_auto_escape_callback("foo.yaml"), AutoEscape::Json);
247            assert_eq!(
248                default_auto_escape_callback("foo.json.j2"),
249                AutoEscape::Json
250            );
251        }
252    }
253}