Skip to content

Commit

Permalink
Make strings iterable. Fixes #152
Browse files Browse the repository at this point in the history
  • Loading branch information
mitsuhiko committed Dec 4, 2022
1 parent 0a8b338 commit fb5991d
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 9 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -20,6 +20,7 @@ All notable changes to MiniJinja are documented here.
- Added `indent` filter. (#151)
- Added the `map`, `select` / `selectattr` and `reject` / `rejectattr` filters.
- Added `safe` / `escaped` test.
- Strings now have the same iteration behavior as in Jinja2. (#152)

## Breaking Changes

Expand Down
13 changes: 4 additions & 9 deletions minijinja/src/filters.rs
Expand Up @@ -593,15 +593,10 @@ mod builtins {
/// an empty list is returned.
#[cfg_attr(docsrs, doc(cfg(feature = "builtins")))]
pub fn list(value: Value) -> Result<Value, Error> {
if let Some(s) = value.as_str() {
Ok(Value::from(s.chars().map(Value::from).collect::<Vec<_>>()))
} else {
let iter = ok!(value.try_iter().map_err(|err| {
Error::new(ErrorKind::InvalidOperation, "cannot convert value to list")
.with_source(err)
}));
Ok(Value::from(iter.collect::<Vec<_>>()))
}
let iter = ok!(value.try_iter().map_err(|err| {
Error::new(ErrorKind::InvalidOperation, "cannot convert value to list").with_source(err)
}));
Ok(Value::from(iter.collect::<Vec<_>>()))
}

/// Converts the value into a boolean value.
Expand Down
11 changes: 11 additions & 0 deletions minijinja/src/value/mod.rs
Expand Up @@ -923,6 +923,10 @@ impl Value {
pub(crate) fn try_iter_owned(&self) -> Result<OwnedValueIterator, Error> {
let (iter_state, len) = match self.0 {
ValueRepr::None | ValueRepr::Undefined => (ValueIteratorState::Empty, 0),
ValueRepr::String(ref s, _) => (
ValueIteratorState::Chars(0, Arc::clone(s)),
s.chars().count(),
),
ValueRepr::Seq(ref seq) => (ValueIteratorState::Seq(0, Arc::clone(seq)), seq.len()),
#[cfg(feature = "preserve_order")]
ValueRepr::Map(ref items, _) => {
Expand Down Expand Up @@ -1093,6 +1097,7 @@ impl fmt::Debug for OwnedValueIterator {

enum ValueIteratorState {
Empty,
Chars(usize, Arc<String>),
Seq(usize, Arc<Vec<Value>>),
StaticStr(usize, &'static [&'static str]),
ArcStr(usize, Vec<Arc<String>>),
Expand All @@ -1107,6 +1112,12 @@ impl ValueIteratorState {
fn advance_state(&mut self) -> Option<Value> {
match self {
ValueIteratorState::Empty => None,
ValueIteratorState::Chars(offset, ref s) => {
s.as_str()[*offset..].chars().next().map(|c| {
*offset += c.len_utf8();
Value::from(c)
})
}
ValueIteratorState::Seq(idx, items) => items
.get(*idx)
.map(|x| {
Expand Down
5 changes: 5 additions & 0 deletions minijinja/tests/inputs/loop_over_string.txt
@@ -0,0 +1,5 @@
{}
---
{% for char in "abcdefg" -%}
{{ char }}
{% endfor %}
@@ -0,0 +1,15 @@
---
source: minijinja/tests/test_templates.rs
description: "{% for char in \"abcdefg\" -%}\n {{ char }}\n{% endfor %}"
info: {}
input_file: minijinja/tests/inputs/loop_over_string.txt
---
a
b
c
d
e
f
g


0 comments on commit fb5991d

Please sign in to comment.