git-next-core 0.14.1

core for git-next, the trunk-based development manager
Documentation
//
use crate::{
    git::{
        self,
        repository::{
            open::{OpenRepository, OpenRepositoryLike},
            test::TestRepository,
        },
        validation::remotes::validate_default_remotes,
        RepoDetails,
    },
    GitDir, RemoteUrl,
};

use tracing::info;

pub mod factory;
pub mod open;
mod test;

#[allow(clippy::module_name_repetitions)]
pub use factory::RepositoryFactory;

#[cfg(test)]
mod tests;

#[derive(Clone, Debug)]
#[allow(clippy::large_enum_variant)]
pub enum Repository {
    Real,
    Test(TestRepository),
}

#[cfg(test)]
pub(crate) const fn test(
    fs: kxio::fs::FileSystem,
    forge_details: crate::ForgeDetails,
) -> TestRepository {
    TestRepository::new(fs, vec![], vec![], forge_details)
}

/// Opens a repository, cloning if necessary
#[tracing::instrument(skip_all)]
#[cfg(not(tarpaulin_include))] // requires network access to either clone new and/or fetch.
pub fn open(
    repository_factory: &dyn factory::RepositoryFactory,
    repo_details: &RepoDetails,
) -> Result<Box<dyn OpenRepositoryLike>> {
    use crate::s;

    let open_repository = if repo_details.gitdir.exists() {
        info!("Local copy found - opening...");
        repository_factory.open(repo_details)?
    } else {
        info!("Local copy not found - cloning...");
        repository_factory.git_clone(repo_details)?
    };
    validate_default_remotes(&*open_repository, repo_details)
        .map_err(|e| Error::Validation(s!(e)))?;
    Ok(open_repository)
}

#[allow(clippy::module_name_repetitions)]
pub trait RepositoryLike {
    /// Opens the repository.
    ///
    /// # Errors
    ///
    /// Will return an `Err` if the repository can't be opened.
    fn open(&self, gitdir: &GitDir) -> Result<OpenRepository>;

    /// Clones the git repository from the remote server.
    ///
    /// # Errors
    ///
    /// Will return an `Err` if there are any network connectivity issues
    /// connecting with the server.
    fn git_clone(&self, repo_details: &RepoDetails) -> Result<OpenRepository>;
}

#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub enum Direction {
    /// Push local changes to the remote.
    Push,
    /// Fetch changes from the remote to the local repository.
    Fetch,
}
impl From<Direction> for gix::remote::Direction {
    fn from(value: Direction) -> Self {
        match value {
            Direction::Push => Self::Push,
            Direction::Fetch => Self::Fetch,
        }
    }
}

pub type Result<T> = core::result::Result<T, Error>;

#[derive(Debug, thiserror::Error)]
pub enum Error {
    #[error("invalid git dir: {0}")]
    InvalidGitDir(GitDir),

    #[error("kxiofs: {0}")]
    KxioFs(#[from] kxio::fs::Error),

    #[error("io: {0}")]
    Io(std::io::Error),

    #[error("git exec wait: {0}")]
    Wait(std::io::Error),

    #[error("git exec spawn: {0}")]
    Spawn(std::io::Error),

    #[error("validation: {0}")]
    Validation(String),

    #[error("git clone: {0}")]
    Clone(String),

    #[error("git fetch: {0}")]
    FetchError(#[from] git::fetch::Error),

    #[error("open: {0}")]
    Open(String),

    #[error("git fetch: {0}")]
    Fetch(String),

    #[error("fake repository lock")]
    FakeLock,

    #[error("MismatchDefaultFetchRemote(found: {found:?}, expected: {expected:?})")]
    MismatchDefaultFetchRemote {
        found: Box<RemoteUrl>,
        expected: Box<RemoteUrl>,
    },
}

mod gix_errors {
    #![cfg(not(tarpaulin_include))]
    use crate::s;

    // third-party library errors
    use super::Error;
    impl From<gix::clone::Error> for Error {
        fn from(value: gix::clone::Error) -> Self {
            Self::Clone(s!(value))
        }
    }
    impl From<gix::open::Error> for Error {
        fn from(value: gix::open::Error) -> Self {
            Self::Open(s!(value))
        }
    }
    impl From<gix::clone::fetch::Error> for Error {
        fn from(value: gix::clone::fetch::Error) -> Self {
            Self::Fetch(s!(value))
        }
    }
}