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
|
use bstr::BString;
/// Indicates key or values contain errors that can't be encoded.
#[derive(Debug, thiserror::Error)]
#[allow(missing_docs)]
pub enum Error {
#[error("{key:?}={value:?} must not contain null bytes or newlines neither in key nor in value.")]
Encoding { key: String, value: BString },
}
mod access {
use bstr::BString;
use crate::protocol::Context;
impl Context {
/// Convert all relevant fields into a URL for consumption.
pub fn to_url(&self) -> Option<BString> {
use bstr::{ByteSlice, ByteVec};
let mut buf: BString = self.protocol.clone()?.into();
buf.push_str(b"://");
if let Some(user) = &self.username {
buf.push_str(user);
buf.push(b'@');
}
if let Some(host) = &self.host {
buf.push_str(host);
}
if let Some(path) = &self.path {
if !path.starts_with_str("/") {
buf.push(b'/');
}
buf.push_str(path);
}
buf.into()
}
/// Compute a prompt to obtain the given value.
pub fn to_prompt(&self, field: &str) -> String {
match self.to_url() {
Some(url) => format!("{field} for {url}: "),
None => format!("{field}: "),
}
}
}
}
mod mutate {
use bstr::ByteSlice;
use crate::{protocol, protocol::Context};
/// In-place mutation
impl Context {
/// Destructure the url at our `url` field into parts like protocol, host, username and path and store
/// them in our respective fields. If `use_http_path` is set, http paths are significant even though
/// normally this isn't the case.
#[allow(clippy::result_large_err)]
pub fn destructure_url_in_place(&mut self, use_http_path: bool) -> Result<&mut Self, protocol::Error> {
let url = gix_url::parse(self.url.as_ref().ok_or(protocol::Error::UrlMissing)?.as_ref())?;
self.protocol = Some(url.scheme.as_str().into());
self.username = url.user().map(ToOwned::to_owned);
self.host = url.host().map(ToOwned::to_owned).map(|mut host| {
if let Some(port) = url.port {
use std::fmt::Write;
write!(host, ":{port}").expect("infallible");
}
host
});
if !matches!(url.scheme, gix_url::Scheme::Http | gix_url::Scheme::Https) || use_http_path {
let path = url.path.trim_with(|b| b == '/');
self.path = (!path.is_empty()).then(|| path.into());
}
Ok(self)
}
}
}
mod serde;
pub use self::serde::decode;
|