1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
use crate::client::Transport;
use quick_error::quick_error;
quick_error! {
#[derive(Debug)]
#[allow(missing_docs)]
pub enum Error {
Url(err: git_url::parse::Error) {
display("The URL could not be parsed")
from()
source(err)
}
PathConversion(err: bstr::Utf8Error) {
display("The git repository paths could not be converted to UTF8")
from()
source(err)
}
Connection(err: Box<dyn std::error::Error + Send + Sync>) {
display("connection failed")
from()
source(&**err)
}
UnsupportedUrlTokens(url: bstr::BString, scheme: git_url::Scheme) {
display("The url '{}' contains information that would not be used by the '{}' protocol", url, scheme)
}
UnsupportedScheme(scheme: git_url::Scheme) {
display("The '{}' protocol is currently unsupported", scheme)
}
#[cfg(not(feature = "http-client-curl"))]
CompiledWithoutHttp(scheme: git_url::Scheme) {
display("'{}' is not compiled in. Compile with the 'http-client-curl' cargo feature", scheme)
}
}
}
mod box_impl {
use crate::{
client::{self, Error, Identity, MessageKind, RequestWriter, SetServiceResponse, WriteMode},
Protocol, Service,
};
use std::ops::{Deref, DerefMut};
impl<T: client::Transport + ?Sized> client::Transport for Box<T> {
fn handshake(&mut self, service: Service) -> Result<SetServiceResponse<'_>, Error> {
self.deref_mut().handshake(service)
}
fn set_identity(&mut self, identity: Identity) -> Result<(), Error> {
self.deref_mut().set_identity(identity)
}
fn request(&mut self, write_mode: WriteMode, on_into_read: MessageKind) -> Result<RequestWriter<'_>, Error> {
self.deref_mut().request(write_mode, on_into_read)
}
fn close(&mut self) -> Result<(), Error> {
self.deref_mut().close()
}
fn to_url(&self) -> String {
self.deref().to_url()
}
fn desired_protocol_version(&self) -> Protocol {
self.deref().desired_protocol_version()
}
fn is_stateful(&self) -> bool {
self.deref().is_stateful()
}
}
}
pub fn connect(url: &[u8], desired_version: crate::Protocol) -> Result<Box<dyn Transport>, Error> {
let urlb = url;
let url = git_url::parse(urlb)?;
Ok(match url.scheme {
git_url::Scheme::Radicle => return Err(Error::UnsupportedScheme(url.scheme)),
git_url::Scheme::File => {
if url.user.is_some() || url.host.is_some() || url.port.is_some() {
return Err(Error::UnsupportedUrlTokens(urlb.into(), url.scheme));
}
Box::new(
crate::client::file::connect(url.path, desired_version)
.map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)?,
)
}
git_url::Scheme::Ssh => Box::new(
crate::client::ssh::connect(
&url.host.as_ref().expect("host is present in url"),
url.path,
desired_version,
url.user.as_deref(),
url.port,
)
.map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)?,
),
git_url::Scheme::Git => {
if url.user.is_some() {
return Err(Error::UnsupportedUrlTokens(urlb.into(), url.scheme));
}
Box::new(
crate::client::git::connect(
&url.host.as_ref().expect("host is present in url"),
url.path,
desired_version,
url.port,
)
.map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)?,
)
}
#[cfg(not(feature = "http-client-curl"))]
git_url::Scheme::Https | git_url::Scheme::Http => return Err(Error::CompiledWithoutHttp(url.scheme)),
#[cfg(feature = "http-client-curl")]
git_url::Scheme::Https | git_url::Scheme::Http => {
use bstr::ByteSlice;
Box::new(
crate::client::http::connect(urlb.to_str()?, desired_version)
.map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)?,
)
}
})
}