mod detect;
mod git;
mod jj;
pub(crate) use detect::{VcsType, detect_vcs};
pub(crate) use git::GitProvider;
pub(crate) use jj::JjProvider;
use crate::cli::AddArgs;
use crate::error::Result;
use std::path::{Path, PathBuf};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum VcsKind {
Git,
Jj,
JjColocated,
}
impl VcsKind {
pub fn name(&self) -> &'static str {
match self {
VcsKind::Git => "git",
VcsKind::Jj | VcsKind::JjColocated => "jj",
}
}
pub fn workspace_type(&self) -> &'static str {
match self {
VcsKind::Git => "worktree",
VcsKind::Jj | VcsKind::JjColocated => "workspace",
}
}
}
#[derive(Debug, Clone)]
pub(crate) struct WorkspaceInfo {
pub path: PathBuf,
pub head: String,
pub branch: Option<String>,
pub is_main: bool,
pub is_locked: bool,
pub workspace_name: Option<String>,
}
#[derive(Debug, Clone, Default)]
pub(crate) struct WorkspaceStatus {
pub has_uncommitted_changes: bool,
pub modified_count: usize,
pub deleted_count: usize,
pub untracked_count: usize,
}
#[derive(Debug, Clone, Default)]
pub(crate) struct UnpushedInfo {
pub has_unpushed: bool,
pub count: usize,
}
pub(crate) trait VcsProvider {
fn kind(&self) -> VcsKind;
fn name(&self) -> &'static str {
self.kind().name()
}
fn workspace_type(&self) -> &'static str {
self.kind().workspace_type()
}
fn is_inside_repo(&self) -> bool;
fn main_workspace_root(&self) -> Result<PathBuf>;
fn repository_name(&self) -> Result<String> {
let root = self.main_workspace_root()?;
root.file_name()
.and_then(|n| n.to_str())
.map(|s| s.to_string())
.ok_or_else(|| crate::error::Error::NotInRepo {
vcs: self.name().to_string(),
})
}
fn main_workspace_path_for(&self, repo_root: &Path) -> Result<PathBuf>;
fn workspace_add(&self, args: &AddArgs, path: &Path) -> Result<()>;
fn workspace_remove(&self, path: &Path, force: bool) -> Result<()>;
fn workspace_remove_checked(&self, path: &Path, force: bool) -> Result<()>;
fn list_workspaces(&self) -> Result<Vec<WorkspaceInfo>>;
fn workspace_status(&self, path: &Path) -> Result<WorkspaceStatus>;
fn workspace_unpushed(&self, path: &Path) -> Result<UnpushedInfo>;
fn get_upstream(&self, path: &Path) -> Result<Option<String>>;
fn list_tracked_files(&self, repo_root: &Path) -> Result<Vec<PathBuf>>;
fn list_branches(&self) -> Result<Vec<String>>;
fn list_remote_branches(&self) -> Result<Vec<String>>;
fn log_oneline(&self, commitish: &str, limit: usize) -> Result<Vec<String>>;
fn validate_branch_name(&self, name: &str) -> Result<Option<String>>;
fn delete_branch(&self, name: &str) -> Result<()>;
}
pub(crate) fn get_provider() -> Result<Box<dyn VcsProvider>> {
match detect_vcs()? {
VcsType::Git => Ok(Box::new(GitProvider)),
VcsType::Jj | VcsType::JjColocated => Ok(Box::new(JjProvider)),
}
}