tera/renderer/
for_loop.rs1use std::borrow::Cow;
2
3use serde_json::Value;
4use unic_segment::Graphemes;
5
6use crate::renderer::stack_frame::Val;
7
8#[derive(Debug, PartialEq)]
10pub enum ForLoopKind {
11 Value,
13 KeyValue,
15}
16
17#[derive(Clone, Copy, Debug, PartialEq)]
19pub enum ForLoopState {
20 Normal,
22 Break,
24 Continue,
26}
27
28#[derive(Debug)]
30pub enum ForLoopValues<'a> {
31 Array(Val<'a>),
33 String(Val<'a>),
35 Object(Vec<(String, Val<'a>)>),
37}
38
39impl<'a> ForLoopValues<'a> {
40 pub fn current_key(&self, i: usize) -> String {
41 match *self {
42 ForLoopValues::Array(_) | ForLoopValues::String(_) => {
43 unreachable!("No key in array list or string")
44 }
45 ForLoopValues::Object(ref values) => {
46 values.get(i).expect("Failed getting current key").0.clone()
47 }
48 }
49 }
50 pub fn current_value(&self, i: usize) -> Val<'a> {
51 match *self {
52 ForLoopValues::Array(ref values) => match *values {
53 Cow::Borrowed(v) => {
54 Cow::Borrowed(v.as_array().expect("Is array").get(i).expect("Value"))
55 }
56 Cow::Owned(_) => {
57 Cow::Owned(values.as_array().expect("Is array").get(i).expect("Value").clone())
58 }
59 },
60 ForLoopValues::String(ref values) => {
61 let mut graphemes = Graphemes::new(values.as_str().expect("Is string"));
62 Cow::Owned(Value::String(graphemes.nth(i).expect("Value").to_string()))
63 }
64 ForLoopValues::Object(ref values) => values.get(i).expect("Value").1.clone(),
65 }
66 }
67}
68
69#[derive(Debug)]
73pub struct ForLoop<'a> {
74 pub key_name: Option<String>,
76 pub value_name: String,
78 pub current: usize,
80 pub values: ForLoopValues<'a>,
82 pub kind: ForLoopKind,
84 pub state: ForLoopState,
86}
87
88impl<'a> ForLoop<'a> {
89 pub fn from_array(value_name: &str, values: Val<'a>) -> Self {
90 ForLoop {
91 key_name: None,
92 value_name: value_name.to_string(),
93 current: 0,
94 values: ForLoopValues::Array(values),
95 kind: ForLoopKind::Value,
96 state: ForLoopState::Normal,
97 }
98 }
99
100 pub fn from_string(value_name: &str, values: Val<'a>) -> Self {
101 ForLoop {
102 key_name: None,
103 value_name: value_name.to_string(),
104 current: 0,
105 values: ForLoopValues::String(values),
106 kind: ForLoopKind::Value,
107 state: ForLoopState::Normal,
108 }
109 }
110
111 pub fn from_object(key_name: &str, value_name: &str, object: &'a Value) -> Self {
112 let object_values = object.as_object().unwrap();
113 let mut values = Vec::with_capacity(object_values.len());
114 for (k, v) in object_values {
115 values.push((k.to_string(), Cow::Borrowed(v)));
116 }
117
118 ForLoop {
119 key_name: Some(key_name.to_string()),
120 value_name: value_name.to_string(),
121 current: 0,
122 values: ForLoopValues::Object(values),
123 kind: ForLoopKind::KeyValue,
124 state: ForLoopState::Normal,
125 }
126 }
127
128 pub fn from_object_owned(key_name: &str, value_name: &str, object: Value) -> Self {
129 let object_values = match object {
130 Value::Object(c) => c,
131 _ => unreachable!(
132 "Tried to create a Forloop from an object owned but it wasn't an object"
133 ),
134 };
135 let mut values = Vec::with_capacity(object_values.len());
136 for (k, v) in object_values {
137 values.push((k.to_string(), Cow::Owned(v)));
138 }
139
140 ForLoop {
141 key_name: Some(key_name.to_string()),
142 value_name: value_name.to_string(),
143 current: 0,
144 values: ForLoopValues::Object(values),
145 kind: ForLoopKind::KeyValue,
146 state: ForLoopState::Normal,
147 }
148 }
149
150 #[inline]
151 pub fn increment(&mut self) {
152 self.current += 1;
153 self.state = ForLoopState::Normal;
154 }
155
156 pub fn is_key_value(&self) -> bool {
157 self.kind == ForLoopKind::KeyValue
158 }
159
160 #[inline]
161 pub fn break_loop(&mut self) {
162 self.state = ForLoopState::Break;
163 }
164
165 #[inline]
166 pub fn continue_loop(&mut self) {
167 self.state = ForLoopState::Continue;
168 }
169
170 #[inline]
171 pub fn get_current_value(&self) -> Val<'a> {
172 self.values.current_value(self.current)
173 }
174
175 #[inline]
177 pub fn get_current_key(&self) -> String {
178 self.values.current_key(self.current)
179 }
180
181 pub fn is_key(&self, name: &str) -> bool {
184 if self.kind == ForLoopKind::Value {
185 return false;
186 }
187
188 if let Some(ref key_name) = self.key_name {
189 return key_name == name;
190 }
191
192 false
193 }
194
195 pub fn len(&self) -> usize {
196 match self.values {
197 ForLoopValues::Array(ref values) => values.as_array().expect("Value is array").len(),
198 ForLoopValues::String(ref values) => {
199 values.as_str().expect("Value is string").chars().count()
200 }
201 ForLoopValues::Object(ref values) => values.len(),
202 }
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use std::borrow::Cow;
209
210 use serde_json::Value;
211
212 use super::ForLoop;
213
214 #[test]
215 fn test_that_iterating_on_string_yields_grapheme_clusters() {
216 let text = "a\u{310}e\u{301}o\u{308}\u{332}".to_string();
217 let string = Value::String(text.clone());
218 let mut string_loop = ForLoop::from_string("whatever", Cow::Borrowed(&string));
219 assert_eq!(*string_loop.get_current_value(), text[0..3]);
220 string_loop.increment();
221 assert_eq!(*string_loop.get_current_value(), text[3..6]);
222 string_loop.increment();
223 assert_eq!(*string_loop.get_current_value(), text[6..]);
224 }
225}