use std::borrow::Cow;
use crate::error::{ResultExt, bail, message};
#[cfg(feature = "async-network-client")]
use gix_transport::client::async_io::{Transport, connect};
#[cfg(feature = "blocking-network-client")]
use gix_transport::client::blocking_io::{Transport, connect};
use crate::{Error, Remote, Result, config::tree::Protocol, remote::Connection};
impl<'repo> Remote<'repo> {
pub fn to_connection_with_transport<T>(&self, transport: T) -> Connection<'_, 'static, 'repo, T>
where
T: Transport,
{
let trace = self.repo.config.trace_packet();
Connection {
remote: self,
authenticate: None,
transport_options: None,
handshake: None,
transport: gix_protocol::SendFlushOnDrop::new(transport, trace),
trace,
}
}
#[cfg(any(feature = "blocking-network-client", feature = "async-network-client-async-std"))]
#[gix_protocol::bisync::bisync]
pub async fn connect(
&self,
direction: crate::remote::Direction,
) -> Result<Connection<'_, 'static, 'repo, Box<dyn Transport + Send>>> {
let (url, version) = self.sanitized_url_and_version(direction)?;
#[cfg(feature = "blocking-network-client")]
let scheme_is_ssh = url.scheme == gix_url::Scheme::Ssh;
let transport = connect::connect(
url,
connect::Options {
version,
#[cfg(feature = "blocking-network-client")]
ssh: scheme_is_ssh
.then(|| self.repo.ssh_connect_options())
.transpose()
.or_raise(|| message("Could not obtain options for connecting via ssh"))?
.unwrap_or_default(),
trace: self.repo.config.trace_packet(),
},
)
.await?;
Ok(self.to_connection_with_transport(transport))
}
pub fn sanitized_url_and_version(
&self,
direction: crate::remote::Direction,
) -> Result<(gix_url::Url, gix_protocol::transport::Protocol)> {
fn sanitize(mut url: gix_url::Url) -> Result<gix_url::Url> {
if url.scheme == gix_url::Scheme::File {
let mut dir = gix_path::to_native_path_on_windows(Cow::Borrowed(url.path.as_ref()))?;
let kind = gix_discover::is_git(dir.as_ref())
.or_else(|_| {
dir.to_mut().push(gix_discover::DOT_GIT_DIR);
gix_discover::is_git(dir.as_ref())
})
.or_raise(|| {
message!(
"Could not verify that {:?} is a valid git directory before attempting to use it",
url.to_bstring()
)
})?;
let Some(repository_path) = gix_discover::repository::Path::from_dot_git_dir(
dir.clone().into_owned(),
kind,
&gix_fs::current_dir(false).or_raise(|| message("Could not obtain the current directory"))?,
) else {
return Err(message("Could not access remote repository")
.with_input(gix_path::into_bstr(dir.into_owned())?.into_owned())
.validation_error());
};
let (git_dir, _work_dir) = repository_path.into_repository_and_work_tree_directories();
url.path = gix_path::into_bstr(git_dir)?.into_owned();
}
Ok(url)
}
let version = crate::config::tree::Protocol::VERSION
.try_into_protocol_version(self.repo.config.resolved.integer(Protocol::VERSION))?;
let url = self
.url(direction)
.ok_or_else(|| {
Error::from_error(gix_error::validation(format!(
"The {} url was missing - don't know where to establish a connection to",
direction.as_str()
)))
})?
.to_owned();
if !self.repo.config.url_scheme()?.allow(&url.scheme) {
bail!(
"Protocol {:?} is denied per configuration"
.permission_denied()
.with_input(url.to_bstring()),
url.scheme
);
}
Ok((sanitize(url)?, version))
}
}