magi-code 0.102.0

Repository-aware CLI coding agent for terminal work
Documentation
use super::PathContext;
use crate::{
    checkpoints::{
        SnapshotEligibility, SnapshotTool, capture_file_snapshot, classify_snapshot_eligibility,
    },
    support::path::lexical_normalize,
};
use std::{
    fs,
    path::{Path, PathBuf},
    sync::{Arc, Mutex},
};

#[derive(Clone, Copy)]
pub(in crate::tools) struct MutationContext<'a> {
    pub(in crate::tools) paths: PathContext<'a>,
    pub(in crate::tools) mutation_locks:
        &'a Mutex<std::collections::HashMap<PathBuf, Arc<Mutex<()>>>>,
    pub(in crate::tools) checkpoint_context: &'a Mutex<Option<crate::checkpoints::SnapshotContext>>,
}

impl MutationContext<'_> {
    pub(in crate::tools) fn file_lock(&self, path: &Path) -> anyhow::Result<Arc<Mutex<()>>> {
        let key = path
            .canonicalize()
            .unwrap_or_else(|_| lexical_normalize(path));
        let mut locks = self
            .mutation_locks
            .lock()
            .map_err(|_| anyhow::anyhow!("tool mutation lock registry mutex poisoned"))?;
        Ok(locks
            .entry(key)
            .or_insert_with(|| Arc::new(Mutex::new(())))
            .clone())
    }
    fn snapshot_eligible(&self, path: &Path) -> bool {
        let Some(context) = self.checkpoint_context() else {
            return false;
        };
        matches!(
            classify_snapshot_eligibility(path, self.paths.cwd_canonical, &context.paths),
            SnapshotEligibility::Eligible
        )
    }

    pub(in crate::tools) fn pre_snapshot_bytes(
        &self,
        path: &Path,
    ) -> anyhow::Result<Option<Vec<u8>>> {
        if !path.exists() || !self.snapshot_eligible(path) {
            return Ok(None);
        }
        Ok(Some(fs::read(path)?))
    }

    pub(in crate::tools) fn record_file_snapshot(
        &self,
        tool: SnapshotTool,
        path: &Path,
        pre: Option<&[u8]>,
        post: Option<&[u8]>,
    ) {
        let Some(context) = self.checkpoint_context() else {
            return;
        };
        if let Err(error) =
            capture_file_snapshot(&context, self.paths.cwd_canonical, tool, path, pre, post)
        {
            eprintln!(
                "warning: checkpoint snapshot failed: {}",
                crate::output::redact_sensitive_text(&error.to_string())
            );
        }
    }
    fn checkpoint_context(&self) -> Option<crate::checkpoints::SnapshotContext> {
        self.checkpoint_context
            .lock()
            .ok()
            .and_then(|slot| slot.clone())
    }
}