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#[cfg_attr(
20 feature = "json",
21 doc = r" * [`Json`](AutoEscape::Json): `.json`, `.json5`, `.js`, `.yaml`, `.yml`"
22)]
23pub 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#[cfg_attr(
45 feature = "json",
46 doc = r" * [`Json`](AutoEscape::Json): serializes values to JSON"
47)]
48pub 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 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}