use crate::error::{ResultExt, message};
#[cfg(feature = "async-network-client")]
use gix_transport::client::async_io::Transport;
#[cfg(feature = "blocking-network-client")]
use gix_transport::client::blocking_io::Transport;
use gix_utils::progress::Progress;
use crate::{
Result,
remote::{Connection, connection::ConnectionDetached, fetch},
};
#[derive(Debug, Clone)]
pub struct Options {
pub prefix_from_spec_as_filter_on_remote: bool,
pub handshake_parameters: Vec<(String, Option<String>)>,
pub extra_refspecs: Vec<gix_refspec::RefSpec>,
}
impl Default for Options {
fn default() -> Self {
Options {
prefix_from_spec_as_filter_on_remote: true,
handshake_parameters: Vec::new(),
extra_refspecs: Vec::new(),
}
}
}
impl<T> Connection<'_, '_, '_, T>
where
T: Transport,
{
#[gix_protocol::bisync::bisync]
pub async fn ref_map(
self,
progress: impl Progress,
options: Options,
) -> Result<(fetch::RefMap, gix_protocol::Handshake)> {
let repo = self.remote.repo;
self.into_detached().ref_map(repo, progress, options).await
}
}
impl<T> ConnectionDetached<'_, T>
where
T: Transport,
{
#[gix_protocol::bisync::bisync]
pub(crate) async fn ref_map(
mut self,
repo: &crate::Repository,
progress: impl Progress,
options: Options,
) -> Result<(fetch::RefMap, gix_protocol::Handshake)> {
let refmap = self.ref_map_by_ref(repo, progress, options).await?;
let handshake = self
.handshake
.expect("refmap always performs handshake and stores it if it succeeds");
Ok((refmap, handshake))
}
#[gix_protocol::bisync::bisync]
pub(crate) async fn ref_map_by_ref(
&mut self,
repo: &crate::Repository,
mut progress: impl Progress,
Options {
prefix_from_spec_as_filter_on_remote,
handshake_parameters,
mut extra_refspecs,
}: Options,
) -> Result<fetch::RefMap> {
let _span = gix_trace::coarse!("remote::Connection::ref_map()");
if let Some(tag_spec) = self.remote.fetch_tags.to_refspec().map(|spec| spec.to_owned())
&& !extra_refspecs.contains(&tag_spec)
{
extra_refspecs.push(tag_spec);
}
let mut credentials_storage;
let url = self.transport.inner.to_url();
let authenticate = match self.authenticate.as_mut() {
Some(f) => f,
None => {
credentials_storage = self.configured_credentials_for_current_url(repo);
&mut credentials_storage
}
};
if self.transport_options.is_none() {
self.transport_options = repo
.transport_options(url.as_ref(), self.remote.name().map(crate::remote::Name::as_bstr))
.or_raise(|| message!("Failed to configure the transport before connecting to {url:?}"))?;
}
if let Some(config) = self.transport_options.as_ref() {
self.transport
.inner
.configure(&**config)
.or_raise(|| message("Failed to configure the transport layer"))?;
}
let mut handshake = gix_protocol::handshake(
&mut self.transport.inner,
gix_transport::Service::UploadPack,
authenticate,
handshake_parameters,
&mut progress,
)
.await?;
let context = fetch::refmap::init::Context {
fetch_refspecs: self.remote.fetch_specs.clone(),
extra_refspecs,
};
let fetch_refmap = handshake.prepare_lsrefs_or_extract_refmap(
repo.config.user_agent_tuple(),
prefix_from_spec_as_filter_on_remote,
context,
)?;
#[cfg(feature = "async-network-client")]
let ref_map = fetch_refmap
.fetch_async(progress, &mut self.transport.inner, self.trace)
.await?;
#[cfg(feature = "blocking-network-client")]
let ref_map = fetch_refmap.fetch_blocking(progress, &mut self.transport.inner, self.trace)?;
self.handshake = Some(handshake);
Ok(ref_map)
}
}