1use std::sync::Arc;
4
5use crate::history::History;
6use crate::revision::Revision;
7use crate::snapshot::SnapshotCell;
8
9pub struct Store<D, X = ()> {
21 doc: Arc<D>,
22 extra: X,
23 revision: Revision,
24 saved: Revision,
25 history: History<D, X>,
26 cell: Option<Arc<SnapshotCell<D>>>,
27}
28
29impl<D: std::fmt::Debug, X: std::fmt::Debug> std::fmt::Debug for Store<D, X> {
30 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31 f.debug_struct("Store")
32 .field("doc", &self.doc)
33 .field("extra", &self.extra)
34 .field("revision", &self.revision)
35 .field("saved", &self.saved)
36 .field("history", &self.history)
37 .finish()
38 }
39}
40
41impl<D: Clone, X: Clone + Default> Store<D, X> {
42 pub fn new(doc: D) -> Store<D, X> {
44 Store::with_extra(doc, X::default())
45 }
46}
47
48impl<D: Clone, X: Clone> Store<D, X> {
49 pub fn with_extra(doc: D, extra: X) -> Store<D, X> {
51 let revision = Revision::next();
52 Store { doc: Arc::new(doc), extra, revision, saved: revision, history: History::new(), cell: None }
53 }
54
55 pub fn doc(&self) -> &Arc<D> {
59 &self.doc
60 }
61
62 pub fn snapshot(&self) -> Arc<D> {
64 Arc::clone(&self.doc)
65 }
66
67 pub fn revision(&self) -> Revision {
68 self.revision
69 }
70
71 pub fn extra(&self) -> &X {
72 &self.extra
73 }
74
75 pub fn extra_mut(&mut self) -> &mut X {
77 &mut self.extra
78 }
79
80 pub fn history(&self) -> &History<D, X> {
81 &self.history
82 }
83
84 pub fn history_mut(&mut self) -> &mut History<D, X> {
86 &mut self.history
87 }
88
89 pub fn edit<R>(&mut self, label: &str, f: impl FnOnce(&mut D) -> R) -> R {
93 match self.try_edit_with::<R, std::convert::Infallible>(label, None, |d, _| Ok(f(d))) {
94 Ok(r) => r,
95 Err(never) => match never {},
96 }
97 }
98
99 pub fn try_edit<R, E>(&mut self, label: &str, f: impl FnOnce(&mut D) -> Result<R, E>) -> Result<R, E> {
101 self.try_edit_with(label, None, |d, _| f(d))
102 }
103
104 pub fn edit_merged<R>(&mut self, label: &str, merge_key: &str, f: impl FnOnce(&mut D) -> R) -> R {
107 match self.try_edit_with::<R, std::convert::Infallible>(label, Some(merge_key), |d, _| Ok(f(d))) {
108 Ok(r) => r,
109 Err(never) => match never {},
110 }
111 }
112
113 pub fn try_edit_with<R, E>(&mut self, label: &str, merge_key: Option<&str>, f: impl FnOnce(&mut D, &mut X) -> Result<R, E>) -> Result<R, E> {
116 let mut doc = Arc::clone(&self.doc);
117 let mut extra = self.extra.clone();
118 let r = f(Arc::make_mut(&mut doc), &mut extra)?;
120 let merge = merge_key.is_some_and(|k| self.history.can_merge(label, k));
121 let old_doc = std::mem::replace(&mut self.doc, doc);
122 let old_extra = std::mem::replace(&mut self.extra, extra);
123 if !merge {
124 self.history.record(label, old_doc, old_extra, self.revision);
125 self.history.set_merge_key(merge_key);
126 }
127 self.revision = Revision::next();
128 self.publish();
129 Ok(r)
130 }
131
132 pub fn commit(&mut self, label: &str, merge_key: Option<&str>, doc: Arc<D>) {
136 if Arc::ptr_eq(&doc, &self.doc) {
137 return;
138 }
139 let merge = merge_key.is_some_and(|k| self.history.can_merge(label, k));
140 let old_doc = std::mem::replace(&mut self.doc, doc);
141 if !merge {
142 self.history.record(label, old_doc, self.extra.clone(), self.revision);
143 self.history.set_merge_key(merge_key);
144 }
145 self.revision = Revision::next();
146 self.publish();
147 }
148
149 pub fn transaction<T, E>(&mut self, label: &str, f: impl FnOnce(&mut Store<D, X>) -> Result<T, E>) -> Result<T, E> {
156 let mut tx = self.begin_transaction(label);
157 let r = f(&mut tx)?;
158 tx.commit();
159 Ok(r)
160 }
161
162 pub fn begin_transaction(&mut self, label: &str) -> Transaction<'_, D, X> {
165 let scope = self.begin_scope(label);
166 Transaction { store: self, scope: Some(scope) }
167 }
168
169 pub fn transaction_on<H, T, E>(
188 host: &mut H,
189 store: impl Fn(&mut H) -> &mut Store<D, X>,
190 label: &str,
191 f: impl FnOnce(&mut H) -> Result<T, E>,
192 ) -> Result<T, E> {
193 let scope = store(host).begin_scope(label);
194 let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| f(&mut *host)));
195 let s = store(host);
196 match r {
197 Ok(Ok(v)) => {
198 s.commit_scope(scope);
199 Ok(v)
200 }
201 Ok(Err(e)) => {
202 s.rollback_scope(scope);
203 Err(e)
204 }
205 Err(panic) => {
206 s.rollback_scope(scope);
207 std::panic::resume_unwind(panic)
208 }
209 }
210 }
211
212 fn begin_scope(&mut self, label: &str) -> Scope<D, X> {
214 let mut scratch = History::new();
215 scratch.set_max_steps(usize::MAX);
216 let history = std::mem::replace(&mut self.history, scratch);
217 Scope { label: label.to_string(), doc: Arc::clone(&self.doc), extra: self.extra.clone(), revision: self.revision, saved: self.saved, history }
218 }
219
220 fn commit_scope(&mut self, b: Scope<D, X>) {
222 self.history = b.history;
223 if self.revision != b.revision {
224 self.history.record(&b.label, b.doc, b.extra, b.revision);
225 }
226 }
227
228 fn rollback_scope(&mut self, b: Scope<D, X>) {
230 let changed = self.revision != b.revision;
231 self.history = b.history;
232 self.saved = b.saved;
233 self.doc = b.doc;
234 self.extra = b.extra;
235 self.revision = b.revision;
236 if changed {
237 self.publish();
238 }
239 }
240
241 pub fn undo(&mut self) -> Option<String> {
243 let e = self.history.undo(Arc::clone(&self.doc), self.extra.clone(), self.revision)?;
244 self.restore(e.doc, e.extra, e.revision);
245 Some(e.label)
246 }
247
248 pub fn redo(&mut self) -> Option<String> {
250 let e = self.history.redo(Arc::clone(&self.doc), self.extra.clone(), self.revision)?;
251 self.restore(e.doc, e.extra, e.revision);
252 Some(e.label)
253 }
254
255 pub fn redo_branch(&mut self, index: usize) -> Option<String> {
257 if !self.history.switch_branch(index, self.revision) {
258 return None;
259 }
260 self.redo()
261 }
262
263 pub fn can_undo(&self) -> bool {
264 self.history.can_undo()
265 }
266
267 pub fn can_redo(&self) -> bool {
268 self.history.can_redo()
269 }
270
271 pub fn replace(&mut self, doc: D, extra: X) {
273 self.doc = Arc::new(doc);
274 self.extra = extra;
275 self.history.clear();
276 self.revision = Revision::next();
277 self.saved = self.revision;
278 self.publish();
279 }
280
281 fn restore(&mut self, doc: Arc<D>, extra: X, revision: Revision) {
282 self.doc = doc;
283 self.extra = extra;
284 self.revision = revision;
285 self.publish();
286 }
287
288 pub fn is_dirty(&self) -> bool {
292 self.revision != self.saved
293 }
294
295 pub fn mark_saved(&mut self) {
297 self.saved = self.revision;
298 }
299
300 pub fn mark_saved_at(&mut self, revision: Revision) {
302 self.saved = revision;
303 }
304
305 pub fn saved_revision(&self) -> Revision {
306 self.saved
307 }
308
309 pub fn subscribe_snapshots(&mut self) -> Arc<SnapshotCell<D>> {
314 let (doc, rev) = (Arc::clone(&self.doc), self.revision);
315 Arc::clone(self.cell.get_or_insert_with(|| Arc::new(SnapshotCell::new(doc, rev))))
316 }
317
318 fn publish(&self) {
319 if let Some(c) = &self.cell {
320 c.publish(Arc::clone(&self.doc), self.revision);
321 }
322 }
323}
324
325struct Scope<D, X> {
327 label: String,
328 doc: Arc<D>,
329 extra: X,
330 revision: Revision,
331 saved: Revision,
332 history: History<D, X>,
333}
334
335pub struct Transaction<'a, D: Clone, X: Clone = ()> {
347 store: &'a mut Store<D, X>,
348 scope: Option<Scope<D, X>>,
349}
350
351impl<D: Clone, X: Clone> Transaction<'_, D, X> {
352 pub fn commit(mut self) {
354 if let Some(b) = self.scope.take() {
355 self.store.commit_scope(b);
356 }
357 }
358
359 pub fn rollback(self) {}
361
362 pub fn changed(&self) -> bool {
364 self.scope.as_ref().is_some_and(|b| b.revision != self.store.revision)
365 }
366}
367
368impl<D: Clone, X: Clone> std::ops::Deref for Transaction<'_, D, X> {
369 type Target = Store<D, X>;
370 fn deref(&self) -> &Store<D, X> {
371 self.store
372 }
373}
374
375impl<D: Clone, X: Clone> std::ops::DerefMut for Transaction<'_, D, X> {
376 fn deref_mut(&mut self) -> &mut Store<D, X> {
377 self.store
378 }
379}
380
381impl<D: Clone, X: Clone> Drop for Transaction<'_, D, X> {
382 fn drop(&mut self) {
383 if let Some(b) = self.scope.take() {
384 self.store.rollback_scope(b);
385 }
386 }
387}
388
389impl<D: Clone + std::fmt::Debug, X: Clone + std::fmt::Debug> std::fmt::Debug for Transaction<'_, D, X> {
390 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
391 f.debug_struct("Transaction").field("label", &self.scope.as_ref().map(|b| b.label.as_str())).field("store", &self.store).finish()
392 }
393}
394
395#[cfg(test)]
396mod tests {
397 use super::*;
398
399 #[derive(Clone, Debug, Default, PartialEq)]
400 struct Doc {
401 items: Vec<i32>,
402 }
403
404 fn store() -> Store<Doc> {
405 Store::new(Doc::default())
406 }
407
408 #[test]
409 fn edit_is_copy_on_write_and_bumps_revision() {
410 let mut s = store();
411 let before = s.snapshot();
412 let r0 = s.revision();
413 assert!(!s.is_dirty());
414 let n = s.edit("Add", |d| {
415 d.items.push(1);
416 d.items.len()
417 });
418 assert_eq!(n, 1);
419 assert!(before.items.is_empty(), "old snapshot untouched");
420 assert_ne!(s.revision(), r0);
421 assert!(s.is_dirty());
422 assert_eq!(s.history().undo_label(), Some("Add"));
423 }
424
425 #[test]
426 fn failed_edit_changes_nothing() {
427 let mut s = store();
428 s.edit("a", |d| d.items.push(1));
429 let rev = s.revision();
430 let r: Result<(), &str> = s.try_edit("b", |d| {
431 d.items.push(2);
432 Err("no")
433 });
434 assert_eq!(r, Err("no"));
435 assert_eq!(s.doc().items, [1]);
436 assert_eq!(s.revision(), rev);
437 assert_eq!(s.history().depth(), 1);
438 }
439
440 #[test]
441 fn panicking_edit_changes_nothing() {
442 let mut s = store();
443 s.edit("a", |d| d.items.push(1));
444 let rev = s.revision();
445 let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
446 s.edit("boom", |d| {
447 d.items.push(2);
448 panic!("bug in a command")
449 })
450 }));
451 assert!(r.is_err());
452 assert_eq!(s.doc().items, [1]);
453 assert_eq!(s.revision(), rev);
454 assert_eq!(s.history().undo_labels().collect::<Vec<_>>(), ["a"]);
455 }
456
457 #[test]
458 fn undo_restores_revision_and_dirty_state() {
459 let mut s = store();
460 let r0 = s.revision();
461 s.edit("a", |d| d.items.push(1));
462 let r1 = s.revision();
463 s.mark_saved();
464 s.edit("b", |d| d.items.push(2));
465 assert!(s.is_dirty());
466 assert_eq!(s.undo().as_deref(), Some("b"));
467 assert_eq!(s.revision(), r1);
468 assert!(!s.is_dirty(), "back at the saved state");
469 s.undo();
470 assert_eq!(s.revision(), r0);
471 assert!(s.is_dirty());
472 s.redo();
473 assert!(!s.is_dirty());
474 assert_eq!(s.redo().as_deref(), Some("b"));
475 assert_eq!(s.doc().items, [1, 2]);
476 assert_eq!(s.undo(), Some("b".into()));
477 assert_eq!(s.undo(), Some("a".into()));
478 assert_eq!(s.undo(), None);
479 }
480
481 #[test]
482 fn merged_edits_are_one_step_with_fresh_revisions() {
483 let mut s = store();
484 s.edit_merged("Move", "drag", |d| d.items.push(1));
485 let r1 = s.revision();
486 s.mark_saved();
487 s.edit_merged("Move", "drag", |d| d.items[0] = 2);
488 assert_ne!(s.revision(), r1, "merged content is new content");
489 assert!(s.is_dirty());
490 s.edit_merged("Move", "drag", |d| d.items[0] = 3);
491 assert_eq!(s.history().depth(), 1);
492 s.undo();
493 assert!(s.doc().items.is_empty());
494 s.redo();
495 assert_eq!(s.doc().items, [3]);
496 s.edit_merged("Move", "drag", |d| d.items[0] = 4);
497 assert_eq!(s.history().depth(), 2, "undo/redo ended the gesture");
498 s.edit("Other", |_| ());
499 s.edit_merged("Move", "drag", |d| d.items[0] = 5);
500 assert_eq!(s.history().depth(), 4);
501 let _: Result<(), ()> = s.try_edit_with("Move", Some("drag"), |_, _| Err(()));
503 s.edit_merged("Move", "drag", |d| d.items[0] = 6);
504 assert_eq!(s.history().depth(), 4);
505 }
506
507 #[test]
508 fn extra_state_travels_with_steps() {
509 let mut s: Store<Doc, Vec<usize>> = Store::new(Doc::default());
510 s.extra_mut().push(0); s.try_edit_with::<_, ()>("Add", None, |d, sel| {
512 d.items.push(7);
513 *sel = vec![0];
514 Ok(())
515 })
516 .unwrap();
517 s.extra_mut().clear(); s.undo();
519 assert_eq!(s.extra(), &vec![0], "selection before the step");
520 s.redo();
521 assert!(s.extra().is_empty(), "selection when undo was pressed");
522 }
523
524 #[test]
525 fn replace_clears_history_and_is_clean() {
526 let mut s = store();
527 s.edit("a", |d| d.items.push(1));
528 s.replace(Doc { items: vec![9] }, ());
529 assert!(!s.can_undo() && !s.can_redo() && !s.is_dirty());
530 assert_eq!(s.doc().items, [9]);
531 }
532
533 #[test]
534 fn snapshots_are_published() {
535 let mut s = store();
536 let cell = s.subscribe_snapshots();
537 assert!(Arc::ptr_eq(&cell, &s.subscribe_snapshots()));
538 s.edit("a", |d| d.items.push(1));
539 assert_eq!(cell.load().doc.items, [1]);
540 assert_eq!(cell.revision(), s.revision());
541 s.undo();
542 assert!(cell.load().doc.items.is_empty());
543 s.redo();
544 s.replace(Doc::default(), ());
545 assert_eq!(cell.revision(), s.revision());
546 }
547
548 #[test]
549 fn branches_through_the_store() {
550 let mut s = store();
551 s.history_mut().set_branching(true);
552 s.edit("a", |d| d.items.push(1));
553 s.undo();
554 s.edit("b", |d| d.items.push(2));
555 s.undo();
556 assert_eq!(s.history().branches().len(), 1);
557 assert_eq!(s.redo_branch(0).as_deref(), Some("a"));
558 assert_eq!(s.doc().items, [1]);
559 assert_eq!(s.redo_branch(0), None, "not at the fork any more");
560 }
561
562 #[test]
563 fn mark_saved_at_an_older_revision() {
564 let mut s = store();
565 s.edit("a", |d| d.items.push(1));
566 let saving = s.revision();
567 s.edit("b", |d| d.items.push(2));
568 s.mark_saved_at(saving);
569 assert!(s.is_dirty());
570 s.undo();
571 assert!(!s.is_dirty());
572 assert_eq!(s.saved_revision(), saving);
573 }
574
575 #[test]
576 fn commit_is_one_step_without_copying() {
577 let mut s = store();
578 let mut work = s.snapshot();
579 Arc::make_mut(&mut work).items.push(1);
580 Arc::make_mut(&mut work).items.push(2);
581 let built = Arc::clone(&work);
582 s.commit("Add Two", None, work);
583 assert!(Arc::ptr_eq(s.doc(), &built), "the committed Arc is kept as is");
584 assert_eq!(s.history().depth(), 1);
585 assert!(s.is_dirty());
586 let same = s.snapshot();
587 let r = s.revision();
588 s.commit("Nothing", None, same);
589 assert_eq!((s.revision(), s.history().depth()), (r, 1), "committing the current state is a no-op");
590 assert_eq!(s.undo().as_deref(), Some("Add Two"));
591 assert!(s.doc().items.is_empty());
592 assert!(!s.is_dirty());
593 }
594
595 fn labels(s: &Store<Doc>) -> (Vec<&str>, Vec<&str>) {
596 (s.history().undo_labels().collect(), s.history().redo_labels().collect())
597 }
598
599 #[test]
600 fn transaction_folds_edits_into_one_step() {
601 let mut s = store();
602 s.edit("a", |d| d.items.push(1));
603 s.edit("b", |d| d.items.push(2));
604 s.undo(); let r: Result<usize, ()> = s.transaction("Assemble", |s| {
606 for i in 10..15 {
607 s.edit("part", |d| d.items.push(i));
608 }
609 s.edit_merged("drag", "k", |d| d.items.push(20));
610 s.edit_merged("drag", "k", |d| d.items.push(21));
611 assert_eq!(s.history().depth(), 6, "the scratch history holds only this transaction's steps");
612 Ok(s.doc().items.len())
613 });
614 assert_eq!(r, Ok(8));
615 assert_eq!(labels(&s), (vec!["a", "Assemble"], vec![]), "one step, redo dropped");
616 assert_eq!(s.history().merge_key(), None);
617 assert_eq!(s.undo().as_deref(), Some("Assemble"));
618 assert_eq!(s.doc().items, [1]);
619 assert_eq!(s.redo().as_deref(), Some("Assemble"));
620 assert_eq!(s.doc().items, [1, 10, 11, 12, 13, 14, 20, 21]);
621 }
622
623 #[test]
624 fn failed_transaction_puts_everything_back() {
625 let mut s: Store<Doc, u8> = Store::new(Doc::default());
626 s.edit("a", |d| d.items.push(1));
627 s.mark_saved();
628 s.edit_merged("Move", "drag", |d| d.items.push(2));
629 s.undo(); s.edit_merged("Move", "drag", |d| d.items.push(3)); s.edit("b", |d| d.items.push(4));
632 s.undo(); s.mark_saved();
634 s.history_mut().set_merge_key(Some("drag"));
635 *s.extra_mut() = 7;
636 let cell = s.subscribe_snapshots();
637 let (doc, rev, hist) = (s.snapshot(), s.revision(), format!("{:?}", s.history()));
638 let r: Result<(), &str> = s.transaction("T", |s| {
639 s.edit("x", |d| d.items.clear());
640 s.mark_saved();
641 *s.extra_mut() = 9;
642 s.undo();
643 s.edit("y", |d| d.items.push(5));
644 Err("no")
645 });
646 assert_eq!(r, Err("no"));
647 assert!(Arc::ptr_eq(s.doc(), &doc));
648 assert_eq!((s.revision(), *s.extra()), (rev, 7));
649 assert!(!s.is_dirty(), "clean before, clean after");
650 assert_eq!(format!("{:?}", s.history()), hist, "history, redo line and merge key as before");
651 assert!(s.history().can_merge("Move", "drag"));
652 assert_eq!(cell.revision(), rev, "the rollback was published");
653 assert_eq!(s.redo().as_deref(), Some("b"));
654
655 s.edit("c", |d| d.items.push(6));
657 let rev = s.revision();
658 let _: Result<(), ()> = s.transaction("T", |s| {
659 s.mark_saved();
660 Err(())
661 });
662 assert!(s.is_dirty());
663 assert_eq!(s.revision(), rev);
664 }
665
666 #[test]
667 fn panicking_transaction_puts_everything_back() {
668 let mut s = store();
669 s.edit("a", |d| d.items.push(1));
670 s.edit("b", |d| d.items.push(2));
671 s.undo();
672 let (rev, hist) = (s.revision(), format!("{:?}", s.history()));
673 let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
674 let _: Result<(), ()> = s.transaction("T", |s| {
675 s.edit("x", |d| d.items.push(9));
676 s.edit("y", |d| d.items.push(9));
677 panic!("bug in a command")
678 });
679 }));
680 assert!(r.is_err());
681 assert_eq!((s.doc().items.as_slice(), s.revision()), ([1].as_slice(), rev));
682 assert_eq!(format!("{:?}", s.history()), hist);
683 assert!(s.is_dirty());
684 }
685
686 #[test]
687 fn guard_commits_or_rolls_back_on_drop() {
688 let mut s = store();
689 {
690 let mut tx = s.begin_transaction("Keep");
691 tx.edit("x", |d| d.items.push(1));
692 tx.edit("y", |d| d.items.push(2));
693 assert!(tx.changed());
694 tx.commit();
695 }
696 assert_eq!(labels(&s).0, ["Keep"]);
697 let rev = s.revision();
698 {
699 let mut tx = s.begin_transaction("Drop");
700 tx.edit("x", |d| d.items.clear());
701 }
702 assert_eq!((s.doc().items.as_slice(), s.revision()), ([1, 2].as_slice(), rev));
703 let mut tx = s.begin_transaction("Explicit");
704 tx.edit("x", |d| d.items.clear());
705 tx.rollback();
706 assert_eq!(s.doc().items, [1, 2]);
707 assert_eq!(labels(&s).0, ["Keep"]);
708 }
709
710 #[test]
711 fn empty_transaction_records_nothing() {
712 let mut s = store();
713 s.edit_merged("Move", "drag", |d| d.items.push(1));
714 s.edit("b", |d| d.items.push(2));
715 s.undo();
716 s.history_mut().set_merge_key(Some("drag"));
717 let (rev, hist, dirty) = (s.revision(), format!("{:?}", s.history()), s.is_dirty());
718 assert_eq!(s.transaction::<_, ()>("Nothing", |_| Ok(3)), Ok(3));
719 let _: Result<(), ()> = s.transaction("Failed edit", |s| {
720 let _ = s.try_edit("x", |_| Err::<(), ()>(()));
721 Ok(())
722 });
723 let _: Result<(), ()> = s.transaction("Edit and undo", |s| {
725 s.edit("x", |d| d.items.push(9));
726 s.undo();
727 Ok(())
728 });
729 assert_eq!(s.revision(), rev);
730 assert_eq!(format!("{:?}", s.history()), hist, "redo line and gesture kept");
731 assert!(s.history().can_merge("Move", "drag"));
732 assert_eq!(s.is_dirty(), dirty);
733 }
734
735 #[test]
736 fn nested_transactions_join_the_outer_one() {
737 let mut s = store();
738 s.edit("a", |d| d.items.push(1));
739 let r: Result<(), ()> = s.transaction("Outer", |s| {
740 s.edit("x", |d| d.items.push(2));
741 let inner: Result<(), ()> = s.transaction("Inner ok", |s| {
742 s.edit("y", |d| d.items.push(3));
743 s.edit("z", |d| d.items.push(4));
744 Ok(())
745 });
746 assert_eq!(inner, Ok(()));
747 assert_eq!(s.history().undo_labels().collect::<Vec<_>>(), ["x", "Inner ok"]);
748 let failed: Result<(), ()> = s.transaction("Inner failed", |s| {
749 s.edit("w", |d| d.items.clear());
750 Err(())
751 });
752 assert_eq!(failed, Err(()));
753 assert_eq!(s.doc().items, [1, 2, 3, 4], "only the inner edits were rolled back");
754 Ok(())
755 });
756 assert_eq!(r, Ok(()));
757 assert_eq!(labels(&s).0, ["a", "Outer"]);
758 s.undo();
759 assert_eq!(s.doc().items, [1]);
760
761 let rev = s.revision();
763 let _: Result<(), ()> = s.transaction("Outer", |s| {
764 let _: Result<(), ()> = s.transaction("Inner", |s| {
765 s.edit("y", |d| d.items.push(3));
766 Ok(())
767 });
768 Err(())
769 });
770 assert_eq!((s.doc().items.as_slice(), s.revision()), ([1].as_slice(), rev));
771 assert_eq!(labels(&s), (vec!["a"], vec!["Outer"]));
772 }
773
774 #[test]
775 fn transaction_and_merge_keys() {
776 let mut s = store();
777 s.edit_merged("Move", "drag", |d| d.items.push(1));
778 let _: Result<(), ()> = s.transaction("Move", |s| {
779 s.edit_merged("Move", "drag", |d| d.items.push(2));
781 s.edit_merged("Move", "drag", |d| d.items.push(3));
782 assert_eq!(s.history().depth(), 1);
783 Ok(())
784 });
785 assert_eq!(labels(&s).0, ["Move", "Move"]);
786 assert_eq!(s.history().merge_key(), None, "a committed transaction ends the gesture");
787 s.edit_merged("Move", "drag", |d| d.items.push(4));
788 assert_eq!(s.history().depth(), 3);
789 }
790
791 #[test]
792 fn transaction_respects_limits_once() {
793 let mut s = store();
794 s.history_mut().set_max_steps(3);
795 for i in 0..3 {
796 s.edit(&format!("e{i}"), |d| d.items.push(i));
797 }
798 let _: Result<(), ()> = s.transaction("Many", |s| {
799 for i in 0..10 {
800 s.edit("part", |d| d.items.push(i));
801 }
802 Ok(())
803 });
804 assert_eq!(labels(&s).0, ["e1", "e2", "Many"], "only the folded step counted");
805 let _: Result<(), ()> = s.transaction("Fails", |s| {
806 for i in 0..10 {
807 s.edit("part", |d| d.items.push(i));
808 }
809 Err(())
810 });
811 assert_eq!(labels(&s).0, ["e1", "e2", "Many"], "no older step pushed out by a rollback");
812 assert_eq!(s.history().max_steps(), 3);
813 s.history_mut().set_max_steps(0);
814 let _: Result<(), ()> = s.transaction("NoUndo", |s| {
815 s.edit("x", |d| d.items.push(1));
816 Ok(())
817 });
818 assert!(!s.can_undo());
819 assert!(s.is_dirty());
820 }
821
822 struct Host {
824 store: Store<Doc, u8>,
825 log: Vec<&'static str>,
826 }
827
828 impl Host {
829 fn new() -> Host {
830 Host { store: Store::new(Doc::default()), log: Vec::new() }
831 }
832 fn push(&mut self, label: &'static str, v: i32) {
834 self.log.push(label);
835 self.store.edit(label, |d| d.items.push(v));
836 }
837 fn tx<T, E>(&mut self, label: &str, f: impl FnOnce(&mut Host) -> Result<T, E>) -> Result<T, E> {
838 Store::transaction_on(self, |h| &mut h.store, label, f)
839 }
840 }
841
842 fn host_labels(h: &Host) -> (Vec<&str>, Vec<&str>) {
843 (h.store.history().undo_labels().collect(), h.store.history().redo_labels().collect())
844 }
845
846 #[test]
847 fn host_transaction_folds_commands_into_one_step() {
848 let mut h = Host::new();
849 h.push("a", 1);
850 h.push("b", 2);
851 h.store.undo(); let r: Result<usize, ()> = h.tx("Assemble", |h| {
853 for i in 10..13 {
854 h.push("part", i);
855 }
856 h.store.edit_merged("drag", "k", |d| d.items.push(20));
857 h.store.edit_merged("drag", "k", |d| d.items.push(21));
858 assert_eq!(h.store.history().depth(), 4, "the scratch history holds only this transaction's steps");
859 Ok(h.store.doc().items.len())
860 });
861 assert_eq!(r, Ok(6));
862 assert_eq!(host_labels(&h), (vec!["a", "Assemble"], vec![]), "one step, redo dropped");
863 assert_eq!(h.store.history().merge_key(), None);
864 assert_eq!(h.log, ["a", "b", "part", "part", "part"]);
865 assert_eq!(h.store.undo().as_deref(), Some("Assemble"));
866 assert_eq!(h.store.doc().items, [1]);
867 assert_eq!(h.store.redo().as_deref(), Some("Assemble"));
868 assert_eq!(h.store.doc().items, [1, 10, 11, 12, 20, 21]);
869 }
870
871 #[test]
872 fn failed_host_transaction_puts_everything_back() {
873 let mut h = Host::new();
874 h.push("a", 1);
875 h.store.edit_merged("Move", "drag", |d| d.items.push(2));
876 h.push("b", 3);
877 h.store.undo(); h.store.mark_saved();
879 h.store.history_mut().set_merge_key(Some("drag"));
880 *h.store.extra_mut() = 7;
881 let cell = h.store.subscribe_snapshots();
882 let (doc, rev, hist) = (h.store.snapshot(), h.store.revision(), format!("{:?}", h.store.history()));
883 let r: Result<(), &str> = h.tx("T", |h| {
884 h.push("x", 9);
885 h.store.mark_saved();
886 *h.store.extra_mut() = 9;
887 h.store.undo();
888 h.push("y", 5);
889 Err("no")
890 });
891 assert_eq!(r, Err("no"));
892 assert!(Arc::ptr_eq(h.store.doc(), &doc));
893 assert_eq!((h.store.revision(), *h.store.extra()), (rev, 7));
894 assert!(!h.store.is_dirty(), "clean before, clean after");
895 assert_eq!(format!("{:?}", h.store.history()), hist, "history, redo line and merge key as before");
896 assert!(h.store.history().can_merge("Move", "drag"));
897 assert_eq!(cell.revision(), rev, "the rollback was published");
898 assert_eq!(h.log, ["a", "b", "x", "y"], "the rest of the host is the host's business");
899 assert_eq!(h.store.redo().as_deref(), Some("b"));
900 }
901
902 #[test]
903 fn panicking_host_transaction_puts_everything_back() {
904 let mut h = Host::new();
905 h.push("a", 1);
906 h.push("b", 2);
907 h.store.undo();
908 let (rev, hist) = (h.store.revision(), format!("{:?}", h.store.history()));
909 let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
910 let _: Result<(), ()> = h.tx("T", |h| {
911 h.push("x", 9);
912 h.push("y", 9);
913 panic!("bug in a command")
914 });
915 }));
916 assert!(r.is_err(), "the panic continues");
917 assert_eq!((h.store.doc().items.as_slice(), h.store.revision()), ([1].as_slice(), rev));
918 assert_eq!(format!("{:?}", h.store.history()), hist);
919 assert!(h.store.is_dirty());
920 }
921
922 #[test]
923 fn empty_host_transaction_records_nothing() {
924 let mut h = Host::new();
925 h.store.edit_merged("Move", "drag", |d| d.items.push(1));
926 h.push("b", 2);
927 h.store.undo();
928 h.store.history_mut().set_merge_key(Some("drag"));
929 let (rev, hist, dirty) = (h.store.revision(), format!("{:?}", h.store.history()), h.store.is_dirty());
930 assert_eq!(h.tx::<_, ()>("Nothing", |_| Ok(3)), Ok(3));
931 let _: Result<(), ()> = h.tx("Edit and undo", |h| {
932 h.push("x", 9);
933 h.store.undo();
934 Ok(())
935 });
936 assert_eq!(h.store.revision(), rev);
937 assert_eq!(format!("{:?}", h.store.history()), hist, "redo line and gesture kept");
938 assert!(h.store.history().can_merge("Move", "drag"));
939 assert_eq!(h.store.is_dirty(), dirty);
940 }
941
942 #[test]
943 fn host_transactions_nest_with_each_other_and_store_transactions() {
944 let mut h = Host::new();
945 h.push("a", 1);
946 let r: Result<(), ()> = h.tx("Outer", |h| {
947 h.push("x", 2);
948 let inner: Result<(), ()> = h.tx("Inner ok", |h| {
949 h.push("y", 3);
950 h.push("z", 4);
951 Ok(())
952 });
953 assert_eq!(inner, Ok(()));
954 let store_inner: Result<(), ()> = h.store.transaction("Store inner", |s| {
955 s.edit("w", |d| d.items.push(5));
956 Ok(())
957 });
958 assert_eq!(store_inner, Ok(()));
959 assert_eq!(h.store.history().undo_labels().collect::<Vec<_>>(), ["x", "Inner ok", "Store inner"]);
960 let failed: Result<(), ()> = h.tx("Inner failed", |h| {
961 h.store.edit("v", |d| d.items.clear());
962 Err(())
963 });
964 assert_eq!(failed, Err(()));
965 assert_eq!(h.store.doc().items, [1, 2, 3, 4, 5], "only the inner edits were rolled back");
966 Ok(())
967 });
968 assert_eq!(r, Ok(()));
969 assert_eq!(host_labels(&h).0, ["a", "Outer"]);
970 h.store.undo();
971 assert_eq!(h.store.doc().items, [1]);
972
973 let rev = h.store.revision();
975 let _: Result<(), ()> = h.tx("Outer", |h| {
976 let _: Result<(), ()> = h.tx("Inner", |h| {
977 h.push("y", 3);
978 Ok(())
979 });
980 Err(())
981 });
982 assert_eq!((h.store.doc().items.as_slice(), h.store.revision()), ([1].as_slice(), rev));
983 assert_eq!(host_labels(&h), (vec!["a"], vec!["Outer"]));
984 }
985
986 #[test]
987 fn host_transaction_respects_limits_once() {
988 let mut h = Host::new();
989 h.store.history_mut().set_max_steps(2);
990 h.push("e0", 0);
991 h.push("e1", 1);
992 let _: Result<(), ()> = h.tx("Many", |h| {
993 for i in 0..10 {
994 h.push("part", i);
995 }
996 Ok(())
997 });
998 assert_eq!(host_labels(&h).0, ["e1", "Many"]);
999 let _: Result<(), ()> = h.tx("Fails", |h| {
1000 for i in 0..10 {
1001 h.push("part", i);
1002 }
1003 Err(())
1004 });
1005 assert_eq!(host_labels(&h).0, ["e1", "Many"], "no older step pushed out by a rollback");
1006 assert_eq!(h.store.history().max_steps(), 2);
1007 }
1008}