1use std::{fmt, mem, ops};
2
3use imbl::Vector;
4
5use crate::vector::OneOrManyDiffs;
6
7use super::{entry::EntryIndex, BroadcastMessage, ObservableVector, VectorDiff};
8
9pub struct ObservableVectorTransaction<'o, T: Clone> {
16 inner: &'o mut ObservableVector<T>,
18 values: Vector<T>,
20 batch: Vec<VectorDiff<T>>,
22}
23
24impl<'o, T: Clone + 'static> ObservableVectorTransaction<'o, T> {
25 pub(super) fn new(inner: &'o mut ObservableVector<T>) -> Self {
26 let values = inner.values.clone();
27 Self { inner, values, batch: Vec::new() }
28 }
29
30 pub fn commit(mut self) {
33 #[cfg(feature = "tracing")]
34 tracing::debug!("commit");
35
36 self.inner.values = mem::take(&mut self.values);
37
38 if self.batch.is_empty() {
39 #[cfg(feature = "tracing")]
40 tracing::trace!(
41 target: "eyeball_im::vector::broadcast",
42 "Skipping broadcast of empty list of diffs"
43 );
44 } else {
45 let diffs = OneOrManyDiffs::Many(mem::take(&mut self.batch));
46 let msg = BroadcastMessage { diffs, state: self.inner.values.clone() };
47 let _num_receivers = self.inner.sender.send(msg).unwrap_or(0);
48 #[cfg(feature = "tracing")]
49 tracing::debug!(
50 target: "eyeball_im::vector::broadcast",
51 "New observable value broadcast to {_num_receivers} receivers"
52 );
53 }
54 }
55
56 pub fn rollback(&mut self) {
60 #[cfg(feature = "tracing")]
61 tracing::debug!("rollback (explicit)");
62
63 self.values = self.inner.values.clone();
64 self.batch.clear();
65 }
66
67 pub fn append(&mut self, values: Vector<T>) {
70 #[cfg(feature = "tracing")]
71 tracing::debug!(
72 target: "eyeball_im::vector::transaction::update",
73 "append(len = {})", values.len()
74 );
75
76 self.values.append(values.clone());
77 self.add_to_batch(VectorDiff::Append { values });
78 }
79
80 pub fn clear(&mut self) {
82 #[cfg(feature = "tracing")]
83 tracing::debug!(target: "eyeball_im::vector::transaction::update", "clear");
84
85 self.values.clear();
86 self.batch.clear(); self.add_to_batch(VectorDiff::Clear);
88 }
89
90 pub fn push_front(&mut self, value: T) {
92 #[cfg(feature = "tracing")]
93 tracing::debug!(target: "eyeball_im::vector::transaction::update", "push_front");
94
95 self.values.push_front(value.clone());
96 self.add_to_batch(VectorDiff::PushFront { value });
97 }
98
99 pub fn push_back(&mut self, value: T) {
101 #[cfg(feature = "tracing")]
102 tracing::debug!(target: "eyeball_im::vector::transaction::update", "push_back");
103
104 self.values.push_back(value.clone());
105 self.add_to_batch(VectorDiff::PushBack { value });
106 }
107
108 pub fn pop_front(&mut self) -> Option<T> {
113 let value = self.values.pop_front();
114 if value.is_some() {
115 #[cfg(feature = "tracing")]
116 tracing::debug!(target: "eyeball_im::vector::transaction::update", "pop_front");
117
118 self.add_to_batch(VectorDiff::PopFront);
119 }
120 value
121 }
122
123 pub fn pop_back(&mut self) -> Option<T> {
128 let value = self.values.pop_back();
129 if value.is_some() {
130 #[cfg(feature = "tracing")]
131 tracing::debug!(target: "eyeball_im::vector::transaction::update", "pop_back");
132
133 self.add_to_batch(VectorDiff::PopBack);
134 }
135 value
136 }
137
138 #[track_caller]
144 pub fn insert(&mut self, index: usize, value: T) {
145 let len = self.values.len();
146 if index <= len {
147 #[cfg(feature = "tracing")]
148 tracing::debug!(
149 target: "eyeball_im::vector::transaction::update",
150 "insert(index = {index})"
151 );
152
153 self.values.insert(index, value.clone());
154 self.add_to_batch(VectorDiff::Insert { index, value });
155 } else {
156 panic!("index out of bounds: the length is {len} but the index is {index}");
157 }
158 }
159
160 #[track_caller]
167 pub fn set(&mut self, index: usize, value: T) -> T {
168 let len = self.values.len();
169 if index < len {
170 #[cfg(feature = "tracing")]
171 tracing::debug!(
172 target: "eyeball_im::vector::transaction::update",
173 "set(index = {index})"
174 );
175
176 let old_value = self.values.set(index, value.clone());
177 self.add_to_batch(VectorDiff::Set { index, value });
178 old_value
179 } else {
180 panic!("index out of bounds: the length is {len} but the index is {index}");
181 }
182 }
183
184 #[track_caller]
191 pub fn remove(&mut self, index: usize) -> T {
192 let len = self.values.len();
193 if index < len {
194 #[cfg(feature = "tracing")]
195 tracing::debug!(
196 target: "eyeball_im::vector::transaction::update",
197 "remove(index = {index})"
198 );
199
200 let value = self.values.remove(index);
201 self.add_to_batch(VectorDiff::Remove { index });
202 value
203 } else {
204 panic!("index out of bounds: the length is {len} but the index is {index}");
205 }
206 }
207
208 pub fn truncate(&mut self, len: usize) {
213 if len < self.len() {
214 #[cfg(feature = "tracing")]
215 tracing::debug!(target: "eyeball_im::vector::update", "truncate(len = {len})");
216
217 self.values.truncate(len);
218 self.add_to_batch(VectorDiff::Truncate { length: len });
219 }
220 }
221
222 #[track_caller]
229 pub fn entry(&mut self, index: usize) -> ObservableVectorTransactionEntry<'_, 'o, T> {
230 let len = self.values.len();
231 if index < len {
232 ObservableVectorTransactionEntry::new(self, index)
233 } else {
234 panic!("index out of bounds: the length is {len} but the index is {index}");
235 }
236 }
237
238 pub fn for_each(&mut self, mut f: impl FnMut(ObservableVectorTransactionEntry<'_, 'o, T>)) {
243 let mut entries = self.entries();
244 while let Some(entry) = entries.next() {
245 f(entry);
246 }
247 }
248
249 pub fn entries(&mut self) -> ObservableVectorTransactionEntries<'_, 'o, T> {
270 ObservableVectorTransactionEntries::new(self)
271 }
272
273 fn add_to_batch(&mut self, diff: VectorDiff<T>) {
274 if self.inner.sender.receiver_count() != 0 {
275 self.batch.push(diff);
276 }
277 }
278}
279
280impl<T> fmt::Debug for ObservableVectorTransaction<'_, T>
281where
282 T: Clone + fmt::Debug,
283{
284 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
285 f.debug_struct("ObservableVectorWriteGuard")
286 .field("values", &self.values)
287 .finish_non_exhaustive()
288 }
289}
290
291impl<T: Clone> ops::Deref for ObservableVectorTransaction<'_, T> {
294 type Target = Vector<T>;
295
296 fn deref(&self) -> &Self::Target {
297 &self.values
298 }
299}
300
301impl<T: Clone> Drop for ObservableVectorTransaction<'_, T> {
302 fn drop(&mut self) {
303 #[cfg(feature = "tracing")]
304 if !self.batch.is_empty() {
305 tracing::debug!("rollback (drop)");
306 }
307 }
308}
309
310pub struct ObservableVectorTransactionEntry<'a, 'o, T: Clone> {
313 inner: &'a mut ObservableVectorTransaction<'o, T>,
314 index: EntryIndex<'a>,
315}
316
317impl<'a, 'o, T> ObservableVectorTransactionEntry<'a, 'o, T>
318where
319 T: Clone + 'static,
320{
321 pub(super) fn new(inner: &'a mut ObservableVectorTransaction<'o, T>, index: usize) -> Self {
322 Self { inner, index: EntryIndex::Owned(index) }
323 }
324
325 fn new_borrowed(
326 inner: &'a mut ObservableVectorTransaction<'o, T>,
327 index: &'a mut usize,
328 ) -> Self {
329 Self { inner, index: EntryIndex::Borrowed(index) }
330 }
331
332 pub fn index(this: &Self) -> usize {
334 this.index.value()
335 }
336
337 pub fn set(this: &mut Self, value: T) -> T {
340 this.inner.set(this.index.value(), value)
341 }
342
343 pub fn remove(mut this: Self) -> T {
345 this.inner.remove(this.index.make_owned())
346 }
347}
348
349impl<T> fmt::Debug for ObservableVectorTransactionEntry<'_, '_, T>
350where
351 T: Clone + fmt::Debug,
352{
353 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
354 let index = self.index.value();
355 f.debug_struct("ObservableVectorEntry")
356 .field("item", &self.inner[index])
357 .field("index", &index)
358 .finish()
359 }
360}
361
362impl<T: Clone> ops::Deref for ObservableVectorTransactionEntry<'_, '_, T> {
363 type Target = T;
364
365 fn deref(&self) -> &Self::Target {
366 &self.inner[self.index.value()]
367 }
368}
369
370impl<T: Clone> Drop for ObservableVectorTransactionEntry<'_, '_, T> {
371 fn drop(&mut self) {
372 if let EntryIndex::Borrowed(idx) = &mut self.index {
383 **idx += 1;
384 }
385 }
386}
387
388#[derive(Debug)]
393pub struct ObservableVectorTransactionEntries<'a, 'o, T: Clone> {
394 inner: &'a mut ObservableVectorTransaction<'o, T>,
395 index: usize,
396}
397
398impl<'a, 'o, T> ObservableVectorTransactionEntries<'a, 'o, T>
399where
400 T: Clone + 'static,
401{
402 pub(super) fn new(inner: &'a mut ObservableVectorTransaction<'o, T>) -> Self {
403 Self { inner, index: 0 }
404 }
405
406 #[allow(clippy::should_implement_trait)]
409 pub fn next(&mut self) -> Option<ObservableVectorTransactionEntry<'_, 'o, T>> {
410 if self.index < self.inner.len() {
411 Some(ObservableVectorTransactionEntry::new_borrowed(self.inner, &mut self.index))
412 } else {
413 None
414 }
415 }
416}