gix_credentials/protocol/context/
mod.rs

1use bstr::BString;
2
3/// Indicates key or values contain errors that can't be encoded.
4#[derive(Debug, thiserror::Error)]
5#[allow(missing_docs)]
6pub enum Error {
7    #[error("{key:?}={value:?} must not contain null bytes or newlines neither in key nor in value.")]
8    Encoding { key: String, value: BString },
9}
10
11mod access {
12    use bstr::BString;
13
14    use crate::protocol::Context;
15
16    impl Context {
17        /// Convert all relevant fields into a URL for consumption.
18        pub fn to_url(&self) -> Option<BString> {
19            use bstr::{ByteSlice, ByteVec};
20            let mut buf: BString = self.protocol.clone()?.into();
21            buf.push_str(b"://");
22            if let Some(user) = &self.username {
23                buf.push_str(user);
24                buf.push(b'@');
25            }
26            if let Some(host) = &self.host {
27                buf.push_str(host);
28            }
29            if let Some(path) = &self.path {
30                if !path.starts_with_str("/") {
31                    buf.push(b'/');
32                }
33                buf.push_str(path);
34            }
35            buf.into()
36        }
37        /// Compute a prompt to obtain the given value.
38        pub fn to_prompt(&self, field: &str) -> String {
39            match self.to_url() {
40                Some(url) => format!("{field} for {url}: "),
41                None => format!("{field}: "),
42            }
43        }
44    }
45}
46
47mod mutate {
48    use bstr::ByteSlice;
49
50    use crate::{protocol, protocol::Context};
51
52    /// In-place mutation
53    impl Context {
54        /// Destructure the url at our `url` field into parts like protocol, host, username and path and store
55        /// them in our respective fields. If `use_http_path` is set, http paths are significant even though
56        /// normally this isn't the case.
57        #[allow(clippy::result_large_err)]
58        pub fn destructure_url_in_place(&mut self, use_http_path: bool) -> Result<&mut Self, protocol::Error> {
59            let url = gix_url::parse(self.url.as_ref().ok_or(protocol::Error::UrlMissing)?.as_ref())?;
60            self.protocol = Some(url.scheme.as_str().into());
61            self.username = url.user().map(ToOwned::to_owned);
62            self.password = url.password().map(ToOwned::to_owned);
63            self.host = url.host().map(ToOwned::to_owned).map(|mut host| {
64                if let Some(port) = url.port {
65                    use std::fmt::Write;
66                    write!(host, ":{port}").expect("infallible");
67                }
68                host
69            });
70            if !matches!(url.scheme, gix_url::Scheme::Http | gix_url::Scheme::Https) || use_http_path {
71                let path = url.path.trim_with(|b| b == '/');
72                self.path = (!path.is_empty()).then(|| path.into());
73            }
74            Ok(self)
75        }
76    }
77}
78
79mod serde;
80pub use self::serde::decode;