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>; 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, urls: Vec, push_urls: Vec, fetch_specs: Vec, push_specs: Vec, should_rewrite_urls: bool, fetch_tags: remote::fetch::Tags, repo: &'repo Repository, ) -> Result { 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: Url, should_rewrite_urls: bool, repo: &'repo Repository, ) -> Result where Url: TryInto, gix_url::parse::Error: From, { 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 { 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, 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>, 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>, 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) { 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) { 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) { 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 { 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)) }