forgekit_core/edit/
undo.rs1use std::path::PathBuf;
2
3use crate::error::Result;
4
5pub(crate) enum UndoableOp {
6 CreateFile {
7 path: PathBuf,
8 },
9 WriteFile {
10 path: PathBuf,
11 previous: Option<String>,
12 },
13 CreateDirectory {
14 path: PathBuf,
15 },
16}
17
18pub struct PendingUndo {
19 pub(crate) operation: UndoableOp,
20}
21
22pub enum UndoResult {
23 Undone { operation: String },
24 Empty,
25}
26
27impl super::EditModule {
28 pub fn with_undo_capacity(mut self, capacity: usize) -> Self {
29 self.undo_capacity = capacity;
30 self
31 }
32
33 pub async fn undo(&self) -> Result<UndoResult> {
34 let op = {
35 let mut stack = self.undo_stack.lock();
36 if stack.is_empty() {
37 return Ok(UndoResult::Empty);
38 }
39 stack
40 .pop()
41 .expect("invariant: stack non-empty after is_empty check")
42 };
43
44 match op.operation {
45 UndoableOp::CreateFile { path } => {
46 let full = self.store.codebase_path.join(&path);
47 if full.exists() {
48 tokio::fs::remove_file(&full).await?;
49 }
50 Ok(UndoResult::Undone {
51 operation: format!("create_file({})", path.display()),
52 })
53 }
54 UndoableOp::WriteFile { path, previous } => {
55 let full = self.store.codebase_path.join(&path);
56 match previous {
57 Some(content) => {
58 tokio::fs::write(&full, content).await?;
59 }
60 None => {
61 if full.exists() {
62 tokio::fs::remove_file(&full).await?;
63 }
64 }
65 }
66 Ok(UndoResult::Undone {
67 operation: format!("write_file({})", path.display()),
68 })
69 }
70 UndoableOp::CreateDirectory { path } => {
71 let full = self.store.codebase_path.join(&path);
72 if full.exists() {
73 tokio::fs::remove_dir(&full).await?;
74 }
75 Ok(UndoResult::Undone {
76 operation: format!("create_directory({})", path.display()),
77 })
78 }
79 }
80 }
81
82 pub fn can_undo(&self) -> bool {
83 !self.undo_stack.lock().is_empty()
84 }
85
86 pub fn undo_depth(&self) -> usize {
87 self.undo_stack.lock().len()
88 }
89
90 pub fn clear_undo_stack(&self) {
91 self.undo_stack.lock().clear();
92 }
93
94 pub(crate) fn push_undo(&self, op: UndoableOp) {
95 let mut stack = self.undo_stack.lock();
96 if stack.len() >= self.undo_capacity {
97 stack.remove(0);
98 }
99 stack.push(PendingUndo { operation: op });
100 }
101}