use gix_refspec::RefSpec; use crate::{Remote, Repository, config, remote}; mod error { use crate::bstr::BString; /// The error returned by [`Repository::remote_at(…)`][crate::Repository::remote_at()]. #[derive(Debug, thiserror::Error)] #[expect(missing_docs)] pub enum Error { #[error(transparent)] Url(#[from] gix_url::parse::Error), #[error("The rewritten {kind} url {rewritten_url:?} failed to parse")] RewrittenUrlInvalid { kind: &'static str, rewritten_url: BString, source: gix_url::parse::Error, }, }}pub use error::Error; use crate::bstr::BString; type UrlAliases = Vec<Option<gix_url::Url>>;type UrlRewriteAliases = (UrlAliases, UrlAliases, UrlAliases); /// Initializationimpl<'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, Error> { 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, Error> where Url: TryInto<gix_url::Url, Error = E>, gix_url::parse::Error: From<E>, { Self::from_fetch_url_inner( url.try_into().map_err(|err| Error::Url(err.into()))?, should_rewrite_urls, repo, ) } fn from_fetch_url_inner( url: gix_url::Url, should_rewrite_urls: bool, repo: &'repo Repository, ) -> Result<Self, Error> { 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>, Error> { config .url_rewrite() .longest(url, direction) .map(|url| { gix_url::parse(url.as_ref()).map_err(|err| Error::RewrittenUrlInvalid { kind: match error_kind { remote::Direction::Fetch => "fetch", remote::Direction::Push => "push", }, source: err, rewritten_url: url, }) }) .transpose()} pub(crate) fn rewrite_url_aliases( config: &config::Cache, urls: &[gix_url::Url], direction: remote::Direction,) -> Result<Vec<Option<gix_url::Url>>, Error> { 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>>, Error> { 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, Error> { 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))}
use gix_refspec::RefSpec;
use crate::{Remote, Repository, config, remote};
mod error {
use crate::bstr::BString;
/// The error returned by [`Repository::remote_at(…)`][crate::Repository::remote_at()].
#[derive(Debug, thiserror::Error)]
#[expect(missing_docs)]
pub enum Error {
#[error(transparent)]
Url(#[from] gix_url::parse::Error),
#[error("The rewritten {kind} url {rewritten_url:?} failed to parse")]
RewrittenUrlInvalid {
kind: &'static str,
rewritten_url: BString,
source: gix_url::parse::Error,
},
}
}
pub use error::Error;
use crate::bstr::BString;
type UrlAliases = Vec<Option<gix_url::Url>>;
type UrlRewriteAliases = (UrlAliases, UrlAliases, UrlAliases);
/// Initialization
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, Error> {
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, Error>
where
Url: TryInto<gix_url::Url, Error = E>,
gix_url::parse::Error: From<E>,
{
Self::from_fetch_url_inner(
url.try_into().map_err(|err| Error::Url(err.into()))?,
should_rewrite_urls,
repo,
)
}
fn from_fetch_url_inner(
url: gix_url::Url,
should_rewrite_urls: bool,
repo: &'repo Repository,
) -> Result<Self, Error> {
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>, Error> {
config
.url_rewrite()
.longest(url, direction)
.map(|url| {
gix_url::parse(url.as_ref()).map_err(|err| Error::RewrittenUrlInvalid {
kind: match error_kind {
remote::Direction::Fetch => "fetch",
remote::Direction::Push => "push",
},
source: err,
rewritten_url: url,
})
})
.transpose()
}
pub(crate) fn rewrite_url_aliases(
config: &config::Cache,
urls: &[gix_url::Url],
direction: remote::Direction,
) -> Result<Vec<Option<gix_url::Url>>, Error> {
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>>, Error> {
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, Error> {
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))
}