forgekit-core 0.5.0

Deterministic code intelligence SDK - Core library
Documentation
use std::path::PathBuf;

use crate::error::Result;

pub(crate) enum UndoableOp {
    CreateFile {
        path: PathBuf,
    },
    WriteFile {
        path: PathBuf,
        previous: Option<String>,
    },
    CreateDirectory {
        path: PathBuf,
    },
}

pub struct PendingUndo {
    pub(crate) operation: UndoableOp,
}

pub enum UndoResult {
    Undone { operation: String },
    Empty,
}

impl super::EditModule {
    pub fn with_undo_capacity(mut self, capacity: usize) -> Self {
        self.undo_capacity = capacity;
        self
    }

    pub async fn undo(&self) -> Result<UndoResult> {
        let op = {
            let mut stack = self.undo_stack.lock();
            if stack.is_empty() {
                return Ok(UndoResult::Empty);
            }
            stack
                .pop()
                .expect("invariant: stack non-empty after is_empty check")
        };

        match op.operation {
            UndoableOp::CreateFile { path } => {
                let full = self.store.codebase_path.join(&path);
                if full.exists() {
                    tokio::fs::remove_file(&full).await?;
                }
                Ok(UndoResult::Undone {
                    operation: format!("create_file({})", path.display()),
                })
            }
            UndoableOp::WriteFile { path, previous } => {
                let full = self.store.codebase_path.join(&path);
                match previous {
                    Some(content) => {
                        tokio::fs::write(&full, content).await?;
                    }
                    None => {
                        if full.exists() {
                            tokio::fs::remove_file(&full).await?;
                        }
                    }
                }
                Ok(UndoResult::Undone {
                    operation: format!("write_file({})", path.display()),
                })
            }
            UndoableOp::CreateDirectory { path } => {
                let full = self.store.codebase_path.join(&path);
                if full.exists() {
                    tokio::fs::remove_dir(&full).await?;
                }
                Ok(UndoResult::Undone {
                    operation: format!("create_directory({})", path.display()),
                })
            }
        }
    }

    pub fn can_undo(&self) -> bool {
        !self.undo_stack.lock().is_empty()
    }

    pub fn undo_depth(&self) -> usize {
        self.undo_stack.lock().len()
    }

    pub fn clear_undo_stack(&self) {
        self.undo_stack.lock().clear();
    }

    pub(crate) fn push_undo(&self, op: UndoableOp) {
        let mut stack = self.undo_stack.lock();
        if stack.len() >= self.undo_capacity {
            stack.remove(0);
        }
        stack.push(PendingUndo { operation: op });
    }
}