1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
//
#[cfg(test)]
mod tests;

pub mod oreal;
pub mod otest;

use std::{
    path::Path,
    sync::{Arc, RwLock},
};

use crate as git;
use git::repository::Direction;
use git_next_config::{self as config, RemoteUrl};
pub use oreal::RealOpenRepository;
pub use otest::TestOpenRepository;

#[derive(Clone, Debug)]
pub enum OpenRepository {
    /// A real git repository.
    ///
    /// This variant is the normal implementation for use in production code.
    Real(RealOpenRepository),

    /// A real git repository, but with preprogrammed responses to network access.
    ///
    /// This variant is for use only in testing. Requests to methods
    /// that would require network access, such as to the git remote
    /// server will result in an error, unless a predefined change
    /// has been scheduled for that request.
    Test(TestOpenRepository),
}

#[cfg(not(tarpaulin_include))]
pub fn real(gix_repo: gix::Repository) -> OpenRepository {
    OpenRepository::Real(oreal::RealOpenRepository::new(Arc::new(RwLock::new(
        gix_repo.into(),
    ))))
}

#[cfg(not(tarpaulin_include))] // don't test mocks
pub fn test(
    gitdir: &config::GitDir,
    fs: kxio::fs::FileSystem,
    on_fetch: Vec<otest::OnFetch>,
    on_push: Vec<otest::OnPush>,
) -> OpenRepository {
    OpenRepository::Test(TestOpenRepository::new(gitdir, fs, on_fetch, on_push))
}

#[mockall::automock]
pub trait OpenRepositoryLike: std::fmt::Debug + Sync {
    fn duplicate(&self) -> Box<dyn OpenRepositoryLike>;
    fn remote_branches(&self) -> git::push::Result<Vec<config::BranchName>>;
    fn find_default_remote(&self, direction: Direction) -> Option<RemoteUrl>;
    fn fetch(&self) -> Result<(), git::fetch::Error>;
    fn push(
        &self,
        repo_details: &git::RepoDetails,
        branch_name: &config::BranchName,
        to_commit: &git::GitRef,
        force: &git::push::Force,
    ) -> git::push::Result<()>;

    /// List of commits in a branch, optionally up-to any specified commit.
    fn commit_log(
        &self,
        branch_name: &config::BranchName,
        find_commits: &[git::Commit],
    ) -> git::commit::log::Result<Vec<git::Commit>>;

    /// Read the contents of a file as a string.
    ///
    /// Only handles files in the root of the repo.
    fn read_file(
        &self,
        branch_name: &config::BranchName,
        file_name: &Path,
    ) -> git::file::Result<String>;
}

pub fn mock() -> Box<MockOpenRepositoryLike> {
    Box::new(MockOpenRepositoryLike::new())
}

impl std::ops::Deref for OpenRepository {
    type Target = dyn OpenRepositoryLike;

    fn deref(&self) -> &Self::Target {
        match self {
            Self::Real(real) => real,
            Self::Test(test) => test,
        }
    }
}