1use crate::{
4 components::first_segment_has_colon,
5 error::{InvalidIri, InvalidUri, IriParseError},
6 parse::find_iri_ref_positions,
7 resolve::resolve,
8 types::{Iri, IriBuf, IriRef, IriRefBuf, Uri, UriBuf, UriRef, UriRefBuf},
9 validate::{validate_authority_body, validate_fragment, validate_iri_ref, validate_path, validate_query, validate_resolved_path, validate_scheme},
10};
11
12impl IriRefBuf {
13 pub fn set_scheme(&mut self, scheme: Option<&str>) -> Result<(), IriParseError> {
17 if let Some(s) = scheme {
18 validate_scheme(s)?;
19 }
20 rebuild(self, &Edit::Scheme(scheme.map(str::to_string)), true)
21 }
22
23 pub fn set_authority(&mut self, authority: Option<&str>) -> Result<(), IriParseError> {
27 if let Some(a) = authority {
28 validate_authority_body(a, true)?;
29 }
30 rebuild(self, &Edit::Authority(authority.map(str::to_string)), true)
31 }
32
33 pub fn set_path(&mut self, path: &str) -> Result<(), IriParseError> {
37 validate_path(path, true)?;
38 rebuild(self, &Edit::Path(path.to_string()), true)
39 }
40
41 pub fn set_query(&mut self, query: Option<&str>) -> Result<(), IriParseError> {
45 if let Some(q) = query {
46 validate_query(q, true)?;
47 }
48 rebuild(self, &Edit::Query(query.map(str::to_string)), true)
49 }
50
51 pub fn set_fragment(&mut self, fragment: Option<&str>) -> Result<(), IriParseError> {
55 if let Some(f) = fragment {
56 validate_fragment(f, true)?;
57 }
58 rebuild(self, &Edit::Fragment(fragment.map(str::to_string)), true)
59 }
60
61 pub fn resolve<T: std::ops::Deref<Target = str>>(&mut self, base: &Iri<T>) -> Result<(), IriParseError> {
65 let mut out = String::with_capacity(self.as_str().len() + base.as_str().len());
66 let pos = resolve((base.as_str(), base.positions()), (self.as_str(), self.positions), &mut out);
67 validate_resolved_path(&out, pos)?;
68 *self = IriRefBuf::from_raw_parts(out, pos);
69 Ok(())
70 }
71
72 pub fn try_into_iri(self) -> Result<IriBuf, InvalidIri<IriRefBuf>> {
76 if self.is_absolute() {
77 let p = self.positions;
78 Ok(Iri::from_raw_parts(self.into_inner(), p))
79 } else {
80 Err(InvalidIri(self))
81 }
82 }
83
84 #[must_use]
85 pub fn as_iri(&self) -> Option<Iri<&str>> {
86 if self.is_absolute() {
87 Some(Iri::from_raw_parts(self.as_str(), self.positions))
88 } else {
89 None
90 }
91 }
92}
93
94impl Default for IriRefBuf {
95 fn default() -> Self {
96 Self::parse_unchecked(String::new())
97 }
98}
99
100impl Default for UriRefBuf {
101 fn default() -> Self {
102 Self::parse_unchecked(String::new())
103 }
104}
105
106impl IriBuf {
107 pub fn set_scheme(&mut self, scheme: &str) -> Result<(), IriParseError> {
111 validate_scheme(scheme)?;
112 let p = self.positions();
113 let iri = std::mem::take(&mut self.0.iri);
114 let mut r = IriRefBuf::from_raw_parts(iri, p);
115 rebuild(&mut r, &Edit::Scheme(Some(scheme.to_string())), true)?;
116 *self = Iri::from_raw_parts(r.iri, r.positions);
117 Ok(())
118 }
119
120 pub fn set_authority(&mut self, authority: Option<&str>) -> Result<(), IriParseError> {
124 let p = self.positions();
125 let iri = std::mem::take(&mut self.0.iri);
126 let mut r = IriRefBuf::from_raw_parts(iri, p);
127 r.set_authority(authority)?;
128 *self = Iri::from_raw_parts(r.iri, r.positions);
129 Ok(())
130 }
131
132 pub fn set_path(&mut self, path: &str) -> Result<(), IriParseError> {
136 let p = self.positions();
137 let iri = std::mem::take(&mut self.0.iri);
138 let mut r = IriRefBuf::from_raw_parts(iri, p);
139 r.set_path(path)?;
140 *self = Iri::from_raw_parts(r.iri, r.positions);
141 Ok(())
142 }
143
144 pub fn set_query(&mut self, query: Option<&str>) -> Result<(), IriParseError> {
148 let p = self.positions();
149 let iri = std::mem::take(&mut self.0.iri);
150 let mut r = IriRefBuf::from_raw_parts(iri, p);
151 r.set_query(query)?;
152 *self = Iri::from_raw_parts(r.iri, r.positions);
153 Ok(())
154 }
155
156 pub fn set_fragment(&mut self, fragment: Option<&str>) -> Result<(), IriParseError> {
160 let p = self.positions();
161 let iri = std::mem::take(&mut self.0.iri);
162 let mut r = IriRefBuf::from_raw_parts(iri, p);
163 r.set_fragment(fragment)?;
164 *self = Iri::from_raw_parts(r.iri, r.positions);
165 Ok(())
166 }
167}
168
169impl UriRefBuf {
170 pub fn set_scheme(&mut self, scheme: Option<&str>) -> Result<(), IriParseError> {
174 if let Some(s) = scheme {
175 validate_scheme(s)?;
176 }
177 rebuild_uri(self, &Edit::Scheme(scheme.map(str::to_string)))
178 }
179 pub fn set_authority(&mut self, authority: Option<&str>) -> Result<(), IriParseError> {
183 if let Some(a) = authority {
184 validate_authority_body(a, false)?;
185 }
186 rebuild_uri(self, &Edit::Authority(authority.map(str::to_string)))
187 }
188 pub fn set_path(&mut self, path: &str) -> Result<(), IriParseError> {
192 validate_path(path, false)?;
193 rebuild_uri(self, &Edit::Path(path.to_string()))
194 }
195 pub fn set_query(&mut self, query: Option<&str>) -> Result<(), IriParseError> {
199 if let Some(q) = query {
200 validate_query(q, false)?;
201 }
202 rebuild_uri(self, &Edit::Query(query.map(str::to_string)))
203 }
204 pub fn set_fragment(&mut self, fragment: Option<&str>) -> Result<(), IriParseError> {
208 if let Some(f) = fragment {
209 validate_fragment(f, false)?;
210 }
211 rebuild_uri(self, &Edit::Fragment(fragment.map(str::to_string)))
212 }
213 pub fn resolve<T: std::ops::Deref<Target = str>>(&mut self, base: &Uri<T>) -> Result<(), IriParseError> {
217 let mut out = String::with_capacity(self.as_str().len() + base.as_str().len());
218 let pos = resolve((base.as_str(), base.positions()), (self.as_str(), self.positions), &mut out);
219 validate_resolved_path(&out, pos)?;
220 *self = UriRefBuf::from_raw_parts(out, pos);
221 Ok(())
222 }
223
224 pub fn try_into_uri(self) -> Result<UriBuf, InvalidUri<UriRefBuf>> {
228 if self.is_absolute() {
229 let p = self.positions;
230 Ok(Uri::from_raw_parts(self.into_inner(), p))
231 } else {
232 Err(InvalidUri(self))
233 }
234 }
235
236 #[must_use]
237 pub fn as_uri(&self) -> Option<Uri<&str>> {
238 if self.is_absolute() {
239 Some(Uri::from_raw_parts(self.as_str(), self.positions))
240 } else {
241 None
242 }
243 }
244}
245
246impl UriBuf {
247 pub fn set_scheme(&mut self, scheme: &str) -> Result<(), IriParseError> {
251 let p = self.positions();
252 let uri = std::mem::take(&mut self.0.uri);
253 let mut r = UriRefBuf::from_raw_parts(uri, p);
254 r.set_scheme(Some(scheme))?;
255 *self = Uri::from_raw_parts(r.uri, r.positions);
256 Ok(())
257 }
258 pub fn set_authority(&mut self, authority: Option<&str>) -> Result<(), IriParseError> {
262 let p = self.positions();
263 let uri = std::mem::take(&mut self.0.uri);
264 let mut r = UriRefBuf::from_raw_parts(uri, p);
265 r.set_authority(authority)?;
266 *self = Uri::from_raw_parts(r.uri, r.positions);
267 Ok(())
268 }
269 pub fn set_path(&mut self, path: &str) -> Result<(), IriParseError> {
273 let p = self.positions();
274 let uri = std::mem::take(&mut self.0.uri);
275 let mut r = UriRefBuf::from_raw_parts(uri, p);
276 r.set_path(path)?;
277 *self = Uri::from_raw_parts(r.uri, r.positions);
278 Ok(())
279 }
280 pub fn set_query(&mut self, query: Option<&str>) -> Result<(), IriParseError> {
284 let p = self.positions();
285 let uri = std::mem::take(&mut self.0.uri);
286 let mut r = UriRefBuf::from_raw_parts(uri, p);
287 r.set_query(query)?;
288 *self = Uri::from_raw_parts(r.uri, r.positions);
289 Ok(())
290 }
291 pub fn set_fragment(&mut self, fragment: Option<&str>) -> Result<(), IriParseError> {
295 let p = self.positions();
296 let uri = std::mem::take(&mut self.0.uri);
297 let mut r = UriRefBuf::from_raw_parts(uri, p);
298 r.set_fragment(fragment)?;
299 *self = Uri::from_raw_parts(r.uri, r.positions);
300 Ok(())
301 }
302}
303
304enum Edit {
305 Scheme(Option<String>),
306 Authority(Option<String>),
307 Path(String),
308 Query(Option<String>),
309 Fragment(Option<String>),
310}
311
312fn rebuild(buf: &mut IriRefBuf, edit: &Edit, is_iri: bool) -> Result<(), IriParseError> {
313 let out = {
314 let s = buf.as_str();
315 let (scheme, authority, path, query, fragment) = destructure(s, buf.positions);
316 let mut out = String::with_capacity(s.len() + 8);
317 match edit {
318 Edit::Scheme(new) => write_iri(&mut out, new.as_deref(), authority, path, query, fragment),
319 Edit::Authority(new) => write_iri(&mut out, scheme, new.as_deref(), path, query, fragment),
320 Edit::Path(new) => write_iri(&mut out, scheme, authority, new, query, fragment),
321 Edit::Query(new) => write_iri(&mut out, scheme, authority, path, new.as_deref(), fragment),
322 Edit::Fragment(new) => write_iri(&mut out, scheme, authority, path, query, new.as_deref()),
323 }
324 out
325 };
326 let positions = find_iri_ref_positions(&out);
327 validate_iri_ref(&out, positions, is_iri)?;
328 *buf = IriRef::from_raw_parts(out, positions);
329 Ok(())
330}
331
332fn write_iri(out: &mut String, scheme: Option<&str>, authority: Option<&str>, path: &str, query: Option<&str>, fragment: Option<&str>) {
333 if let Some(s) = scheme {
334 out.push_str(s);
335 out.push(':');
336 }
337 if let Some(a) = authority {
338 out.push_str("//");
339 out.push_str(a);
340 } else {
341 if path.starts_with("//") {
345 out.push_str("/.");
347 } else if scheme.is_none() && first_segment_has_colon(path) {
348 out.push_str("./");
351 }
352 }
353 out.push_str(path);
354 if let Some(q) = query {
355 out.push('?');
356 out.push_str(q);
357 }
358 if let Some(f) = fragment {
359 out.push('#');
360 out.push_str(f);
361 }
362}
363
364fn rebuild_uri(buf: &mut UriRefBuf, edit: &Edit) -> Result<(), IriParseError> {
365 let mut tmp: IriRefBuf = IriRef::from_raw_parts(buf.as_str().to_string(), buf.positions);
366 rebuild(&mut tmp, edit, false)?;
367 let p = tmp.positions;
368 *buf = UriRef::from_raw_parts(tmp.iri, p);
369 Ok(())
370}
371
372fn destructure(s: &str, p: crate::parse::Positions) -> (Option<&str>, Option<&str>, &str, Option<&str>, Option<&str>) {
373 let scheme = if p.scheme_end > 0 { Some(&s[..p.scheme_end - 1]) } else { None };
374 let authority = if p.authority_end > p.scheme_end + 2
375 || (p.authority_end == p.scheme_end + 2 && p.scheme_end + 2 <= s.len() && &s[p.scheme_end..p.scheme_end + 2] == "//")
376 {
377 Some(&s[p.scheme_end + 2..p.authority_end])
378 } else {
379 None
380 };
381 let path = &s[p.authority_end..p.path_end];
382 let query = if p.query_end > p.path_end {
383 Some(&s[p.path_end + 1..p.query_end])
384 } else {
385 None
386 };
387 let fragment = if s.len() > p.query_end { Some(&s[p.query_end + 1..]) } else { None };
388 (scheme, authority, path, query, fragment)
389}
390
391#[cfg(test)]
392mod tests {
393 use super::*;
394
395 fn iri_ref(s: &str) -> IriRefBuf {
396 IriRef::parse(s.to_owned()).unwrap()
397 }
398
399 #[test]
402 fn removing_a_scheme_disambiguates_a_first_segment_with_a_colon() {
403 let mut buf = iri_ref("scheme:a:b/c");
404 buf.set_scheme(None).unwrap();
405
406 assert_eq!(buf.as_str(), "./a:b/c");
407 assert_eq!(buf.scheme(), None, "the path must not be read back as a scheme");
408 assert_eq!(buf.path(), "./a:b/c");
409 }
410
411 #[test]
414 fn removing_an_authority_disambiguates_a_path_starting_with_two_slashes() {
415 let mut buf = iri_ref("//host//path");
416 buf.set_authority(None).unwrap();
417
418 assert_eq!(buf.as_str(), "/.//path");
419 assert_eq!(buf.authority(), None, "the path must not be read back as an authority");
420 assert_eq!(buf.path(), "/.//path");
421 }
422
423 #[test]
424 fn removing_an_authority_disambiguates_under_a_scheme_too() {
425 let mut buf = iri_ref("http://host//path");
426 buf.set_authority(None).unwrap();
427
428 assert_eq!(buf.as_str(), "http:/.//path");
429 assert_eq!(buf.scheme(), Some("http"));
430 assert_eq!(buf.authority(), None);
431 assert_eq!(buf.path(), "/.//path");
432 }
433
434 #[test]
435 fn setting_an_ambiguous_path_disambiguates_it() {
436 let mut buf = iri_ref("http:x");
437 buf.set_path("//foo").unwrap();
438 assert_eq!(buf.as_str(), "http:/.//foo");
439 assert_eq!(buf.authority(), None);
440
441 let mut buf = iri_ref("x");
442 buf.set_path("a:b").unwrap();
443 assert_eq!(buf.as_str(), "./a:b");
444 assert_eq!(buf.scheme(), None);
445 }
446
447 #[test]
448 fn unambiguous_removals_gain_no_dot_segment() {
449 let mut buf = iri_ref("http://host/path");
450 buf.set_authority(None).unwrap();
451 assert_eq!(buf.as_str(), "http:/path");
452
453 let mut buf = iri_ref("scheme:/a/b");
454 buf.set_scheme(None).unwrap();
455 assert_eq!(buf.as_str(), "/a/b");
456
457 let mut buf = iri_ref("scheme:a/b:c");
459 buf.set_scheme(None).unwrap();
460 assert_eq!(buf.as_str(), "a/b:c");
461
462 let mut buf = iri_ref("http://host//path");
464 buf.set_scheme(None).unwrap();
465 assert_eq!(buf.as_str(), "//host//path");
466 }
467
468 #[test]
469 fn uri_buffers_disambiguate_the_same_way() {
470 let mut buf: UriRefBuf = UriRef::parse("//host//path".to_owned()).unwrap();
471 buf.set_authority(None).unwrap();
472 assert_eq!(buf.as_str(), "/.//path");
473 assert_eq!(buf.authority(), None);
474
475 let mut buf: UriRefBuf = UriRef::parse("scheme:a:b/c".to_owned()).unwrap();
476 buf.set_scheme(None).unwrap();
477 assert_eq!(buf.as_str(), "./a:b/c");
478 assert_eq!(buf.scheme(), None);
479 }
480
481 #[test]
487 fn percent_encoded_values_survive_mutation() {
488 let mut buf = iri_ref("scheme://user:pass@example.com:8080/path");
489 buf.set_authority(Some("%6Eew_long_host.org")).unwrap();
490 assert_eq!(buf.as_str(), "scheme://%6Eew_long_host.org/path");
491 assert_eq!(buf.authority(), Some("%6Eew_long_host.org"));
492
493 let mut buf = iri_ref("scheme://%65xample.com:8080/path");
494 buf.set_authority(Some("example.com")).unwrap();
495 assert_eq!(buf.authority(), Some("example.com"));
496
497 let mut buf = iri_ref("scheme://authority/path");
498 buf.set_query(Some("%71uery")).unwrap();
499 buf.set_fragment(Some("%66rag")).unwrap();
500 assert_eq!(buf.as_str(), "scheme://authority/path?%71uery#%66rag");
501 assert_eq!(buf.query(), Some("%71uery"));
502 assert_eq!(buf.fragment(), Some("%66rag"));
503
504 buf.set_fragment(None).unwrap();
505 assert_eq!(buf.as_str(), "scheme://authority/path?%71uery");
506 }
507
508 #[test]
511 fn disambiguated_results_resolve_to_the_intended_path() {
512 let mut buf = iri_ref("//host//path");
513 buf.set_authority(None).unwrap();
514
515 let base = Iri::parse("http://example.org/a/b").unwrap();
516 let resolved = buf.resolved(&base).unwrap();
517 assert_eq!(resolved.as_str(), "http://example.org//path");
518 assert_eq!(resolved.authority(), Some("example.org"));
519 assert_eq!(resolved.path(), "//path");
520 }
521}