]> git.proxmox.com Git - rustc.git/blob - vendor/gix-url/src/lib.rs
New upstream version 1.70.0+dfsg2
[rustc.git] / vendor / gix-url / src / lib.rs
1 //! A library implementing a URL for use in git with access to its special capabilities.
2 //! ## Feature Flags
3 #![cfg_attr(
4 feature = "document-features",
5 cfg_attr(doc, doc = ::document_features::document_features!())
6 )]
7 #![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
8 #![deny(rust_2018_idioms, missing_docs)]
9 #![forbid(unsafe_code)]
10
11 use bstr::{BStr, BString};
12
13 ///
14 pub mod parse;
15 #[doc(inline)]
16 pub use parse::parse;
17
18 ///
19 pub mod expand_path;
20 #[doc(inline)]
21 pub use expand_path::expand_path;
22
23 mod scheme;
24 pub use scheme::Scheme;
25
26 /// A URL with support for specialized git related capabilities.
27 ///
28 /// Additionally there is support for [deserialization][Url::from_bytes()] and serialization
29 /// (_see the `Display::fmt()` implementation_).
30 ///
31 /// # Deviation
32 ///
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))]
36 pub struct Url {
37 /// The URL scheme.
38 pub scheme: Scheme,
39 /// The user to impersonate on the remote.
40 user: Option<String>,
41 /// The host to which to connect. Localhost is implied if `None`.
42 host: Option<String>,
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,
49 }
50
51 /// Instantiation
52 impl Url {
53 /// Create a new instance from the given parts, which will be validated by parsing them back.
54 pub fn from_parts(
55 scheme: Scheme,
56 user: Option<String>,
57 host: Option<String>,
58 port: Option<u16>,
59 path: BString,
60 ) -> Result<Self, parse::Error> {
61 parse(
62 Url {
63 scheme,
64 user,
65 host,
66 port,
67 path,
68 serialize_alternative_form: false,
69 }
70 .to_bstring()
71 .as_ref(),
72 )
73 }
74
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(
77 scheme: Scheme,
78 user: Option<String>,
79 host: Option<String>,
80 port: Option<u16>,
81 path: BString,
82 ) -> Result<Self, parse::Error> {
83 parse(
84 Url {
85 scheme,
86 user,
87 host,
88 port,
89 path,
90 serialize_alternative_form: true,
91 }
92 .to_bstring()
93 .as_ref(),
94 )
95 }
96 }
97
98 /// Modification
99 impl Url {
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();
103 self.user = user;
104 prev
105 }
106 }
107
108 /// Builder
109 impl Url {
110 /// Enable alternate serialization for this url, e.g. `file:///path` becomes `/path`.
111 ///
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;
116 self
117 }
118
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();
125 }
126 Ok(())
127 }
128 }
129
130 /// Access
131 impl Url {
132 /// Returns the user mentioned in the url, if present.
133 pub fn user(&self) -> Option<&str> {
134 self.user.as_deref()
135 }
136 /// Returns the host mentioned in the url, if present.
137 pub fn host(&self) -> Option<&str> {
138 self.host.as_deref()
139 }
140 /// Returns true if the path portion of the url is `/`.
141 pub fn path_is_root(&self) -> bool {
142 self.path == "/"
143 }
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(|| {
148 use Scheme::*;
149 Some(match self.scheme {
150 Http => 80,
151 Https => 443,
152 Ssh => 22,
153 Git => 9418,
154 File | Ext(_) => return None,
155 })
156 })
157 }
158 }
159
160 /// Transformation
161 impl Url {
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();
165 res.canonicalize()?;
166 Ok(res)
167 }
168 }
169
170 /// Serialization
171 impl Url {
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"://")?;
177 }
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())?;
183 }
184 (None, Some(host)) => {
185 out.write_all(host.as_bytes())?;
186 }
187 (None, None) => {}
188 (Some(_user), None) => unreachable!("BUG: should not be possible to have a user but no host"),
189 };
190 if let Some(port) = &self.port {
191 write!(&mut out, ":{port}")?;
192 }
193 if self.serialize_alternative_form && self.scheme == Scheme::Ssh {
194 out.write_all(b":")?;
195 }
196 out.write_all(&self.path)?;
197 Ok(())
198 }
199
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(
203 (5 + 3)
204 + self.user.as_ref().map(|n| n.len()).unwrap_or_default()
205 + 1
206 + self.host.as_ref().map(|h| h.len()).unwrap_or_default()
207 + self.port.map(|_| 5).unwrap_or_default()
208 + self.path.len(),
209 );
210 self.write_to(&mut buf).expect("io cannot fail in memory");
211 buf.into()
212 }
213 }
214
215 /// Deserialization
216 impl Url {
217 /// Parse a URL from `bytes`
218 pub fn from_bytes(bytes: &BStr) -> Result<Self, parse::Error> {
219 parse(bytes)
220 }
221 }
222
223 mod impls;