Skip to main content

formualizer_workbook/
session.rs

1use crate::error::IoError;
2use formualizer_eval::engine::graph::DependencyGraph;
3use formualizer_eval::engine::graph::editor::change_log::ChangeLog;
4use formualizer_eval::engine::graph::editor::undo_engine::UndoEngine;
5use formualizer_eval::engine::graph::editor::vertex_editor::{EditorError, VertexEditor};
6
7/// IO-level configuration toggles.
8#[derive(Clone, Debug)]
9pub struct IoConfig {
10    /// When false, ChangeLog is disabled and undo/redo become no-ops.
11    pub enable_changelog: bool,
12}
13
14impl Default for IoConfig {
15    fn default() -> Self {
16        Self {
17            enable_changelog: true,
18        }
19    }
20}
21
22/// High-level editing session that owns a graph and optional change tracking.
23pub struct EditorSession {
24    pub graph: DependencyGraph,
25    enable_changelog: bool,
26    log: ChangeLog,
27    undo: UndoEngine,
28}
29
30impl EditorSession {
31    pub fn new_with_graph(graph: DependencyGraph, cfg: IoConfig) -> Self {
32        let mut log = ChangeLog::new();
33        log.set_enabled(cfg.enable_changelog);
34        Self {
35            graph,
36            enable_changelog: cfg.enable_changelog,
37            log,
38            undo: UndoEngine::new(),
39        }
40    }
41
42    pub fn new(cfg: IoConfig) -> Self {
43        Self::new_with_graph(DependencyGraph::new(), cfg)
44    }
45
46    /// Begin a user-visible action; opens a compound group when enabled.
47    pub fn begin_action(&mut self, description: impl Into<String>) {
48        if self.enable_changelog {
49            self.log.begin_compound(description.into());
50        }
51    }
52
53    /// End the current user-visible action; closes group when enabled.
54    pub fn end_action(&mut self) {
55        if self.enable_changelog {
56            self.log.end_compound();
57        }
58    }
59
60    /// Helper to run an action with automatic begin/end.
61    pub fn with_action<F, R>(
62        &mut self,
63        description: impl Into<String>,
64        f: F,
65    ) -> Result<R, EditorError>
66    where
67        F: FnOnce(&mut VertexEditor) -> Result<R, EditorError>,
68    {
69        self.begin_action(description);
70        let res = {
71            let mut editor = self.make_editor();
72            f(&mut editor)
73        };
74        self.end_action();
75        res
76    }
77
78    /// Get a VertexEditor wired appropriately (with or without logger).
79    pub fn make_editor(&mut self) -> VertexEditor<'_> {
80        if self.enable_changelog {
81            VertexEditor::with_logger(&mut self.graph, &mut self.log)
82        } else {
83            VertexEditor::new(&mut self.graph)
84        }
85    }
86
87    /// Undo last compound group if enabled. No-op otherwise.
88    pub fn undo(&mut self) -> Result<(), EditorError> {
89        if self.enable_changelog {
90            let _ = self.undo.undo(&mut self.graph, &mut self.log)?;
91            Ok(())
92        } else {
93            Ok(())
94        }
95    }
96
97    /// Redo last undone group if enabled. No-op otherwise.
98    pub fn redo(&mut self) -> Result<(), EditorError> {
99        if self.enable_changelog {
100            let _ = self.undo.redo(&mut self.graph, &mut self.log)?;
101            Ok(())
102        } else {
103            Ok(())
104        }
105    }
106
107    /// Commit using a persistence callback. On error, roll back last group when enabled.
108    pub fn commit_with_rollback<F>(&mut self, persist: F) -> Result<(), IoError>
109    where
110        F: FnOnce(&DependencyGraph) -> Result<(), IoError>,
111    {
112        match persist(&self.graph) {
113            Ok(()) => Ok(()),
114            Err(e) => {
115                if self.enable_changelog {
116                    // Best-effort rollback of the last group
117                    let _ = self.undo();
118                }
119                Err(e)
120            }
121        }
122    }
123
124    /// Access to ChangeLog for inspection; None when disabled.
125    pub fn changelog_enabled(&self) -> bool {
126        self.enable_changelog
127    }
128}