git_next_core/git/repository/
mod.rs

1//
2use crate::{
3    git::{
4        self,
5        repository::{
6            open::{OpenRepository, OpenRepositoryLike},
7            test::TestRepository,
8        },
9        validation::remotes::validate_default_remotes,
10        RepoDetails,
11    },
12    GitDir, RemoteUrl,
13};
14
15use tracing::info;
16
17pub mod factory;
18pub mod open;
19mod test;
20
21#[allow(clippy::module_name_repetitions)]
22pub use factory::RepositoryFactory;
23
24#[cfg(test)]
25mod tests;
26
27#[derive(Clone, Debug)]
28#[allow(clippy::large_enum_variant)]
29pub enum Repository {
30    Real,
31    Test(TestRepository),
32}
33
34#[cfg(test)]
35pub(crate) const fn test(
36    fs: kxio::fs::FileSystem,
37    forge_details: crate::ForgeDetails,
38) -> TestRepository {
39    TestRepository::new(fs, vec![], vec![], forge_details)
40}
41
42/// Opens a repository, cloning if necessary
43#[tracing::instrument(skip_all)]
44#[cfg(not(tarpaulin_include))] // requires network access to either clone new and/or fetch.
45pub fn open(
46    repository_factory: &dyn factory::RepositoryFactory,
47    repo_details: &RepoDetails,
48) -> Result<Box<dyn OpenRepositoryLike>> {
49    use crate::s;
50
51    let open_repository = if repo_details.gitdir.exists() {
52        info!("Local copy found - opening...");
53        repository_factory.open(repo_details)?
54    } else {
55        info!("Local copy not found - cloning...");
56        repository_factory.git_clone(repo_details)?
57    };
58    validate_default_remotes(&*open_repository, repo_details)
59        .map_err(|e| Error::Validation(s!(e)))?;
60    Ok(open_repository)
61}
62
63#[allow(clippy::module_name_repetitions)]
64pub trait RepositoryLike {
65    /// Opens the repository.
66    ///
67    /// # Errors
68    ///
69    /// Will return an `Err` if the repository can't be opened.
70    fn open(&self, gitdir: &GitDir) -> Result<OpenRepository>;
71
72    /// Clones the git repository from the remote server.
73    ///
74    /// # Errors
75    ///
76    /// Will return an `Err` if there are any network connectivity issues
77    /// connecting with the server.
78    fn git_clone(&self, repo_details: &RepoDetails) -> Result<OpenRepository>;
79}
80
81#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
82pub enum Direction {
83    /// Push local changes to the remote.
84    Push,
85    /// Fetch changes from the remote to the local repository.
86    Fetch,
87}
88impl From<Direction> for gix::remote::Direction {
89    fn from(value: Direction) -> Self {
90        match value {
91            Direction::Push => Self::Push,
92            Direction::Fetch => Self::Fetch,
93        }
94    }
95}
96
97pub type Result<T> = core::result::Result<T, Error>;
98
99#[derive(Debug, thiserror::Error)]
100pub enum Error {
101    #[error("invalid git dir: {0}")]
102    InvalidGitDir(GitDir),
103
104    #[error("kxiofs: {0}")]
105    KxioFs(#[from] kxio::fs::Error),
106
107    #[error("io: {0}")]
108    Io(std::io::Error),
109
110    #[error("git exec wait: {0}")]
111    Wait(std::io::Error),
112
113    #[error("git exec spawn: {0}")]
114    Spawn(std::io::Error),
115
116    #[error("validation: {0}")]
117    Validation(String),
118
119    #[error("git clone: {0}")]
120    Clone(String),
121
122    #[error("git fetch: {0}")]
123    FetchError(#[from] git::fetch::Error),
124
125    #[error("open: {0}")]
126    Open(String),
127
128    #[error("git fetch: {0}")]
129    Fetch(String),
130
131    #[error("fake repository lock")]
132    FakeLock,
133
134    #[error("MismatchDefaultFetchRemote(found: {found:?}, expected: {expected:?})")]
135    MismatchDefaultFetchRemote {
136        found: Box<RemoteUrl>,
137        expected: Box<RemoteUrl>,
138    },
139}
140
141mod gix_errors {
142    #![cfg(not(tarpaulin_include))]
143    use crate::s;
144
145    // third-party library errors
146    use super::Error;
147    impl From<gix::clone::Error> for Error {
148        fn from(value: gix::clone::Error) -> Self {
149            Self::Clone(s!(value))
150        }
151    }
152    impl From<gix::open::Error> for Error {
153        fn from(value: gix::open::Error) -> Self {
154            Self::Open(s!(value))
155        }
156    }
157    impl From<gix::clone::fetch::Error> for Error {
158        fn from(value: gix::clone::fetch::Error) -> Self {
159            Self::Fetch(s!(value))
160        }
161    }
162}