1 use std::borrow::Cow; 2 3 use serde_json::Value; 4 use unic_segment::Graphemes; 5 6 use crate::renderer::stack_frame::Val; 7 8 /// Enumerates the two types of for loops 9 #[derive(Debug, PartialEq)] 10 pub enum ForLoopKind { 11 /// Loop over values, eg an `Array` 12 Value, 13 /// Loop over key value pairs, eg a `HashMap` or `Object` style iteration 14 KeyValue, 15 } 16 17 /// Enumerates the states of a for loop 18 #[derive(Clone, Copy, Debug, PartialEq)] 19 pub enum ForLoopState { 20 /// State during iteration 21 Normal, 22 /// State on encountering *break* statement 23 Break, 24 /// State on encountering *continue* statement 25 Continue, 26 } 27 28 /// Enumerates on the types of values to be iterated, scalars and pairs 29 #[derive(Debug)] 30 pub enum ForLoopValues<'a> { 31 /// Values for an array style iteration 32 Array(Val<'a>), 33 /// Values for a per-character iteration on a string 34 String(Val<'a>), 35 /// Values for an object style iteration 36 Object(Vec<(String, Val<'a>)>), 37 } 38 39 impl<'a> ForLoopValues<'a> { current_key(&self, i: usize) -> String40 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 } current_value(&self, i: usize) -> Val<'a>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 // We need to have some data in the renderer for when we are in a ForLoop 70 // For example, accessing the local variable would fail when 71 // looking it up in the global context 72 #[derive(Debug)] 73 pub struct ForLoop<'a> { 74 /// The key name when iterate as a Key-Value, ie in `{% for i, person in people %}` it would be `i` 75 pub key_name: Option<String>, 76 /// The value name, ie in `{% for person in people %}` it would be `person` 77 pub value_name: String, 78 /// What's the current loop index (0-indexed) 79 pub current: usize, 80 /// A list of (key, value) for the forloop. The key is `None` for `ForLoopKind::Value` 81 pub values: ForLoopValues<'a>, 82 /// Value or KeyValue? 83 pub kind: ForLoopKind, 84 /// Has the for loop encountered break or continue? 85 pub state: ForLoopState, 86 } 87 88 impl<'a> ForLoop<'a> { from_array(value_name: &str, values: Val<'a>) -> Self89 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 from_string(value_name: &str, values: Val<'a>) -> Self100 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 from_object(key_name: &str, value_name: &str, object: &'a Value) -> Self111 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 from_object_owned(key_name: &str, value_name: &str, object: Value) -> Self128 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] increment(&mut self)151 pub fn increment(&mut self) { 152 self.current += 1; 153 self.state = ForLoopState::Normal; 154 } 155 is_key_value(&self) -> bool156 pub fn is_key_value(&self) -> bool { 157 self.kind == ForLoopKind::KeyValue 158 } 159 160 #[inline] break_loop(&mut self)161 pub fn break_loop(&mut self) { 162 self.state = ForLoopState::Break; 163 } 164 165 #[inline] continue_loop(&mut self)166 pub fn continue_loop(&mut self) { 167 self.state = ForLoopState::Continue; 168 } 169 170 #[inline] get_current_value(&self) -> Val<'a>171 pub fn get_current_value(&self) -> Val<'a> { 172 self.values.current_value(self.current) 173 } 174 175 /// Only called in `ForLoopKind::KeyValue` 176 #[inline] get_current_key(&self) -> String177 pub fn get_current_key(&self) -> String { 178 self.values.current_key(self.current) 179 } 180 181 /// Checks whether the key string given is the variable used as key for 182 /// the current forloop is_key(&self, name: &str) -> bool183 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 len(&self) -> usize195 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)] 207 mod tests { 208 use std::borrow::Cow; 209 210 use serde_json::Value; 211 212 use super::ForLoop; 213 214 #[test] test_that_iterating_on_string_yields_grapheme_clusters()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 } 226