1use crate::engine::graph::DependencyGraph;
9use crate::engine::graph::editor::transaction_manager::{
10 TransactionError, TransactionId, TransactionManager,
11};
12use crate::engine::graph::editor::{EditorError, VertexEditor};
13use crate::engine::named_range::{NameScope, NamedDefinition};
14use crate::engine::vertex::VertexId;
15use crate::engine::{ChangeEvent, ChangeLog};
16use formualizer_common::LiteralValue;
17use formualizer_parse::parser::ASTNode;
18
19pub struct TransactionContext<'g> {
21 graph: &'g mut DependencyGraph,
22 change_log: ChangeLog,
23 tx_manager: TransactionManager,
24}
25
26impl<'g> TransactionContext<'g> {
27 pub fn new(graph: &'g mut DependencyGraph) -> Self {
29 Self {
30 graph,
31 change_log: ChangeLog::new(),
32 tx_manager: TransactionManager::new(),
33 }
34 }
35
36 pub fn with_max_size(graph: &'g mut DependencyGraph, max_size: usize) -> Self {
38 Self {
39 graph,
40 change_log: ChangeLog::new(),
41 tx_manager: TransactionManager::with_max_size(max_size),
42 }
43 }
44
45 pub fn begin(&mut self) -> Result<TransactionId, TransactionError> {
53 self.tx_manager.begin(self.change_log.len())
54 }
55
56 pub fn editor(&mut self) -> VertexEditor<'_> {
65 unsafe {
69 let graph_ptr = self.graph as *mut DependencyGraph;
70 let logger_ptr = &mut self.change_log as *mut ChangeLog;
71 VertexEditor::with_logger(&mut *graph_ptr, &mut *logger_ptr)
72 }
73 }
74
75 pub fn commit(&mut self) -> Result<TransactionId, TransactionError> {
83 self.tx_manager.check_size(self.change_log.len())?;
85 self.tx_manager.commit()
86 }
87
88 pub fn rollback(&mut self) -> Result<(), TransactionError> {
94 let (_tx_id, start_index) = self.tx_manager.rollback_info()?;
95
96 let changes = self.change_log.take_from(start_index);
98
99 self.apply_rollback(changes)?;
101
102 Ok(())
103 }
104
105 pub fn savepoint(&mut self, name: &str) -> Result<(), TransactionError> {
113 self.tx_manager
114 .add_savepoint(name.to_string(), self.change_log.len())
115 }
116
117 pub fn rollback_to_savepoint(&mut self, name: &str) -> Result<(), TransactionError> {
127 let savepoint_index = self.tx_manager.get_savepoint(name)?;
128
129 let changes = self.change_log.take_from(savepoint_index);
131
132 self.tx_manager.truncate_savepoints(savepoint_index);
134
135 self.apply_rollback(changes)?;
137
138 Ok(())
139 }
140
141 pub fn is_active(&self) -> bool {
143 self.tx_manager.is_active()
144 }
145
146 pub fn active_id(&self) -> Option<TransactionId> {
148 self.tx_manager.active_id()
149 }
150
151 pub fn change_count(&self) -> usize {
153 self.change_log.len()
154 }
155
156 pub fn change_log(&self) -> &ChangeLog {
158 &self.change_log
159 }
160
161 pub fn clear_change_log(&mut self) {
163 self.change_log.clear();
164 }
165
166 fn apply_rollback(&mut self, changes: Vec<ChangeEvent>) -> Result<(), TransactionError> {
168 self.change_log.set_enabled(false);
170
171 let mut compound_stack = Vec::new();
173
174 for change in changes.into_iter().rev() {
176 match change {
177 ChangeEvent::CompoundEnd { depth } => {
178 compound_stack.push(depth);
180 }
181 ChangeEvent::CompoundStart { depth, .. } => {
182 if compound_stack.last() == Some(&depth) {
184 compound_stack.pop();
185 }
186 }
187 _ => {
188 if let Err(e) = self.apply_inverse(change) {
190 self.change_log.set_enabled(true);
191 return Err(TransactionError::RollbackFailed(e.to_string()));
192 }
193 }
194 }
195 }
196
197 self.change_log.set_enabled(true);
198 Ok(())
199 }
200
201 fn apply_inverse(&mut self, change: ChangeEvent) -> Result<(), EditorError> {
203 match change {
204 ChangeEvent::AddVertex { id, .. } => {
205 let mut editor = VertexEditor::new(self.graph);
206 let _ = editor.remove_vertex(id); Ok(())
208 }
209 ChangeEvent::SetValue { addr, old, .. } => {
210 if let Some(old_value) = old {
211 let mut editor = VertexEditor::new(self.graph);
212 editor.set_cell_value(addr, old_value);
213 } else {
214 let vertex_id = self.graph.get_vertex_id_for_address(&addr).copied();
216 if let Some(id) = vertex_id {
217 let mut editor = VertexEditor::new(self.graph);
218 editor.remove_vertex(id)?;
219 }
220 }
221 Ok(())
222 }
223
224 ChangeEvent::SetFormula { addr, old, .. } => {
225 if let Some(old_formula) = old {
226 let mut editor = VertexEditor::new(self.graph);
227 editor.set_cell_formula(addr, old_formula);
228 } else {
229 let vertex_id = self.graph.get_vertex_id_for_address(&addr).copied();
231 if let Some(id) = vertex_id {
232 let mut editor = VertexEditor::new(self.graph);
233 editor.remove_vertex(id)?;
234 }
235 }
236 Ok(())
237 }
238
239 ChangeEvent::RemoveVertex {
240 id,
241 old_value,
242 old_formula,
243 old_dependencies,
244 coord,
245 sheet_id,
246 kind,
247 ..
248 } => {
249 if let (Some(coord), Some(sheet_id)) = (coord, sheet_id) {
251 let cell_ref = crate::reference::CellRef::new(
253 sheet_id,
254 crate::reference::Coord::new(coord.row(), coord.col(), true, true),
255 );
256 let mut editor = VertexEditor::new(self.graph);
257 if let Some(val) = old_value.clone() {
258 editor.set_cell_value(cell_ref, val);
259 }
260 if let Some(formula) = old_formula {
261 editor.set_cell_formula(cell_ref, formula);
262 }
263 }
265 Ok(())
266 }
267
268 ChangeEvent::VertexMoved { id, old_coord, .. } => {
270 let mut editor = VertexEditor::new(self.graph);
271 editor.move_vertex(id, old_coord)
272 }
273
274 ChangeEvent::FormulaAdjusted { id, old_ast, .. } => {
275 self.update_vertex_formula(id, old_ast)
277 }
278
279 ChangeEvent::NamedRangeAdjusted {
280 name,
281 scope,
282 old_definition,
283 ..
284 } => {
285 self.update_name(&name, scope, old_definition)
287 }
288
289 ChangeEvent::EdgeAdded { from, to } => {
290 self.remove_edge(from, to)
292 }
293
294 ChangeEvent::EdgeRemoved { from, to } => {
295 self.add_edge(from, to)
297 }
298
299 ChangeEvent::DefineName { name, scope, .. } => {
301 self.delete_name(&name, scope)
303 }
304
305 ChangeEvent::UpdateName {
306 name,
307 scope,
308 old_definition,
309 ..
310 } => {
311 self.update_name(&name, scope, old_definition)
313 }
314
315 ChangeEvent::DeleteName {
316 name,
317 scope,
318 old_definition,
319 } => {
320 if let Some(def) = old_definition {
321 self.update_name(&name, scope, def)
322 } else {
323 Ok(())
324 }
325 }
326
327 ChangeEvent::CompoundStart { .. } | ChangeEvent::CompoundEnd { .. } => Ok(()),
329 }
330 }
331
332 fn restore_vertex(
334 &mut self,
335 _id: VertexId,
336 _old_value: Option<LiteralValue>,
337 _old_formula: Option<ASTNode>,
338 _old_dependencies: Vec<VertexId>,
339 ) -> Result<(), EditorError> {
340 Err(EditorError::TransactionFailed {
343 reason: "Vertex restoration not yet implemented".to_string(),
344 })
345 }
346
347 fn update_vertex_formula(&mut self, _id: VertexId, _ast: ASTNode) -> Result<(), EditorError> {
349 Err(EditorError::TransactionFailed {
352 reason: "Direct formula update not yet implemented".to_string(),
353 })
354 }
355
356 fn update_name(
358 &mut self,
359 _name: &str,
360 _scope: NameScope,
361 _definition: NamedDefinition,
362 ) -> Result<(), EditorError> {
363 Ok(())
366 }
367
368 fn delete_name(&mut self, _name: &str, _scope: NameScope) -> Result<(), EditorError> {
370 Ok(())
373 }
374
375 fn remove_edge(&mut self, _from: VertexId, _to: VertexId) -> Result<(), EditorError> {
377 Err(EditorError::TransactionFailed {
380 reason: "Edge removal not supported in rollback".to_string(),
381 })
382 }
383
384 fn add_edge(&mut self, _from: VertexId, _to: VertexId) -> Result<(), EditorError> {
386 Err(EditorError::TransactionFailed {
389 reason: "Edge addition not supported in rollback".to_string(),
390 })
391 }
392}
393
394#[cfg(test)]
395mod tests {
396 use super::*;
397 use crate::{CellRef, reference::Coord};
398 use formualizer_parse::parse;
399
400 fn create_test_graph() -> DependencyGraph {
401 DependencyGraph::new()
402 }
403
404 fn cell_ref(sheet_id: u16, row: u32, col: u32) -> CellRef {
405 CellRef::new(sheet_id, Coord::new(row, col, false, false))
406 }
407
408 #[test]
409 fn test_transaction_context_basic() {
410 let mut graph = create_test_graph();
411 let mut ctx = TransactionContext::new(&mut graph);
412
413 let tx_id = ctx.begin().unwrap();
415 assert!(ctx.is_active());
416 assert_eq!(ctx.active_id(), Some(tx_id));
417
418 {
420 let mut editor = ctx.editor();
421 editor.set_cell_value(cell_ref(0, 1, 1), LiteralValue::Number(42.0));
422 }
423
424 assert_eq!(ctx.change_count(), 1);
426
427 let committed_id = ctx.commit().unwrap();
429 assert_eq!(tx_id, committed_id);
430 assert!(!ctx.is_active());
431 }
432
433 #[test]
434 fn test_transaction_context_rollback_new_value() {
435 let mut graph = create_test_graph();
436
437 {
438 let mut ctx = TransactionContext::new(&mut graph);
439
440 ctx.begin().unwrap();
441
442 {
444 let mut editor = ctx.editor();
445 editor.set_cell_value(cell_ref(0, 1, 1), LiteralValue::Number(20.0));
446 }
447
448 ctx.rollback().unwrap();
450 assert_eq!(ctx.change_count(), 0);
451 }
452
453 assert!(
455 graph
456 .get_vertex_id_for_address(&cell_ref(0, 1, 1))
457 .is_none()
458 );
459 }
460
461 #[test]
466 #[ignore]
467 fn test_transaction_context_rollback_value_update() {
468 let mut graph = create_test_graph();
469
470 let _ = graph.set_cell_value("Sheet1", 1, 1, LiteralValue::Number(10.0));
472
473 {
474 let mut ctx = TransactionContext::new(&mut graph);
475 ctx.begin().unwrap();
476
477 {
479 let mut editor = ctx.editor();
480 editor.set_cell_value(cell_ref(0, 1, 1), LiteralValue::Number(20.0));
481 }
482
483 ctx.rollback().unwrap();
485 }
486
487 assert_eq!(
489 graph.get_cell_value("Sheet1", 1, 1),
490 Some(LiteralValue::Number(10.0))
491 );
492 }
493
494 #[test]
497 #[ignore]
498 fn test_transaction_context_multiple_changes() {
499 let mut graph = create_test_graph();
500
501 {
502 let mut ctx = TransactionContext::new(&mut graph);
503
504 ctx.begin().unwrap();
505
506 {
508 let mut editor = ctx.editor();
509 editor.set_cell_value(cell_ref(0, 1, 1), LiteralValue::Number(10.0));
510 editor.set_cell_value(cell_ref(0, 2, 1), LiteralValue::Number(20.0));
511 editor.set_cell_formula(cell_ref(0, 3, 1), parse("=A1+A2").unwrap());
512 }
513
514 assert_eq!(ctx.change_count(), 3);
515
516 ctx.commit().unwrap();
518 }
519
520 assert_eq!(
522 graph.get_cell_value("Sheet1", 1, 1),
523 Some(LiteralValue::Number(10.0))
524 );
525 assert_eq!(
526 graph.get_cell_value("Sheet1", 2, 1),
527 Some(LiteralValue::Number(20.0))
528 );
529 assert!(
530 graph
531 .get_vertex_id_for_address(&cell_ref(0, 3, 1))
532 .is_some()
533 );
534 }
535
536 #[test]
537 fn test_transaction_context_savepoints() {
538 let mut graph = create_test_graph();
539
540 {
541 let mut ctx = TransactionContext::new(&mut graph);
542
543 ctx.begin().unwrap();
544
545 {
547 let mut editor = ctx.editor();
548 editor.set_cell_value(cell_ref(0, 1, 1), LiteralValue::Number(10.0));
549 }
550
551 ctx.savepoint("after_first").unwrap();
553
554 {
556 let mut editor = ctx.editor();
557 editor.set_cell_value(cell_ref(0, 2, 1), LiteralValue::Number(20.0));
558 editor.set_cell_value(cell_ref(0, 3, 1), LiteralValue::Number(30.0));
559 }
560
561 assert_eq!(ctx.change_count(), 3);
562
563 ctx.rollback_to_savepoint("after_first").unwrap();
565
566 assert_eq!(ctx.change_count(), 1);
568
569 ctx.commit().unwrap();
571 }
572
573 assert_eq!(
575 graph.get_cell_value("Sheet1", 1, 1),
576 Some(LiteralValue::Number(10.0))
577 );
578 assert!(
579 graph
580 .get_vertex_id_for_address(&cell_ref(0, 2, 1))
581 .is_none()
582 );
583 assert!(
584 graph
585 .get_vertex_id_for_address(&cell_ref(0, 3, 1))
586 .is_none()
587 );
588 }
589
590 #[test]
591 fn test_transaction_context_size_limit() {
592 let mut graph = create_test_graph();
593 let mut ctx = TransactionContext::with_max_size(&mut graph, 2);
594
595 ctx.begin().unwrap();
596
597 {
599 let mut editor = ctx.editor();
600 editor.set_cell_value(cell_ref(0, 1, 1), LiteralValue::Number(1.0));
601 editor.set_cell_value(cell_ref(0, 2, 1), LiteralValue::Number(2.0));
602 }
603
604 assert!(ctx.commit().is_ok());
606
607 ctx.begin().unwrap();
608
609 {
611 let mut editor = ctx.editor();
612 editor.set_cell_value(cell_ref(0, 3, 1), LiteralValue::Number(3.0));
613 editor.set_cell_value(cell_ref(0, 4, 1), LiteralValue::Number(4.0));
614 editor.set_cell_value(cell_ref(0, 5, 1), LiteralValue::Number(5.0));
615 }
616
617 match ctx.commit() {
619 Err(TransactionError::TransactionTooLarge { size, max }) => {
620 assert_eq!(size, 3);
621 assert_eq!(max, 2);
622 }
623 _ => panic!("Expected TransactionTooLarge error"),
624 }
625 }
626
627 #[test]
628 fn test_transaction_context_no_active_transaction() {
629 let mut graph = create_test_graph();
630 let mut ctx = TransactionContext::new(&mut graph);
631
632 assert!(ctx.commit().is_err());
634 assert!(ctx.rollback().is_err());
635 assert!(ctx.savepoint("test").is_err());
636 assert!(ctx.rollback_to_savepoint("test").is_err());
637 }
638
639 #[test]
640 fn test_transaction_context_clear_change_log() {
641 let mut graph = create_test_graph();
642 let mut ctx = TransactionContext::new(&mut graph);
643
644 {
646 let mut editor = ctx.editor();
647 editor.set_cell_value(cell_ref(0, 1, 1), LiteralValue::Number(1.0));
648 editor.set_cell_value(cell_ref(0, 2, 1), LiteralValue::Number(2.0));
649 }
650
651 assert_eq!(ctx.change_count(), 2);
652
653 ctx.clear_change_log();
655 assert_eq!(ctx.change_count(), 0);
656 }
657
658 #[test]
659 fn test_transaction_context_compound_operations() {
660 let mut graph = create_test_graph();
661 let mut ctx = TransactionContext::new(&mut graph);
662
663 ctx.begin().unwrap();
664
665 ctx.change_log.begin_compound("test_compound".to_string());
667
668 {
669 let mut editor = ctx.editor();
670 editor.set_cell_value(cell_ref(0, 1, 1), LiteralValue::Number(1.0));
671 editor.set_cell_value(cell_ref(0, 2, 1), LiteralValue::Number(2.0));
672 }
673
674 ctx.change_log.end_compound();
675
676 assert_eq!(ctx.change_count(), 4);
678
679 ctx.rollback().unwrap();
681 assert_eq!(ctx.change_count(), 0);
682 }
683}