vcs-modify-guard 0.1.0

A library for checking whether it is safe to modify files in a VCS worktree.
Documentation
use std::{
    fmt::Debug,
    path::{Path, PathBuf},
};

#[cfg(feature = "git-cli")]
pub use self::git_cli::GitCliBackendError;
#[cfg(feature = "git-gix")]
pub use self::git_gix::GixBackendError;
#[cfg(feature = "git-libgit2")]
pub use self::git_libgit2::Libgit2BackendError;
use crate::{
    error::{self, ModifyGuardError},
    repository::{FileChange, RepositoryChanges},
    util::WorktreeRelativePath,
};

#[cfg(feature = "git-cli")]
mod git_cli;
#[cfg(feature = "git-gix")]
mod git_gix;
#[cfg(feature = "git-libgit2")]
mod git_libgit2;
#[cfg(test)]
mod tests;

trait VcsBackend: Debug + Send + Sync {
    fn discover(&self, path: &Path) -> Result<Option<Box<dyn VcsRepository>>, ModifyGuardError>;
    fn open(&self, path: &Path) -> Result<Option<Box<dyn VcsRepository>>, ModifyGuardError>;
}

static BACKENDS: &[&dyn VcsBackend] = &[
    #[cfg(feature = "git-gix")]
    &git_gix::BACKEND,
    #[cfg(feature = "git-libgit2")]
    &git_libgit2::BACKEND,
    #[cfg(feature = "git-cli")]
    &git_cli::BACKEND,
];

pub(crate) fn discover(path: &Path) -> Result<Option<Box<dyn VcsRepository>>, ModifyGuardError> {
    for backend in BACKENDS {
        if let Some(repo) = backend.discover(path)? {
            return Ok(Some(repo));
        }
    }
    Ok(None)
}

pub(crate) fn open(path: &Path) -> Result<Box<dyn VcsRepository>, ModifyGuardError> {
    for backend in BACKENDS {
        if let Some(repo) = backend.open(path)? {
            return Ok(repo);
        }
    }
    Err(error::NotARepositorySnafu { path }.build())
}

pub(crate) trait VcsRepository: Debug {
    fn worktree(&self) -> &Path;
    fn repository_changes(&self) -> Result<Option<RepositoryChanges>, ModifyGuardError>;
    fn path_changes(&self, wt_path: &Path) -> Result<Option<RepositoryChanges>, ModifyGuardError>;
    fn file_change(&self, wt_path: &Path) -> Result<Option<FileChange>, ModifyGuardError>;

    fn resolve_path(&self, path: &Path) -> Result<PathBuf, ModifyGuardError> {
        let wt_path = WorktreeRelativePath::from_path(self.worktree(), path)?;
        Ok(wt_path.into())
    }
}

// assert that VcsRepository is dyn safe
const _: Option<&dyn VcsRepository> = None;