co-author 0.1.0

Co-Author your git commits
Documentation
use std::{error::Error, fs::OpenOptions, io::Write, path::PathBuf};

use co_author::conf;
use git2::{Repository, Signature};

use super::commit_body::{CommitBody, GitWrapper};

pub mod editmsg_handler;

pub struct LibGitWrapper {
	repo: Option<Repository>,
}

impl GitWrapper for LibGitWrapper {
	fn commit(&self) -> Result<(), Box<dyn Error>> {
		let signature = self
			.repo
			.as_ref()
			.unwrap()
			.signature()
			.map_err(|_| "User name and/or email not set".to_string())?;

		let commit_message = editmsg_handler::read_editmsg().ok_or("Commit message cannot be empty".to_string())?;

		self.try_to_commit(signature, commit_message)
			.map_err(|_| "Something went wrong!".to_string())?;
		Ok(())
	}

	fn write_to_editmsg(&self, commit_body: CommitBody) -> Result<(), Box<dyn Error>> {
		editmsg_handler::write_commit_to_file(commit_body)
	}

	fn add_status_to_editmsg(&self) -> Result<(), Box<dyn Error>> {
		let status = editmsg_handler::get_status_for_commit_file(self.repo.as_ref().unwrap());

		let mut file_to_append = OpenOptions::new().create(true).append(true).open(conf::editmsg())?;
		file_to_append.write_all(status.as_bytes())?;
		Ok(())
	}

	fn prev_commit_msg(&self) -> Result<String, Box<dyn Error>> {
		let head_ref = self.repo.as_ref().unwrap().head()?;
		let last_commit = head_ref.peel_to_commit()?;

		let commit_message = last_commit.message().unwrap_or_default();
		let first_line = commit_message.lines().next().unwrap_or_default();

		Ok(first_line.to_string())
	}
}

impl LibGitWrapper {
	pub fn from(path: PathBuf) -> Result<Self, String> {
		if let Ok(repo) = Repository::open(path.clone()) {
			return match Self::no_staged_changes(&repo) {
				true => Err("No staged changes".to_string()),
				false => Ok(Self { repo: Some(repo) }),
			};
		}
		Err("Could not open the repo".to_string())
	}

	fn no_staged_changes(repo: &Repository) -> bool {
		let head = repo.head().unwrap();
		let tree = head.peel_to_tree().unwrap();
		let index = repo.index().unwrap();
		let diff = repo.diff_tree_to_index(Some(&tree), Some(&index), None).unwrap();
		diff.deltas().count() == 0
	}

	fn try_to_commit(&self, signature: Signature, commit_message: String) -> Result<(), git2::Error> {
		let oid = self.repo.as_ref().unwrap().index()?.write_tree()?;
		let tree = self.repo.as_ref().unwrap().find_tree(oid)?;
		let parent_commit = self.repo.as_ref().unwrap().head()?.peel_to_commit()?;
		self.repo
			.as_ref()
			.unwrap()
			.commit(
				Some("HEAD"),
				&signature,
				&signature,
				commit_message.as_str(),
				&tree,
				&[&parent_commit],
			)
			.map(|_| ())
	}
}