Expand description
Parse the given bytes
as git url.
Note
We cannot and should never have to deal with UTF-16 encoded windows strings, so bytes input is acceptable. For file-paths, we don’t expect UTF8 encoding either.
Examples found in repository?
src/lib.rs (lines 61-72)
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 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
pub fn from_parts(
scheme: Scheme,
user: Option<String>,
host: Option<String>,
port: Option<u16>,
path: BString,
) -> Result<Self, parse::Error> {
parse(
Url {
scheme,
user,
host,
port,
path,
serialize_alternative_form: false,
}
.to_bstring()
.as_ref(),
)
}
/// Create a new instance from the given parts, which will be validated by parsing them back from its alternative form.
pub fn from_parts_as_alternative_form(
scheme: Scheme,
user: Option<String>,
host: Option<String>,
port: Option<u16>,
path: BString,
) -> Result<Self, parse::Error> {
parse(
Url {
scheme,
user,
host,
port,
path,
serialize_alternative_form: true,
}
.to_bstring()
.as_ref(),
)
}
}
/// Modification
impl Url {
/// Set the given `user`, with `None` unsetting it. Returns the previous value.
pub fn set_user(&mut self, user: Option<String>) -> Option<String> {
let prev = self.user.take();
self.user = user;
prev
}
}
/// Builder
impl Url {
/// Enable alternate serialization for this url, e.g. `file:///path` becomes `/path`.
///
/// This is automatically set correctly for parsed URLs, but can be set here for urls
/// created by constructor.
pub fn serialize_alternate_form(mut self, use_alternate_form: bool) -> Self {
self.serialize_alternative_form = use_alternate_form;
self
}
/// Turn a file url like `file://relative` into `file:///root/relative`, hence it assures the url's path component is absolute.
pub fn canonicalize(&mut self) -> Result<(), git_path::realpath::Error> {
if self.scheme == Scheme::File {
let path = git_path::from_bstr(self.path.as_ref());
let abs_path = git_path::realpath(path)?;
self.path = git_path::into_bstr(abs_path).into_owned();
}
Ok(())
}
}
/// Access
impl Url {
/// Returns the user mentioned in the url, if present.
pub fn user(&self) -> Option<&str> {
self.user.as_deref()
}
/// Returns the host mentioned in the url, if present.
pub fn host(&self) -> Option<&str> {
self.host.as_deref()
}
/// Returns true if the path portion of the url is `/`.
pub fn path_is_root(&self) -> bool {
self.path == "/"
}
/// Returns the actual or default port for use according to the url scheme.
/// Note that there may be no default port either.
pub fn port_or_default(&self) -> Option<u16> {
self.port.or_else(|| {
use Scheme::*;
Some(match self.scheme {
Http => 80,
Https => 443,
Ssh => 21,
Git => 9418,
File | Ext(_) => return None,
})
})
}
}
/// Transformation
impl Url {
/// Turn a file url like `file://relative` into `file:///root/relative`, hence it assures the url's path component is absolute.
pub fn canonicalized(&self) -> Result<Self, git_path::realpath::Error> {
let mut res = self.clone();
res.canonicalize()?;
Ok(res)
}
}
/// Serialization
impl Url {
/// Write this URL losslessly to `out`, ready to be parsed again.
pub fn write_to(&self, mut out: impl std::io::Write) -> std::io::Result<()> {
if !(self.serialize_alternative_form && (self.scheme == Scheme::File || self.scheme == Scheme::Ssh)) {
out.write_all(self.scheme.as_str().as_bytes())?;
out.write_all(b"://")?;
}
match (&self.user, &self.host) {
(Some(user), Some(host)) => {
out.write_all(user.as_bytes())?;
out.write_all(&[b'@'])?;
out.write_all(host.as_bytes())?;
}
(None, Some(host)) => {
out.write_all(host.as_bytes())?;
}
(None, None) => {}
(Some(_user), None) => unreachable!("BUG: should not be possible to have a user but no host"),
};
if let Some(port) = &self.port {
write!(&mut out, ":{}", port)?;
}
if self.serialize_alternative_form && self.scheme == Scheme::Ssh {
out.write_all(b":")?;
out.write_all(&self.path[1..])?;
} else {
out.write_all(&self.path)?;
}
Ok(())
}
/// Transform ourselves into a binary string, losslessly, or fail if the URL is malformed due to host or user parts being incorrect.
pub fn to_bstring(&self) -> bstr::BString {
let mut buf = Vec::with_capacity(
(5 + 3)
+ self.user.as_ref().map(|n| n.len()).unwrap_or_default()
+ 1
+ self.host.as_ref().map(|h| h.len()).unwrap_or_default()
+ self.port.map(|_| 5).unwrap_or_default()
+ self.path.len(),
);
self.write_to(&mut buf).expect("io cannot fail in memory");
buf.into()
}
}
/// Deserialization
impl Url {
/// Parse a URL from `bytes`
pub fn from_bytes(bytes: &BStr) -> Result<Self, parse::Error> {
parse(bytes)
}