use anyhow::Result;
use std::sync::Arc;
use crate::git::{CommitResult, GitRepo};
use crate::log_debug;
pub struct GitCommitService {
repo: Arc<GitRepo>,
verify: bool,
}
impl GitCommitService {
#[must_use]
pub fn new(repo: Arc<GitRepo>, _use_gitmoji: bool, verify: bool) -> Self {
Self { repo, verify }
}
#[must_use]
pub fn from_repo(repo: GitRepo, use_gitmoji: bool, verify: bool) -> Self {
Self::new(Arc::new(repo), use_gitmoji, verify)
}
#[must_use]
pub fn is_remote(&self) -> bool {
self.repo.is_remote()
}
pub fn pre_commit(&self) -> Result<()> {
if self.is_remote() {
log_debug!("Skipping pre-commit hook for remote repository");
return Ok(());
}
if self.verify {
self.repo.execute_hook("pre-commit")
} else {
Ok(())
}
}
pub fn perform_commit(&self, message: &str) -> Result<CommitResult> {
self.perform_local_change(
message,
"commit",
"Cannot commit to a remote repository",
GitRepo::commit,
)
}
pub fn perform_amend(&self, message: &str) -> Result<CommitResult> {
self.perform_local_change(
message,
"amend",
"Cannot amend a commit in a remote repository",
GitRepo::amend_commit,
)
}
fn perform_local_change(
&self,
message: &str,
action: &str,
remote_error: &str,
operation: fn(&GitRepo, &str) -> Result<CommitResult>,
) -> Result<CommitResult> {
if self.is_remote() {
return Err(anyhow::anyhow!("{remote_error}"));
}
log_debug!("Performing {} with message: {}", action, message);
if !self.verify {
log_debug!("Skipping pre-commit hook (verify=false)");
return operation(&self.repo, message);
}
self.run_pre_commit_hook()?;
self.finish_local_change(message, action, operation)
}
fn run_pre_commit_hook(&self) -> Result<()> {
log_debug!("Executing pre-commit hook");
self.repo
.execute_hook("pre-commit")
.inspect(|()| {
log_debug!("Pre-commit hook executed successfully");
})
.inspect_err(|e| {
log_debug!("Pre-commit hook failed: {}", e);
})
}
fn finish_local_change(
&self,
message: &str,
action: &str,
operation: fn(&GitRepo, &str) -> Result<CommitResult>,
) -> Result<CommitResult> {
match operation(&self.repo, message) {
Ok(result) => {
self.run_post_commit_hook();
log_debug!("{} performed successfully", capitalized_action(action));
Ok(result)
}
Err(e) => {
log_debug!("{} failed: {}", capitalized_action(action), e);
Err(e)
}
}
}
fn run_post_commit_hook(&self) {
log_debug!("Executing post-commit hook");
if let Err(e) = self.repo.execute_hook("post-commit") {
log_debug!("Post-commit hook failed: {}", e);
}
}
pub fn get_head_commit_message(&self) -> Result<String> {
self.repo.get_head_commit_message()
}
#[must_use]
pub fn repo(&self) -> &GitRepo {
&self.repo
}
}
fn capitalized_action(action: &str) -> String {
let mut chars = action.chars();
chars.next().map_or_else(String::new, |first| {
first.to_uppercase().collect::<String>() + chars.as_str()
})
}
#[cfg(test)]
mod tests {
#[test]
fn test_git_commit_service_construction() {
}
}