Repository: mitsuhiko/minijinja
Pull request #38 (2022-01-01 09:15:33)

Added list filter
This filter does not make the most of sense in MiniJinja as there are no iterators exposed to the runtime, but is useful to create templates that evaluate to the same in Jinja2 and MiniJinja where lists are required. An example for this is the following template which takes the keys from a dictionary:

```jinja
{% with user_list = user_map|list %}
```


Patch:
diff --git a/minijinja/src/filters.rs b/minijinja/src/filters.rs
index e9479f7..6482c75 100644
--- a/minijinja/src/filters.rs
+++ b/minijinja/src/filters.rs
@@ -111,6 +111,7 @@ pub(crate) fn get_builtin_filters() -> BTreeMap<&'static str, BoxedFilter> {
         rv.insert("join", BoxedFilter::new(join));
         rv.insert("default", BoxedFilter::new(default));
         rv.insert("d", BoxedFilter::new(default));
+        rv.insert("list", BoxedFilter::new(list));
         rv.insert("batch", BoxedFilter::new(batch));
         rv.insert("slice", BoxedFilter::new(slice));
         #[cfg(feature = "json")]
@@ -287,6 +288,32 @@ mod builtins {
         })
     }
 
+    /// Converts the input value into a list.
+    ///
+    /// If the value is already a list, then it's returned unchanged.
+    /// Applied to a map this returns the list of keys, applied to a
+    /// string this returns the characters.  If the value is undefined
+    /// an empty list is returned.
+    #[cfg_attr(docsrs, doc(cfg(feature = "builtin_filters")))]
+    pub fn list(_: &State, value: Value) -> Result<Value, Error> {
+        match &value.0 {
+            ValueRepr::Undefined => Ok(Value::from(Vec::<Value>::new())),
+            ValueRepr::String(ref s) | ValueRepr::SafeString(ref s) => {
+                Ok(Value::from(s.chars().map(Value::from).collect::<Vec<_>>()))
+            }
+            ValueRepr::Seq(_) => Ok(value.clone()),
+            ValueRepr::Map(ref m) => Ok(Value::from(
+                m.iter()
+                    .map(|x| Value::from(x.0.clone()))
+                    .collect::<Vec<_>>(),
+            )),
+            _ => Err(Error::new(
+                ErrorKind::ImpossibleOperation,
+                "cannot convert value to list",
+            )),
+        }
+    }
+
     /// """Slice an iterator and return a list of lists containing
     /// those items. Useful if you want to create a div containing
     /// three ul tags that represent columns:


Test patch:
diff --git a/minijinja/tests/inputs/filters.txt b/minijinja/tests/inputs/filters.txt
index 81245ae..1c52322 100644
--- a/minijinja/tests/inputs/filters.txt
+++ b/minijinja/tests/inputs/filters.txt
@@ -14,6 +14,10 @@ e: {{ "<"|e }}
 double-escape: {{ "<"|escape|escape }}
 safe: {{ "<"|safe|escape }}
 list-length: {{ list|length }}
+list-from-list: {{ list|list }}
+list-from-map: {{ map|list }}
+list-from-word: {{ word|list }}
+list-from-undefined: {{ undefined|list }}
 map-length: {{ map|length }}
 string-length: {{ word|length }}
 string-count: {{ word|count }}
diff --git a/minijinja/tests/snapshots/test_templates__vm@debug.txt.snap b/minijinja/tests/snapshots/test_templates__vm@debug.txt.snap
index 0aae62f..4f202f1 100644
--- a/minijinja/tests/snapshots/test_templates__vm@debug.txt.snap
+++ b/minijinja/tests/snapshots/test_templates__vm@debug.txt.snap
@@ -58,6 +58,7 @@ State {
             "escape",
             "join",
             "length",
+            "list",
             "lower",
             "replace",
             "reverse",
diff --git a/minijinja/tests/snapshots/test_templates__vm@filters.txt.snap b/minijinja/tests/snapshots/test_templates__vm@filters.txt.snap
index 0128a32..7e00b11 100644
--- a/minijinja/tests/snapshots/test_templates__vm@filters.txt.snap
+++ b/minijinja/tests/snapshots/test_templates__vm@filters.txt.snap
@@ -12,6 +12,10 @@ e: &lt;
 double-escape: &lt;
 safe: <
 list-length: 3
+list-from-list: [1, 2, 3]
+list-from-map: ["a", "c"]
+list-from-word: ['B', 'i', 'r', 'd']
+list-from-undefined: []
 map-length: 2
 string-length: 4
 string-count: 4
@@ -88,147 +92,167 @@ Template {
         0002b | BUILD_LIST (0 items),
         0002c | APPLY_FILTER (name "length"),
         0002d | EMIT,
-        0002e | EMIT_RAW (string "\nmap-length: "),
-        0002f | LOOKUP (var "map")  [line 9],
+        0002e | EMIT_RAW (string "\nlist-from-list: "),
+        0002f | LOOKUP (var "list")  [line 9],
         00030 | BUILD_LIST (0 items),
-        00031 | APPLY_FILTER (name "length"),
+        00031 | APPLY_FILTER (name "list"),
         00032 | EMIT,
-        00033 | EMIT_RAW (string "\nstring-length: "),
-        00034 | LOOKUP (var "word")  [line 10],
+        00033 | EMIT_RAW (string "\nlist-from-map: "),
+        00034 | LOOKUP (var "map")  [line 10],
         00035 | BUILD_LIST (0 items),
-        00036 | APPLY_FILTER (name "length"),
+        00036 | APPLY_FILTER (name "list"),
         00037 | EMIT,
-        00038 | EMIT_RAW (string "\nstring-count: "),
+        00038 | EMIT_RAW (string "\nlist-from-word: "),
         00039 | LOOKUP (var "word")  [line 11],
         0003a | BUILD_LIST (0 items),
-        0003b | APPLY_FILTER (name "count"),
+        0003b | APPLY_FILTER (name "list"),
         0003c | EMIT,
-        0003d | EMIT_RAW (string "\nreverse-list: "),
-        0003e | LOOKUP (var "list")  [line 12],
+        0003d | EMIT_RAW (string "\nlist-from-undefined: "),
+        0003e | LOOKUP (var "undefined")  [line 12],
         0003f | BUILD_LIST (0 items),
-        00040 | APPLY_FILTER (name "reverse"),
+        00040 | APPLY_FILTER (name "list"),
         00041 | EMIT,
-        00042 | EMIT_RAW (string "\nreverse-string: "),
-        00043 | LOOKUP (var "word")  [line 13],
+        00042 | EMIT_RAW (string "\nmap-length: "),
+        00043 | LOOKUP (var "map")  [line 13],
         00044 | BUILD_LIST (0 items),
-        00045 | APPLY_FILTER (name "reverse"),
+        00045 | APPLY_FILTER (name "length"),
         00046 | EMIT,
-        00047 | EMIT_RAW (string "\ntrim: |"),
-        00048 | LOOKUP (var "word_with_spaces")  [line 14],
+        00047 | EMIT_RAW (string "\nstring-length: "),
+        00048 | LOOKUP (var "word")  [line 14],
         00049 | BUILD_LIST (0 items),
-        0004a | APPLY_FILTER (name "trim"),
+        0004a | APPLY_FILTER (name "length"),
         0004b | EMIT,
-        0004c | EMIT_RAW (string "|\ntrim-bird: "),
+        0004c | EMIT_RAW (string "\nstring-count: "),
         0004d | LOOKUP (var "word")  [line 15],
-        0004e | LOAD_CONST (value "Bd"),
-        0004f | BUILD_LIST (1 items),
-        00050 | APPLY_FILTER (name "trim"),
-        00051 | EMIT,
-        00052 | EMIT_RAW (string "\njoin-default: "),
-        00053 | LOOKUP (var "list")  [line 16],
-        00054 | BUILD_LIST (0 items),
-        00055 | APPLY_FILTER (name "join"),
-        00056 | EMIT,
-        00057 | EMIT_RAW (string "\njoin-pipe: "),
-        00058 | LOOKUP (var "list")  [line 17],
-        00059 | LOAD_CONST (value "|"),
-        0005a | BUILD_LIST (1 items),
-        0005b | APPLY_FILTER (name "join"),
-        0005c | EMIT,
-        0005d | EMIT_RAW (string "\njoin_string: "),
-        0005e | LOOKUP (var "word")  [line 18],
-        0005f | LOAD_CONST (value "-"),
-        00060 | BUILD_LIST (1 items),
-        00061 | APPLY_FILTER (name "join"),
-        00062 | EMIT,
-        00063 | EMIT_RAW (string "\ndefault: "),
-        00064 | LOOKUP (var "undefined")  [line 19],
-        00065 | BUILD_LIST (0 items),
-        00066 | APPLY_FILTER (name "default"),
-        00067 | LOAD_CONST (value ""),
-        00068 | EQ,
-        00069 | EMIT,
-        0006a | EMIT_RAW (string "\ndefault-value: "),
-        0006b | LOOKUP (var "undefined")  [line 20],
-        0006c | LOAD_CONST (value 42),
-        0006d | BUILD_LIST (1 items),
-        0006e | APPLY_FILTER (name "default"),
-        0006f | EMIT,
-        00070 | EMIT_RAW (string "\nd: "),
-        00071 | LOOKUP (var "undefined")  [line 21],
-        00072 | BUILD_LIST (0 items),
-        00073 | APPLY_FILTER (name "d"),
-        00074 | LOAD_CONST (value ""),
-        00075 | EQ,
+        0004e | BUILD_LIST (0 items),
+        0004f | APPLY_FILTER (name "count"),
+        00050 | EMIT,
+        00051 | EMIT_RAW (string "\nreverse-list: "),
+        00052 | LOOKUP (var "list")  [line 16],
+        00053 | BUILD_LIST (0 items),
+        00054 | APPLY_FILTER (name "reverse"),
+        00055 | EMIT,
+        00056 | EMIT_RAW (string "\nreverse-string: "),
+        00057 | LOOKUP (var "word")  [line 17],
+        00058 | BUILD_LIST (0 items),
+        00059 | APPLY_FILTER (name "reverse"),
+        0005a | EMIT,
+        0005b | EMIT_RAW (string "\ntrim: |"),
+        0005c | LOOKUP (var "word_with_spaces")  [line 18],
+        0005d | BUILD_LIST (0 items),
+        0005e | APPLY_FILTER (name "trim"),
+        0005f | EMIT,
+        00060 | EMIT_RAW (string "|\ntrim-bird: "),
+        00061 | LOOKUP (var "word")  [line 19],
+        00062 | LOAD_CONST (value "Bd"),
+        00063 | BUILD_LIST (1 items),
+        00064 | APPLY_FILTER (name "trim"),
+        00065 | EMIT,
+        00066 | EMIT_RAW (string "\njoin-default: "),
+        00067 | LOOKUP (var "list")  [line 20],
+        00068 | BUILD_LIST (0 items),
+        00069 | APPLY_FILTER (name "join"),
+        0006a | EMIT,
+        0006b | EMIT_RAW (string "\njoin-pipe: "),
+        0006c | LOOKUP (var "list")  [line 21],
+        0006d | LOAD_CONST (value "|"),
+        0006e | BUILD_LIST (1 items),
+        0006f | APPLY_FILTER (name "join"),
+        00070 | EMIT,
+        00071 | EMIT_RAW (string "\njoin_string: "),
+        00072 | LOOKUP (var "word")  [line 22],
+        00073 | LOAD_CONST (value "-"),
+        00074 | BUILD_LIST (1 items),
+        00075 | APPLY_FILTER (name "join"),
         00076 | EMIT,
-        00077 | EMIT_RAW (string "\njson: "),
-        00078 | LOOKUP (var "map")  [line 22],
+        00077 | EMIT_RAW (string "\ndefault: "),
+        00078 | LOOKUP (var "undefined")  [line 23],
         00079 | BUILD_LIST (0 items),
-        0007a | APPLY_FILTER (name "tojson"),
-        0007b | EMIT,
-        0007c | EMIT_RAW (string "\njson-pretty: "),
-        0007d | LOOKUP (var "map")  [line 23],
-        0007e | LOAD_CONST (value true),
-        0007f | BUILD_LIST (1 items),
-        00080 | APPLY_FILTER (name "tojson"),
-        00081 | EMIT,
-        00082 | EMIT_RAW (string "\njson-scary-html: "),
-        00083 | LOOKUP (var "scary_html")  [line 24],
-        00084 | BUILD_LIST (0 items),
-        00085 | APPLY_FILTER (name "tojson"),
-        00086 | EMIT,
-        00087 | EMIT_RAW (string "\nurlencode: "),
-        00088 | LOAD_CONST (value "hello world/baz")  [line 25],
-        00089 | BUILD_LIST (0 items),
-        0008a | APPLY_FILTER (name "urlencode"),
-        0008b | EMIT,
-        0008c | EMIT_RAW (string "\nurlencode-kv: "),
-        0008d | LOAD_CONST (value "a")  [line 26],
-        0008e | LOAD_CONST (value "x y"),
-        0008f | LOAD_CONST (value "b"),
-        00090 | LOAD_CONST (value 2),
-        00091 | LOAD_CONST (value "c"),
-        00092 | LOAD_CONST (value 3),
-        00093 | BUILD_MAP (3 pairs),
-        00094 | BUILD_LIST (1 items),
-        00095 | CALL_FUNCTION (name "dict"),
-        00096 | BUILD_LIST (0 items),
-        00097 | APPLY_FILTER (name "urlencode"),
-        00098 | EMIT,
-        00099 | EMIT_RAW (string "\nbatch: "),
-        0009a | LOAD_CONST (value 10)  [line 27],
-        0009b | BUILD_LIST (1 items),
-        0009c | CALL_FUNCTION (name "range"),
-        0009d | LOAD_CONST (value 3),
-        0009e | BUILD_LIST (1 items),
-        0009f | APPLY_FILTER (name "batch"),
-        000a0 | EMIT,
-        000a1 | EMIT_RAW (string "\nbatch-fill: "),
-        000a2 | LOAD_CONST (value 10)  [line 28],
-        000a3 | BUILD_LIST (1 items),
-        000a4 | CALL_FUNCTION (name "range"),
-        000a5 | LOAD_CONST (value 3),
-        000a6 | LOAD_CONST (value "-"),
-        000a7 | BUILD_LIST (2 items),
-        000a8 | APPLY_FILTER (name "batch"),
-        000a9 | EMIT,
-        000aa | EMIT_RAW (string "\nslice: "),
-        000ab | LOAD_CONST (value 10)  [line 29],
-        000ac | BUILD_LIST (1 items),
-        000ad | CALL_FUNCTION (name "range"),
-        000ae | LOAD_CONST (value 3),
+        0007a | APPLY_FILTER (name "default"),
+        0007b | LOAD_CONST (value ""),
+        0007c | EQ,
+        0007d | EMIT,
+        0007e | EMIT_RAW (string "\ndefault-value: "),
+        0007f | LOOKUP (var "undefined")  [line 24],
+        00080 | LOAD_CONST (value 42),
+        00081 | BUILD_LIST (1 items),
+        00082 | APPLY_FILTER (name "default"),
+        00083 | EMIT,
+        00084 | EMIT_RAW (string "\nd: "),
+        00085 | LOOKUP (var "undefined")  [line 25],
+        00086 | BUILD_LIST (0 items),
+        00087 | APPLY_FILTER (name "d"),
+        00088 | LOAD_CONST (value ""),
+        00089 | EQ,
+        0008a | EMIT,
+        0008b | EMIT_RAW (string "\njson: "),
+        0008c | LOOKUP (var "map")  [line 26],
+        0008d | BUILD_LIST (0 items),
+        0008e | APPLY_FILTER (name "tojson"),
+        0008f | EMIT,
+        00090 | EMIT_RAW (string "\njson-pretty: "),
+        00091 | LOOKUP (var "map")  [line 27],
+        00092 | LOAD_CONST (value true),
+        00093 | BUILD_LIST (1 items),
+        00094 | APPLY_FILTER (name "tojson"),
+        00095 | EMIT,
+        00096 | EMIT_RAW (string "\njson-scary-html: "),
+        00097 | LOOKUP (var "scary_html")  [line 28],
+        00098 | BUILD_LIST (0 items),
+        00099 | APPLY_FILTER (name "tojson"),
+        0009a | EMIT,
+        0009b | EMIT_RAW (string "\nurlencode: "),
+        0009c | LOAD_CONST (value "hello world/baz")  [line 29],
+        0009d | BUILD_LIST (0 items),
+        0009e | APPLY_FILTER (name "urlencode"),
+        0009f | EMIT,
+        000a0 | EMIT_RAW (string "\nurlencode-kv: "),
+        000a1 | LOAD_CONST (value "a")  [line 30],
+        000a2 | LOAD_CONST (value "x y"),
+        000a3 | LOAD_CONST (value "b"),
+        000a4 | LOAD_CONST (value 2),
+        000a5 | LOAD_CONST (value "c"),
+        000a6 | LOAD_CONST (value 3),
+        000a7 | BUILD_MAP (3 pairs),
+        000a8 | BUILD_LIST (1 items),
+        000a9 | CALL_FUNCTION (name "dict"),
+        000aa | BUILD_LIST (0 items),
+        000ab | APPLY_FILTER (name "urlencode"),
+        000ac | EMIT,
+        000ad | EMIT_RAW (string "\nbatch: "),
+        000ae | LOAD_CONST (value 10)  [line 31],
         000af | BUILD_LIST (1 items),
-        000b0 | APPLY_FILTER (name "slice"),
-        000b1 | EMIT,
-        000b2 | EMIT_RAW (string "\nslice-fill: "),
-        000b3 | LOAD_CONST (value 10)  [line 30],
-        000b4 | BUILD_LIST (1 items),
-        000b5 | CALL_FUNCTION (name "range"),
-        000b6 | LOAD_CONST (value 3),
-        000b7 | LOAD_CONST (value "-"),
-        000b8 | BUILD_LIST (2 items),
-        000b9 | APPLY_FILTER (name "slice"),
-        000ba | EMIT,
+        000b0 | CALL_FUNCTION (name "range"),
+        000b1 | LOAD_CONST (value 3),
+        000b2 | BUILD_LIST (1 items),
+        000b3 | APPLY_FILTER (name "batch"),
+        000b4 | EMIT,
+        000b5 | EMIT_RAW (string "\nbatch-fill: "),
+        000b6 | LOAD_CONST (value 10)  [line 32],
+        000b7 | BUILD_LIST (1 items),
+        000b8 | CALL_FUNCTION (name "range"),
+        000b9 | LOAD_CONST (value 3),
+        000ba | LOAD_CONST (value "-"),
+        000bb | BUILD_LIST (2 items),
+        000bc | APPLY_FILTER (name "batch"),
+        000bd | EMIT,
+        000be | EMIT_RAW (string "\nslice: "),
+        000bf | LOAD_CONST (value 10)  [line 33],
+        000c0 | BUILD_LIST (1 items),
+        000c1 | CALL_FUNCTION (name "range"),
+        000c2 | LOAD_CONST (value 3),
+        000c3 | BUILD_LIST (1 items),
+        000c4 | APPLY_FILTER (name "slice"),
+        000c5 | EMIT,
+        000c6 | EMIT_RAW (string "\nslice-fill: "),
+        000c7 | LOAD_CONST (value 10)  [line 34],
+        000c8 | BUILD_LIST (1 items),
+        000c9 | CALL_FUNCTION (name "range"),
+        000ca | LOAD_CONST (value 3),
+        000cb | LOAD_CONST (value "-"),
+        000cc | BUILD_LIST (2 items),
+        000cd | APPLY_FILTER (name "slice"),
+        000ce | EMIT,
     ],
     blocks: {},
     initial_auto_escape: None,