1use std::fmt;
2use std::sync::atomic::{AtomicUsize, Ordering};
3use std::sync::{Arc, Mutex};
4
5use crate::error::{Error, ErrorKind};
6use crate::value::{Enumerator, Object, Value, ValueIter};
7use crate::vm::state::State;
8
9pub(crate) struct LoopState {
10 pub(crate) with_loop_var: bool,
11
12 pub(crate) current_recursion_jump: Option<(u32, bool)>,
16 pub(crate) object: Arc<Loop>,
17
18 #[cfg(not(feature = "adjacent_loop_items"))]
22 iter: ValueIter,
23}
24
25impl LoopState {
26 pub fn new(
27 iter: ValueIter,
28 depth: usize,
29 with_loop_var: bool,
30 recurse_jump_target: Option<u32>,
31 current_recursion_jump: Option<(u32, bool)>,
32 ) -> LoopState {
33 let len = match iter.size_hint() {
38 (lower, Some(upper)) if lower == upper => Some(lower),
39 _ => None,
40 };
41 LoopState {
42 with_loop_var,
43 current_recursion_jump,
44 object: Arc::new(Loop {
45 idx: AtomicUsize::new(!0usize),
46 len,
47 depth,
48 recurse_jump_target,
49 last_changed_value: Mutex::default(),
50 #[cfg(feature = "adjacent_loop_items")]
51 iter: Mutex::new(AdjacentLoopItemIterWrapper::new(iter)),
52 }),
53 #[cfg(not(feature = "adjacent_loop_items"))]
54 iter,
55 }
56 }
57
58 pub fn did_not_iterate(&self) -> bool {
59 self.object.idx.load(Ordering::Relaxed) == 0
60 }
61
62 pub fn next(&mut self) -> Option<Value> {
63 self.object.idx.fetch_add(1, Ordering::Relaxed);
64 #[cfg(feature = "adjacent_loop_items")]
65 {
66 self.object.iter.lock().unwrap().next()
67 }
68 #[cfg(not(feature = "adjacent_loop_items"))]
69 {
70 self.iter.next()
71 }
72 }
73}
74
75#[cfg(feature = "adjacent_loop_items")]
76pub(crate) struct AdjacentLoopItemIterWrapper {
77 prev_item: Option<Value>,
78 current_item: Option<Value>,
79 next_item: Option<Value>,
80 iter: ValueIter,
81}
82
83#[cfg(feature = "adjacent_loop_items")]
84impl AdjacentLoopItemIterWrapper {
85 pub fn new(iter: ValueIter) -> AdjacentLoopItemIterWrapper {
86 AdjacentLoopItemIterWrapper {
87 prev_item: None,
88 current_item: None,
89 next_item: None,
90 iter,
91 }
92 }
93
94 fn next(&mut self) -> Option<Value> {
95 self.prev_item = self.current_item.take();
96 self.current_item = self.next_item.take().or_else(|| self.iter.next());
97 self.current_item.clone()
98 }
99
100 fn prev_item(&self) -> Value {
101 self.prev_item.clone().unwrap_or_default()
102 }
103
104 fn next_item(&mut self) -> Value {
105 self.next_item.clone().unwrap_or_else(|| {
106 self.next_item = self.iter.next();
107 self.next_item.clone().unwrap_or_default()
108 })
109 }
110}
111
112pub(crate) struct Loop {
113 pub len: Option<usize>,
114 pub idx: AtomicUsize,
115 pub depth: usize,
116 pub last_changed_value: Mutex<Option<Vec<Value>>>,
117 pub recurse_jump_target: Option<u32>,
118 #[cfg(feature = "adjacent_loop_items")]
119 iter: Mutex<AdjacentLoopItemIterWrapper>,
120}
121
122impl fmt::Debug for Loop {
123 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124 f.debug_struct("Loop")
125 .field("len", &self.len)
126 .field("idx", &self.idx)
127 .field("depth", &self.depth)
128 .finish()
129 }
130}
131
132impl Object for Loop {
133 fn call(self: &Arc<Self>, _state: &State, _args: &[Value]) -> Result<Value, Error> {
134 Err(Error::new(
139 ErrorKind::InvalidOperation,
140 "loop recursion cannot be called this way",
141 ))
142 }
143
144 fn call_method(
145 self: &Arc<Self>,
146 _state: &State,
147 name: &str,
148 args: &[Value],
149 ) -> Result<Value, Error> {
150 if name == "changed" {
151 let mut last_changed_value = self.last_changed_value.lock().unwrap();
152 let value = args.to_owned();
153 let changed = last_changed_value.as_ref() != Some(&value);
154 if changed {
155 *last_changed_value = Some(value);
156 Ok(Value::from(true))
157 } else {
158 Ok(Value::from(false))
159 }
160 } else if name == "cycle" {
161 let idx = self.idx.load(Ordering::Relaxed);
162 match args.get(idx % args.len()) {
163 Some(arg) => Ok(arg.clone()),
164 None => Ok(Value::UNDEFINED),
165 }
166 } else {
167 Err(Error::from(ErrorKind::UnknownMethod))
168 }
169 }
170
171 fn enumerate(self: &Arc<Self>) -> Enumerator {
172 Enumerator::Str(&[
173 "index0",
174 "index",
175 "length",
176 "revindex",
177 "revindex0",
178 "first",
179 "last",
180 "depth",
181 "depth0",
182 #[cfg(feature = "adjacent_loop_items")]
183 "previtem",
184 #[cfg(feature = "adjacent_loop_items")]
185 "nextitem",
186 ])
187 }
188
189 fn get_value(self: &Arc<Self>, key: &Value) -> Option<Value> {
190 let key = some!(key.as_str());
191 let idx = self.idx.load(Ordering::Relaxed) as u64;
192 if idx == !0 {
196 return Some(Value::UNDEFINED);
197 }
198 let len = self.len.map(|x| x as u64);
199 match key {
200 "index0" => Some(Value::from(idx)),
201 "index" => Some(Value::from(idx + 1)),
202 "length" => Some(len.map(Value::from).unwrap_or(Value::UNDEFINED)),
203 "revindex" => Some(
204 len.map(|len| Value::from(len.saturating_sub(idx)))
205 .unwrap_or(Value::UNDEFINED),
206 ),
207 "revindex0" => Some(
208 len.map(|len| Value::from(len.saturating_sub(idx).saturating_sub(1)))
209 .unwrap_or(Value::UNDEFINED),
210 ),
211 "first" => Some(Value::from(idx == 0)),
212 "last" => Some(len.map_or(Value::from(false), |len| {
213 Value::from(len == 0 || idx == len - 1)
214 })),
215 "depth" => Some(Value::from(self.depth + 1)),
216 "depth0" => Some(Value::from(self.depth)),
217 #[cfg(feature = "adjacent_loop_items")]
218 "previtem" => Some(self.iter.lock().unwrap().prev_item()),
219 #[cfg(feature = "adjacent_loop_items")]
220 "nextitem" => Some(self.iter.lock().unwrap().next_item()),
221 _ => None,
222 }
223 }
224
225 fn render(self: &Arc<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226 write!(
227 f,
228 "<loop {}/{}>",
229 self.idx.load(Ordering::Relaxed),
230 match self.len {
231 Some(ref len) => len as &dyn fmt::Display,
232 None => &"?" as &dyn fmt::Display,
233 },
234 )
235 }
236}