use std::sync::Arc;
use ::url::Url;
pub use git::{CheckoutOptions, LfsFilter};
use git::{GitBinaryError, GitReference};
use sha::{GitSha, OidParseError};
pub mod credentials;
pub mod git;
pub mod resolver;
pub mod sha;
pub mod source;
pub mod url;
pub use rattler_networking::LazyClient;
pub const GIT_URL_QUERY_REV_TYPE: &str = "rev_type";
pub const GIT_SSH_CLONING_WARNING_MSG: &str = "Heads-up: use `ssh-add` if this hangs.";
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Hash, Ord)]
pub struct GitUrl {
repository: Url,
reference: GitReference,
precise: Option<GitSha>,
}
impl GitUrl {
pub fn from_reference(repository: Url, reference: GitReference) -> Self {
let precise = reference.as_sha();
Self {
repository,
reference,
precise,
}
}
pub fn from_commit(repository: Url, reference: GitReference, precise: GitSha) -> Self {
Self {
repository,
reference,
precise: Some(precise),
}
}
#[must_use]
pub fn with_precise(mut self, precise: GitSha) -> Self {
self.precise = Some(precise);
self
}
#[must_use]
pub fn with_reference(mut self, reference: GitReference) -> Self {
self.reference = reference;
self
}
pub fn repository(&self) -> &Url {
&self.repository
}
pub fn reference(&self) -> &GitReference {
&self.reference
}
pub fn precise(&self) -> Option<GitSha> {
self.precise
}
}
impl TryFrom<Url> for GitUrl {
type Error = OidParseError;
fn try_from(url: Url) -> Result<Self, Self::Error> {
let mut url = if url.scheme().starts_with("git+") {
let url_as_str = &url.as_str()[4..];
Url::parse(url_as_str).expect("url should be valid")
} else {
url
};
url.set_fragment(None);
let mut reference = GitReference::DefaultBranch;
if let Some((prefix, suffix)) = url
.path()
.rsplit_once('@')
.map(|(prefix, suffix)| (prefix.to_string(), suffix.to_string()))
{
if let Some((_, rev_type)) = url
.query_pairs()
.find(|(key, _)| key == GIT_URL_QUERY_REV_TYPE)
{
match rev_type.into_owned().as_str() {
"tag" => reference = GitReference::Tag(suffix),
"branch" => reference = GitReference::Branch(suffix),
"rev" => reference = GitReference::from_rev(suffix),
_ => return Err(OidParseError::UrlParse(url.to_string())),
}
} else {
reference = GitReference::from_rev(suffix);
}
url.set_path(&prefix);
}
url.set_query(None);
Ok(Self::from_reference(url, reference))
}
}
impl From<GitUrl> for Url {
fn from(git: GitUrl) -> Self {
let mut url = git.repository;
if let Some(precise) = git.precise {
url.set_path(&format!("{}@{}", url.path(), precise));
} else {
match git.reference {
GitReference::Branch(rev)
| GitReference::Tag(rev)
| GitReference::ShortCommit(rev)
| GitReference::BranchOrTag(rev)
| GitReference::NamedRef(rev)
| GitReference::FullCommit(rev)
| GitReference::BranchOrTagOrCommit(rev) => {
url.set_path(&format!("{}@{}", url.path(), rev));
}
GitReference::DefaultBranch => {}
}
}
url
}
}
impl std::fmt::Display for GitUrl {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.repository)
}
}
pub trait Reporter: Send + Sync {
fn on_checkout_start(&self, url: &Url, rev: &str) -> usize;
fn on_checkout_complete(&self, url: &Url, rev: &str, index: usize);
}
#[derive(Debug, Clone, thiserror::Error)]
pub enum GitError {
#[error(transparent)]
GitBinary(#[from] GitBinaryError),
#[error(transparent)]
Io(Arc<std::io::Error>),
#[error(transparent)]
FromUtf8(#[from] std::string::FromUtf8Error),
#[error(transparent)]
OidParse(#[from] OidParseError),
#[error("failed to fetch {0}: {1}")]
Fetch(String, String),
#[error("`git {0}` failed: {1}")]
Command(String, String),
#[error("could not find a branch, tag, or commit named `{reference}` in `{repository}`")]
ReferenceNotFound {
reference: String,
repository: String,
#[source]
source: Box<GitError>,
},
#[error("failed to fetch LFS objects for {0}: {1}")]
LfsFetch(String, String),
#[error(transparent)]
UrlParse(#[from] ::url::ParseError),
#[error("could not transform original url {0} into a git url: {1}")]
GitUrlFormat(String, String),
#[error(transparent)]
ReqwestMiddleware(Arc<reqwest_middleware::Error>),
#[error(transparent)]
Reqwest(Arc<reqwest::Error>),
#[error(transparent)]
Join(Arc<tokio::task::JoinError>),
#[error("corrupted or invalid git repository at {0}")]
InvalidRepository(std::path::PathBuf),
#[error("failed to set submodule url for {0}: {1}")]
SubmoduleUrl(String, String),
}
impl From<std::io::Error> for GitError {
fn from(err: std::io::Error) -> Self {
Self::Io(Arc::new(err))
}
}
impl From<reqwest_middleware::Error> for GitError {
fn from(err: reqwest_middleware::Error) -> Self {
Self::ReqwestMiddleware(Arc::new(err))
}
}
impl From<reqwest::Error> for GitError {
fn from(err: reqwest::Error) -> Self {
Self::Reqwest(Arc::new(err))
}
}
impl From<tokio::task::JoinError> for GitError {
fn from(err: tokio::task::JoinError) -> Self {
Self::Join(Arc::new(err))
}
}