use gix_error::ResultExt;
use gix_refspec::RefSpec;
use crate::{Error, Remote, Repository, Result, config, remote};
use crate::bstr::BString;
type UrlAliases = Vec<Option<gix_url::Url>>;
type UrlRewriteAliases = (UrlAliases, UrlAliases, UrlAliases);
impl<'repo> Remote<'repo> {
#[expect(clippy::too_many_arguments)]
pub(crate) fn from_preparsed_config(
name_or_url: Option<BString>,
urls: Vec<gix_url::Url>,
push_urls: Vec<gix_url::Url>,
fetch_specs: Vec<RefSpec>,
push_specs: Vec<RefSpec>,
should_rewrite_urls: bool,
fetch_tags: remote::fetch::Tags,
repo: &'repo Repository,
) -> Result<Self> {
let (url_aliases, url_push_aliases, push_url_aliases) = if should_rewrite_urls {
rewrite_urls(&repo.config, &urls, &push_urls)
} else {
Ok((
vec![None; urls.len()],
vec![None; urls.len()],
vec![None; push_urls.len()],
))
}?;
Ok(Remote {
name: name_or_url.map(Into::into),
urls,
url_aliases,
url_push_aliases,
push_urls,
push_url_aliases,
fetch_specs,
push_specs,
fetch_tags,
repo,
})
}
pub(crate) fn from_fetch_url<Url, E>(url: Url, should_rewrite_urls: bool, repo: &'repo Repository) -> Result<Self>
where
Url: TryInto<gix_url::Url, Error = E>,
E: std::error::Error + Send + Sync + 'static,
{
Self::from_fetch_url_inner(url.try_into().or_error()?, should_rewrite_urls, repo)
}
fn from_fetch_url_inner(url: gix_url::Url, should_rewrite_urls: bool, repo: &'repo Repository) -> Result<Self> {
let urls = vec![url];
let (url_aliases, url_push_aliases, _) = if should_rewrite_urls {
rewrite_urls(&repo.config, &urls, &[])
} else {
Ok((vec![None; urls.len()], vec![None; urls.len()], Vec::new()))
}?;
Ok(Remote {
name: None,
urls,
url_aliases,
url_push_aliases,
push_urls: Vec::new(),
push_url_aliases: Vec::new(),
fetch_specs: Vec::new(),
push_specs: Vec::new(),
fetch_tags: Default::default(),
repo,
})
}
}
pub(crate) fn rewrite_url(
config: &config::Cache,
url: &gix_url::Url,
direction: remote::Direction,
error_kind: remote::Direction,
) -> Result<Option<gix_url::Url>> {
config
.url_rewrite()
.longest(url, direction)
.map(|url| {
gix_url::parse(&url).or_raise(|| {
let kind = match error_kind {
remote::Direction::Fetch => "fetch",
remote::Direction::Push => "push",
};
gix_error::message!("The rewritten {kind} url {url:?} failed to parse")
})
})
.transpose()
}
pub(crate) fn rewrite_url_aliases(
config: &config::Cache,
urls: &[gix_url::Url],
direction: remote::Direction,
) -> Result<Vec<Option<gix_url::Url>>> {
rewrite_url_aliases_with_error_kind(config, urls, direction, direction)
}
pub(crate) fn rewrite_url_aliases_with_error_kind(
config: &config::Cache,
urls: &[gix_url::Url],
direction: remote::Direction,
error_kind: remote::Direction,
) -> Result<Vec<Option<gix_url::Url>>> {
urls.iter()
.map(|url| rewrite_url(config, url, direction, error_kind))
.collect()
}
pub(crate) fn rewrite_url_aliases_non_destructive(
config: &config::Cache,
urls: &[gix_url::Url],
direction: remote::Direction,
) -> (Vec<Option<gix_url::Url>>, Option<Error>) {
rewrite_url_aliases_non_destructive_with_error_kind(config, urls, direction, direction)
}
pub(crate) fn rewrite_url_aliases_non_destructive_with_error_kind(
config: &config::Cache,
urls: &[gix_url::Url],
direction: remote::Direction,
error_kind: remote::Direction,
) -> (Vec<Option<gix_url::Url>>, Option<Error>) {
let mut first_error = None;
let aliases = urls
.iter()
.map(|url| match rewrite_url(config, url, direction, error_kind) {
Ok(alias) => alias,
Err(err) => {
first_error.get_or_insert(err);
None
}
})
.collect();
(aliases, first_error)
}
pub(crate) fn rewrite_url_aliases_with_fallback_non_destructive(
config: &config::Cache,
urls: &[gix_url::Url],
direction: remote::Direction,
fallback: remote::Direction,
) -> (Vec<Option<gix_url::Url>>, Option<Error>) {
let mut first_error = None;
let aliases = urls
.iter()
.map(|url| match rewrite_url(config, url, direction, direction) {
Ok(Some(alias)) => Some(alias),
Ok(None) => match rewrite_url(config, url, fallback, direction) {
Ok(alias) => alias,
Err(err) => {
first_error.get_or_insert(err);
None
}
},
Err(err) => {
first_error.get_or_insert(err);
None
}
})
.collect();
(aliases, first_error)
}
pub(crate) fn rewrite_urls(
config: &config::Cache,
urls: &[gix_url::Url],
push_urls: &[gix_url::Url],
) -> Result<UrlRewriteAliases> {
let url_aliases = rewrite_url_aliases(config, urls, remote::Direction::Fetch)?;
let url_push_aliases = if push_urls.is_empty() {
rewrite_url_aliases_with_fallback_non_destructive(
config,
urls,
remote::Direction::Push,
remote::Direction::Fetch,
)
.0
} else {
vec![None; urls.len()]
};
let push_url_aliases =
rewrite_url_aliases_with_error_kind(config, push_urls, remote::Direction::Fetch, remote::Direction::Push)?;
Ok((url_aliases, url_push_aliases, push_url_aliases))
}