1 //! A library implementing a URL for use in git with access to its special capabilities.
4 feature
= "document-features",
5 cfg_attr(doc
, doc
= ::document_features
::document_features
!())
7 #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
8 #![deny(rust_2018_idioms, missing_docs)]
9 #![forbid(unsafe_code)]
11 use bstr
::{BStr, BString}
;
21 pub use expand_path
::expand_path
;
24 pub use scheme
::Scheme
;
26 /// A URL with support for specialized git related capabilities.
28 /// Additionally there is support for [deserialization][Url::from_bytes()] and serialization
29 /// (_see the `Display::fmt()` implementation_).
33 /// Note that we do not support passing the password using the URL as it's likely leading to accidents.
34 #[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]
35 #[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
39 /// The user to impersonate on the remote.
41 /// The host to which to connect. Localhost is implied if `None`.
43 /// When serializing, use the alternative forms as it was parsed as such.
44 serialize_alternative_form
: bool
,
45 /// The port to use when connecting to a host. If `None`, standard ports depending on `scheme` will be used.
46 pub port
: Option
<u16>,
47 /// The path portion of the URL, usually the location of the git repository.
48 pub path
: bstr
::BString
,
53 /// Create a new instance from the given parts, which will be validated by parsing them back.
60 ) -> Result
<Self, parse
::Error
> {
68 serialize_alternative_form
: false,
75 /// Create a new instance from the given parts, which will be validated by parsing them back from its alternative form.
76 pub fn from_parts_as_alternative_form(
82 ) -> Result
<Self, parse
::Error
> {
90 serialize_alternative_form
: true,
100 /// Set the given `user`, with `None` unsetting it. Returns the previous value.
101 pub fn set_user(&mut self, user
: Option
<String
>) -> Option
<String
> {
102 let prev
= self.user
.take();
110 /// Enable alternate serialization for this url, e.g. `file:///path` becomes `/path`.
112 /// This is automatically set correctly for parsed URLs, but can be set here for urls
113 /// created by constructor.
114 pub fn serialize_alternate_form(mut self, use_alternate_form
: bool
) -> Self {
115 self.serialize_alternative_form
= use_alternate_form
;
119 /// Turn a file url like `file://relative` into `file:///root/relative`, hence it assures the url's path component is absolute.
120 pub fn canonicalize(&mut self) -> Result
<(), gix_path
::realpath
::Error
> {
121 if self.scheme
== Scheme
::File
{
122 let path
= gix_path
::from_bstr(self.path
.as_ref());
123 let abs_path
= gix_path
::realpath(path
)?
;
124 self.path
= gix_path
::into_bstr(abs_path
).into_owned();
132 /// Returns the user mentioned in the url, if present.
133 pub fn user(&self) -> Option
<&str> {
136 /// Returns the host mentioned in the url, if present.
137 pub fn host(&self) -> Option
<&str> {
140 /// Returns true if the path portion of the url is `/`.
141 pub fn path_is_root(&self) -> bool
{
144 /// Returns the actual or default port for use according to the url scheme.
145 /// Note that there may be no default port either.
146 pub fn port_or_default(&self) -> Option
<u16> {
147 self.port
.or_else(|| {
149 Some(match self.scheme
{
154 File
| Ext(_
) => return None
,
162 /// Turn a file url like `file://relative` into `file:///root/relative`, hence it assures the url's path component is absolute.
163 pub fn canonicalized(&self) -> Result
<Self, gix_path
::realpath
::Error
> {
164 let mut res
= self.clone();
172 /// Write this URL losslessly to `out`, ready to be parsed again.
173 pub fn write_to(&self, mut out
: impl std
::io
::Write
) -> std
::io
::Result
<()> {
174 if !(self.serialize_alternative_form
&& (self.scheme
== Scheme
::File
|| self.scheme
== Scheme
::Ssh
)) {
175 out
.write_all(self.scheme
.as_str().as_bytes())?
;
176 out
.write_all(b
"://")?
;
178 match (&self.user
, &self.host
) {
179 (Some(user
), Some(host
)) => {
180 out
.write_all(user
.as_bytes())?
;
181 out
.write_all(&[b'@'
])?
;
182 out
.write_all(host
.as_bytes())?
;
184 (None
, Some(host
)) => {
185 out
.write_all(host
.as_bytes())?
;
188 (Some(_user
), None
) => unreachable
!("BUG: should not be possible to have a user but no host"),
190 if let Some(port
) = &self.port
{
191 write
!(&mut out
, ":{port}")?
;
193 if self.serialize_alternative_form
&& self.scheme
== Scheme
::Ssh
{
194 out
.write_all(b
":")?
;
196 out
.write_all(&self.path
)?
;
200 /// Transform ourselves into a binary string, losslessly, or fail if the URL is malformed due to host or user parts being incorrect.
201 pub fn to_bstring(&self) -> bstr
::BString
{
202 let mut buf
= Vec
::with_capacity(
204 + self.user
.as_ref().map(|n
| n
.len()).unwrap_or_default()
206 + self.host
.as_ref().map(|h
| h
.len()).unwrap_or_default()
207 + self.port
.map(|_
| 5).unwrap_or_default()
210 self.write_to(&mut buf
).expect("io cannot fail in memory");
217 /// Parse a URL from `bytes`
218 pub fn from_bytes(bytes
: &BStr
) -> Result
<Self, parse
::Error
> {