mod implementation;
pub mod reserved_state;
mod templates;
#[cfg(test)]
mod tests;
use super::*;
use eyre::Result;
use git2::{
ApplyLocation, BranchType, DiffFormat, Email, EmailCreateOptions, IndexAddOption, ObjectType,
Oid, Repository, RepositoryInitOptions, ResetType, Sort, StashApplyOptions, StashFlags, Status,
StatusOptions, StatusShow,
};
use implementation::RawRepositoryInner;
use simperby_core::reserved::ReservedState;
use std::convert::TryFrom;
use std::str;
use templates::*;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum Error {
#[error("git2 error: {0}")]
Git2Error(git2::Error),
#[error("not found: {0}")]
NotFound(String),
#[error("the repository is invalid: {0}")]
InvalidRepository(String),
#[error("unknown error: {0}")]
Unknown(String),
}
impl From<git2::Error> for Error {
fn from(e: git2::Error) -> Self {
Error::Git2Error(e)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SemanticCommit {
pub title: String,
pub body: String,
pub diff: Diff,
pub author: MemberName,
pub timestamp: Timestamp,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct RawCommit {
pub message: String,
pub diff: Option<String>,
pub author: String,
pub email: String,
pub timestamp: Timestamp,
}
#[derive(Debug)]
pub struct RawRepository {
inner: tokio::sync::Mutex<Option<RawRepositoryInner>>,
}
impl RawRepository {
pub async fn init(
directory: &str,
init_commit_message: &str,
init_commit_branch: &Branch,
) -> Result<Self, Error>
where
Self: Sized,
{
let repo = RawRepositoryInner::init(directory, init_commit_message, init_commit_branch)?;
let inner = tokio::sync::Mutex::new(Some(repo));
Ok(Self { inner })
}
pub async fn open(directory: &str) -> Result<Self, Error>
where
Self: Sized,
{
let repo = RawRepositoryInner::open(directory)?;
let inner = tokio::sync::Mutex::new(Some(repo));
Ok(Self { inner })
}
pub async fn clone(directory: &str, url: &str) -> Result<Self, Error>
where
Self: Sized,
{
let repo = RawRepositoryInner::clone(directory, url)?;
let inner = tokio::sync::Mutex::new(Some(repo));
Ok(Self { inner })
}
pub async fn retrieve_commit_hash(
&self,
revision_selection: String,
) -> Result<CommitHash, Error> {
helper_1(
self,
RawRepositoryInner::retrieve_commit_hash,
revision_selection,
)
.await
}
pub async fn list_branches(&self) -> Result<Vec<Branch>, Error> {
helper_0(self, RawRepositoryInner::list_branches).await
}
pub async fn create_branch(
&self,
branch_name: Branch,
commit_hash: CommitHash,
) -> Result<(), Error> {
helper_2(
self,
RawRepositoryInner::create_branch,
branch_name,
commit_hash,
)
.await
}
pub async fn locate_branch(&self, branch: Branch) -> Result<CommitHash, Error> {
helper_1(self, RawRepositoryInner::locate_branch, branch).await
}
pub async fn get_branches(&self, commit_hash: CommitHash) -> Result<Vec<Branch>, Error> {
helper_1(self, RawRepositoryInner::get_branches, commit_hash).await
}
pub async fn move_branch(
&mut self,
branch: Branch,
commit_hash: CommitHash,
) -> Result<(), Error> {
helper_2_mut(self, RawRepositoryInner::move_branch, branch, commit_hash).await
}
pub async fn delete_branch(&mut self, branch: Branch) -> Result<(), Error> {
helper_1_mut(self, RawRepositoryInner::delete_branch, branch).await
}
pub async fn list_tags(&self) -> Result<Vec<Tag>, Error> {
helper_0(self, RawRepositoryInner::list_tags).await
}
pub async fn create_tag(&mut self, tag: Tag, commit_hash: CommitHash) -> Result<(), Error> {
helper_2_mut(self, RawRepositoryInner::create_tag, tag, commit_hash).await
}
pub async fn locate_tag(&self, tag: Tag) -> Result<CommitHash, Error> {
helper_1(self, RawRepositoryInner::locate_tag, tag).await
}
pub async fn get_tag(&self, commit_hash: CommitHash) -> Result<Vec<Tag>, Error> {
helper_1(self, RawRepositoryInner::get_tag, commit_hash).await
}
pub async fn remove_tag(&mut self, tag: Tag) -> Result<(), Error> {
helper_1_mut(self, RawRepositoryInner::remove_tag, tag).await
}
pub async fn create_commit(&mut self, commit: RawCommit) -> Result<CommitHash, Error> {
helper_1_mut(self, RawRepositoryInner::create_commit, commit).await
}
pub async fn create_commit_all(&mut self, commit: RawCommit) -> Result<CommitHash, Error> {
helper_1_mut(self, RawRepositoryInner::create_commit_all, commit).await
}
pub async fn read_commit(&self, commit_hash: CommitHash) -> Result<RawCommit, Error> {
helper_1(self, RawRepositoryInner::read_commit, commit_hash).await
}
pub async fn create_semantic_commit(
&mut self,
commit: SemanticCommit,
) -> Result<CommitHash, Error> {
helper_1_mut(self, RawRepositoryInner::create_semantic_commit, commit).await
}
pub async fn read_semantic_commit(
&self,
commit_hash: CommitHash,
) -> Result<SemanticCommit, Error> {
helper_1(self, RawRepositoryInner::read_semantic_commit, commit_hash).await
}
pub async fn run_garbage_collection(&mut self) -> Result<(), Error> {
helper_0_mut(self, RawRepositoryInner::run_garbage_collection).await
}
pub async fn checkout_clean(&mut self) -> Result<(), Error> {
helper_0_mut(self, RawRepositoryInner::checkout_clean).await
}
pub async fn checkout(&mut self, branch: Branch) -> Result<(), Error> {
helper_1_mut(self, RawRepositoryInner::checkout, branch).await
}
pub async fn checkout_detach(&mut self, commit_hash: CommitHash) -> Result<(), Error> {
helper_1_mut(self, RawRepositoryInner::checkout_detach, commit_hash).await
}
pub async fn stash(&mut self) -> Result<(), Error> {
helper_0_mut(self, RawRepositoryInner::stash).await
}
pub async fn stash_pop(&mut self, index: bool) -> Result<(), Error> {
helper_1_mut(self, RawRepositoryInner::stash_pop, index).await
}
pub async fn stash_apply(&mut self, index: bool) -> Result<(), Error> {
helper_1_mut(self, RawRepositoryInner::stash_apply, index).await
}
pub async fn stash_drop(&mut self) -> Result<(), Error> {
helper_0_mut(self, RawRepositoryInner::stash_drop).await
}
pub async fn check_clean(&self) -> Result<(), Error> {
helper_0(self, RawRepositoryInner::check_clean).await
}
pub async fn get_working_directory_path(&self) -> Result<String, Error> {
helper_0(self, RawRepositoryInner::get_working_directory_path).await
}
pub async fn get_head(&self) -> Result<CommitHash, Error> {
helper_0(self, RawRepositoryInner::get_head).await
}
pub async fn get_currently_checkout_branch(&self) -> Result<Option<Branch>, Error> {
helper_0(self, RawRepositoryInner::get_currently_checkout_branch).await
}
pub async fn get_initial_commit(&self) -> Result<CommitHash, Error> {
helper_0(self, RawRepositoryInner::get_initial_commit).await
}
pub async fn get_patch(&self, commit_hash: CommitHash) -> Result<String, Error> {
helper_1(self, RawRepositoryInner::get_patch, commit_hash).await
}
pub async fn show_commit(&self, commit_hash: CommitHash) -> Result<String, Error> {
helper_1(self, RawRepositoryInner::show_commit, commit_hash).await
}
pub async fn list_ancestors(
&self,
commit_hash: CommitHash,
max: Option<usize>,
) -> Result<Vec<CommitHash>, Error> {
helper_2(self, RawRepositoryInner::list_ancestors, commit_hash, max).await
}
pub async fn query_commit_path(
&self,
ancestor: CommitHash,
descendant: CommitHash,
) -> Result<Vec<CommitHash>, Error> {
helper_2(
self,
RawRepositoryInner::query_commit_path,
ancestor,
descendant,
)
.await
}
pub async fn list_children(&self, commit_hash: CommitHash) -> Result<Vec<CommitHash>, Error> {
helper_1(self, RawRepositoryInner::list_children, commit_hash).await
}
pub async fn find_merge_base(
&self,
commit_hash1: CommitHash,
commit_hash2: CommitHash,
) -> Result<CommitHash, Error> {
helper_2(
self,
RawRepositoryInner::find_merge_base,
commit_hash1,
commit_hash2,
)
.await
}
pub async fn read_reserved_state(&self) -> Result<ReservedState, Error> {
helper_0(self, RawRepositoryInner::read_reserved_state).await
}
pub async fn read_reserved_state_at_commit(
&self,
commit_hash: CommitHash,
) -> Result<ReservedState, Error> {
helper_1(
self,
RawRepositoryInner::read_reserved_state_at_commit,
commit_hash,
)
.await
}
pub async fn add_remote(
&mut self,
remote_name: String,
remote_url: String,
) -> Result<(), Error> {
helper_2_mut(
self,
RawRepositoryInner::add_remote,
remote_name,
remote_url,
)
.await
}
pub async fn remove_remote(&mut self, remote_name: String) -> Result<(), Error> {
helper_1_mut(self, RawRepositoryInner::remove_remote, remote_name).await
}
pub async fn fetch_all(&mut self) -> Result<(), Error> {
helper_0_mut(self, RawRepositoryInner::fetch_all).await
}
pub async fn push_option(
&self,
remote_name: String,
branch: Branch,
option: Option<String>,
) -> Result<(), Error> {
helper_3(
self,
RawRepositoryInner::push_option,
remote_name,
branch,
option,
)
.await
}
pub async fn list_remotes(&self) -> Result<Vec<(String, String)>, Error> {
helper_0(self, RawRepositoryInner::list_remotes).await
}
pub async fn list_remote_tracking_branches(
&self,
) -> Result<Vec<(String, String, CommitHash)>, Error> {
helper_0(self, RawRepositoryInner::list_remote_tracking_branches).await
}
pub async fn locate_remote_tracking_branch(
&self,
remote_name: String,
branch_name: String,
) -> Result<CommitHash, Error> {
helper_2(
self,
RawRepositoryInner::locate_remote_tracking_branch,
remote_name,
branch_name,
)
.await
}
}
#[cfg(target_os = "windows")]
pub fn run_command(command: impl AsRef<str>) -> Result<(), Error> {
println!("> RUN: {}", command.as_ref());
let mut child = std::process::Command::new("C:/Program Files/Git/bin/sh.exe")
.arg("--login")
.arg("-c")
.arg(command.as_ref())
.spawn()
.map_err(|_| Error::Unknown("failed to execute process".to_string()))?;
let ecode = child
.wait()
.map_err(|_| Error::Unknown("failed to wait on child".to_string()))?;
if ecode.success() {
Ok(())
} else {
Err(Error::Unknown("failed to run process".to_string()))
}
}
#[cfg(not(target_os = "windows"))]
pub fn run_command(command: impl AsRef<str>) -> Result<(), Error> {
println!("> RUN: {}", command.as_ref());
let mut child = std::process::Command::new("sh")
.arg("-c")
.arg(command.as_ref())
.spawn()
.map_err(|_| Error::Unknown("failed to execute process".to_string()))?;
let ecode = child
.wait()
.map_err(|_| Error::Unknown("failed to wait on child".to_string()))?;
if ecode.success() {
Ok(())
} else {
Err(Error::Unknown("failed to run process".to_string()))
}
}