use crate::{self as git, repository::OpenRepositoryLike};
use derive_more::{Constructor, Deref};
use git_next_config::{self as config, RemoteUrl};
use std::{
path::Path,
sync::{Arc, RwLock},
};
pub type OnFetchFn =
fn(&config::RepoBranches, &config::GitDir, &kxio::fs::FileSystem) -> git::fetch::Result<()>;
#[derive(Clone, Debug, Constructor)]
pub struct OnFetch {
repo_branches: config::RepoBranches,
gitdir: config::GitDir,
fs: kxio::fs::FileSystem,
action: OnFetchFn,
}
impl OnFetch {
pub fn invoke(&self) -> git::fetch::Result<()> {
(self.action)(&self.repo_branches, &self.gitdir, &self.fs)
}
}
pub type OnPushFn = fn(
&git::RepoDetails,
&config::BranchName,
&git::GitRef,
&git::push::Force,
&config::RepoBranches,
&config::GitDir,
&kxio::fs::FileSystem,
) -> git::push::Result<()>;
#[derive(Clone, Debug, Constructor)]
pub struct OnPush {
repo_branches: config::RepoBranches,
gitdir: config::GitDir,
fs: kxio::fs::FileSystem,
action: OnPushFn,
}
impl OnPush {
pub fn invoke(
&self,
repo_details: &git::RepoDetails,
branch_name: &config::BranchName,
to_commit: &git::GitRef,
force: &git::push::Force,
) -> git::push::Result<()> {
(self.action)(
repo_details,
branch_name,
to_commit,
force,
&self.repo_branches,
&self.gitdir,
&self.fs,
)
}
}
#[derive(Clone, Debug)]
pub struct TestOpenRepository {
on_fetch: Vec<OnFetch>,
fetch_counter: Arc<RwLock<usize>>,
on_push: Vec<OnPush>,
push_counter: Arc<RwLock<usize>>,
real: git::repository::RealOpenRepository,
}
#[cfg(not(tarpaulin_include))]
impl git::repository::OpenRepositoryLike for TestOpenRepository {
fn remote_branches(&self) -> git::push::Result<Vec<config::BranchName>> {
self.real.remote_branches()
}
fn find_default_remote(&self, direction: git::repository::Direction) -> Option<RemoteUrl> {
self.real.find_default_remote(direction)
}
fn fetch(&self) -> Result<(), git::fetch::Error> {
let i: usize = *self
.fetch_counter
.read()
.map_err(|_| git::fetch::Error::Lock)?
.deref();
println!("Fetch: {i}");
self.fetch_counter
.write()
.map_err(|_| git::fetch::Error::Lock)
.map(|mut c| *c += 1)?;
self.on_fetch.get(i).map(|f| f.invoke()).unwrap_or_else(|| {
unimplemented!("Unexpected fetch");
})
}
fn push(
&self,
repo_details: &git::RepoDetails,
branch_name: &config::BranchName,
to_commit: &git::GitRef,
force: &git::push::Force,
) -> git::push::Result<()> {
let i: usize = *self
.push_counter
.read()
.map_err(|_| git::fetch::Error::Lock)?
.deref();
println!("Push: {i}");
self.push_counter
.write()
.map_err(|_| git::fetch::Error::Lock)
.map(|mut c| *c += 1)?;
self.on_push
.get(i)
.map(|f| f.invoke(repo_details, branch_name, to_commit, force))
.unwrap_or_else(|| {
unimplemented!("Unexpected push");
})
}
fn commit_log(
&self,
branch_name: &config::BranchName,
find_commits: &[git::Commit],
) -> git::commit::log::Result<Vec<crate::Commit>> {
self.real.commit_log(branch_name, find_commits)
}
fn read_file(
&self,
branch_name: &config::BranchName,
file_name: &Path,
) -> git::file::Result<String> {
self.real.read_file(branch_name, file_name)
}
fn duplicate(&self) -> Box<dyn OpenRepositoryLike> {
Box::new(self.clone())
}
}
impl TestOpenRepository {
pub fn new(
gitdir: &config::GitDir,
fs: kxio::fs::FileSystem,
on_fetch: Vec<OnFetch>,
on_push: Vec<OnPush>,
) -> Self {
let pathbuf = fs.base().join(gitdir.to_path_buf());
#[allow(clippy::expect_used)]
let gix = gix::init(pathbuf).expect("git init");
Self::write_origin(gitdir, &fs);
Self {
on_fetch,
fetch_counter: Arc::new(RwLock::new(0)),
on_push,
push_counter: Arc::new(RwLock::new(0)),
real: git::repository::RealOpenRepository::new(Arc::new(RwLock::new(gix.into()))),
}
}
fn write_origin(gitdir: &config::GitDir, fs: &kxio::fs::FileSystem) {
let config_file = fs.base().join(gitdir.to_path_buf()).join(".git/config");
#[allow(clippy::expect_used)]
let contents = fs
.file_read_to_string(&config_file)
.expect("read original .git/config");
let updated_contents = format!(
r#"{contents}
[remote "origin"]
url = git@foo.example,net
fetch = +refs/heads/*:refs/remotes/origin/*
"#
);
#[allow(clippy::expect_used)]
fs.file_write(&config_file, &updated_contents)
.expect("write updated .git/config")
}
}