#![allow(clippy::result_large_err)] use std::convert::TryInto; use crate::{bstr::BStr, config, remote, remote::find, Remote}; impl crate::Repository { /// Create a new remote available at the given `url`. /// /// It's configured to fetch included tags by default, similar to git. /// See [`with_fetch_tags(…)`][Remote::with_fetch_tags()] for a way to change it. pub fn remote_at(&self, url: Url) -> Result, remote::init::Error> where Url: TryInto, gix_url::parse::Error: From, { Remote::from_fetch_url(url, true, self) } /// Create a new remote available at the given `url` similarly to [`remote_at()`][crate::Repository::remote_at()], /// but don't rewrite the url according to rewrite rules. /// This eliminates a failure mode in case the rewritten URL is faulty, allowing to selectively [apply rewrite /// rules][Remote::rewrite_urls()] later and do so non-destructively. pub fn remote_at_without_url_rewrite(&self, url: Url) -> Result, remote::init::Error> where Url: TryInto, gix_url::parse::Error: From, { Remote::from_fetch_url(url, false, self) } /// Find the remote with the given `name_or_url` or report an error, similar to [`try_find_remote(…)`][Self::try_find_remote()]. /// /// Note that we will obtain remotes only if we deem them [trustworthy][crate::open::Options::filter_config_section()]. pub fn find_remote<'a>(&self, name_or_url: impl Into<&'a BStr>) -> Result, find::existing::Error> { let name_or_url = name_or_url.into(); Ok(self .try_find_remote(name_or_url) .ok_or_else(|| find::existing::Error::NotFound { name: name_or_url.into(), })??) } /// Find the default remote as configured, or `None` if no such configuration could be found. /// /// See [remote_default_name()][Self::remote_default_name()] for more information on the `direction` parameter. pub fn find_default_remote( &self, direction: remote::Direction, ) -> Option, find::existing::Error>> { self.remote_default_name(direction) .map(|name| self.find_remote(name.as_ref())) } /// Find the remote with the given `name_or_url` or return `None` if it doesn't exist, for the purpose of fetching or pushing /// data to a remote. /// /// There are various error kinds related to partial information or incorrectly formatted URLs or ref-specs. /// Also note that the created `Remote` may have neither fetch nor push ref-specs set at all. /// /// Note that ref-specs are de-duplicated right away which may change their order. This doesn't affect matching in any way /// as negations/excludes are applied after includes. /// /// We will only include information if we deem it [trustworthy][crate::open::Options::filter_config_section()]. pub fn try_find_remote<'a>(&self, name_or_url: impl Into<&'a BStr>) -> Option, find::Error>> { self.try_find_remote_inner(name_or_url, true) } /// Similar to [try_find_remote()][Self::try_find_remote()], but removes a failure mode if rewritten URLs turn out to be invalid /// as it skips rewriting them. /// Use this in conjunction with [`Remote::rewrite_urls()`] to non-destructively apply the rules and keep the failed urls unchanged. pub fn try_find_remote_without_url_rewrite<'a>( &self, name_or_url: impl Into<&'a BStr>, ) -> Option, find::Error>> { self.try_find_remote_inner(name_or_url, false) } fn try_find_remote_inner<'a>( &self, name_or_url: impl Into<&'a BStr>, rewrite_urls: bool, ) -> Option, find::Error>> { fn config_spec( specs: Vec>, name_or_url: &BStr, key: &'static config::tree::keys::Any, op: gix_refspec::parse::Operation, ) -> Result, find::Error> { let kind = key.name; specs .into_iter() .map(|spec| { key.try_into_refspec(spec, op).map_err(|err| find::Error::RefSpec { remote_name: name_or_url.into(), kind, source: err, }) }) .collect::, _>>() .map(|mut specs| { specs.sort(); specs.dedup(); specs }) } let mut filter = self.filter_config_section(); let name_or_url = name_or_url.into(); let mut config_url = |key: &'static config::tree::keys::Url, kind: &'static str| { self.config .resolved .string_filter("remote", Some(name_or_url), key.name, &mut filter) .map(|url| { key.try_into_url(url).map_err(|err| find::Error::Url { kind, remote_name: name_or_url.into(), source: err, }) }) }; let url = config_url(&config::tree::Remote::URL, "fetch"); let push_url = config_url(&config::tree::Remote::PUSH_URL, "push"); let config = &self.config.resolved; let fetch_specs = config .strings_filter("remote", Some(name_or_url), "fetch", &mut filter) .map(|specs| { config_spec( specs, name_or_url, &config::tree::Remote::FETCH, gix_refspec::parse::Operation::Fetch, ) }); let push_specs = config .strings_filter("remote", Some(name_or_url), "push", &mut filter) .map(|specs| { config_spec( specs, name_or_url, &config::tree::Remote::PUSH, gix_refspec::parse::Operation::Push, ) }); let fetch_tags = config .string_filter("remote", Some(name_or_url), "tagOpt", &mut filter) .map(|value| { config::tree::Remote::TAG_OPT .try_into_tag_opt(value) .map_err(Into::into) }); let fetch_tags = match fetch_tags { Some(Ok(v)) => v, Some(Err(err)) => return Some(Err(err)), None => Default::default(), }; match (url, fetch_specs, push_url, push_specs) { (None, None, None, None) => None, (None, _, None, _) => Some(Err(find::Error::UrlMissing)), (url, fetch_specs, push_url, push_specs) => { let url = match url { Some(Ok(v)) => Some(v), Some(Err(err)) => return Some(Err(err)), None => None, }; let push_url = match push_url { Some(Ok(v)) => Some(v), Some(Err(err)) => return Some(Err(err)), None => None, }; let fetch_specs = match fetch_specs { Some(Ok(v)) => v, Some(Err(err)) => return Some(Err(err)), None => Vec::new(), }; let push_specs = match push_specs { Some(Ok(v)) => v, Some(Err(err)) => return Some(Err(err)), None => Vec::new(), }; Some( Remote::from_preparsed_config( Some(name_or_url.to_owned()), url, push_url, fetch_specs, push_specs, rewrite_urls, fetch_tags, self, ) .map_err(Into::into), ) } } } }