use crate::error::{FossilError, Result};
use crate::repo::Repository;
use std::fs;
use std::path::Path;
pub struct CommitBuilder<'a> {
repo: &'a Repository,
message: Option<String>,
author: Option<String>,
parent: Option<String>,
branch: Option<String>,
files: Vec<(String, Vec<u8>)>,
is_initial: bool,
}
impl<'a> CommitBuilder<'a> {
pub(crate) fn new(repo: &'a Repository) -> Self {
Self {
repo,
message: None,
author: None,
parent: None,
branch: None,
files: Vec::new(),
is_initial: false,
}
}
pub fn message(mut self, msg: &str) -> Self {
self.message = Some(msg.to_string());
self
}
pub fn author(mut self, user: &str) -> Self {
self.author = Some(user.to_string());
self
}
pub fn parent(mut self, hash: &str) -> Self {
self.parent = Some(hash.to_string());
self
}
pub fn branch(mut self, name: &str) -> Self {
self.branch = Some(name.to_string());
self
}
pub fn file(mut self, path: &str, content: &[u8]) -> Self {
self.files.push((path.to_string(), content.to_vec()));
self
}
pub fn files(mut self, files: &[(&str, &[u8])]) -> Self {
for (path, content) in files {
self.files.push((path.to_string(), content.to_vec()));
}
self
}
pub fn file_from_path<P: AsRef<Path>>(mut self, repo_path: &str, disk_path: P) -> Result<Self> {
let content = fs::read(disk_path.as_ref())?;
self.files.push((repo_path.to_string(), content));
Ok(self)
}
pub fn initial(mut self) -> Self {
self.is_initial = true;
self.branch = Some("trunk".to_string());
self
}
pub fn execute(self) -> Result<String> {
let message = self.message.ok_or_else(|| {
FossilError::InvalidArtifact("commit message is required".to_string())
})?;
let author = self
.author
.ok_or_else(|| FossilError::InvalidArtifact("commit author is required".to_string()))?;
let files_refs: Vec<(&str, &[u8])> = self
.files
.iter()
.map(|(p, c)| (p.as_str(), c.as_slice()))
.collect();
self.repo.commit_internal(
&files_refs,
&message,
&author,
self.parent.as_deref(),
self.branch.as_deref(),
)
}
}