1use super::*;
17use core::fmt::Display;
18
19#[cfg(feature = "alloc")]
20use core::ops::Deref;
21#[cfg(feature = "alloc")]
22use alloc::string::{String, ToString};
23
24pub trait AnyUriRef {
27 #[must_use]
33 fn components(&self) -> UriRawComponents<'_>;
34
35 #[must_use]
37 fn is_empty(&self) -> bool {
38 self.components().is_empty()
39 }
40
41 #[must_use]
45 fn uri_type(&self) -> UriType {
46 self.components().uri_type()
47 }
48
49 #[cfg(feature = "alloc")]
54 #[must_use]
55 fn to_uri_ref_buf(&self) -> UriRefBuf {
56 unsafe { UriRefBuf::from_string_unchecked(self.display().to_string()) }
57 }
58
59 unsafe fn write_to_unsafe<T: core::fmt::Write + ?Sized>(
80 &self,
81 write: &mut T,
82 ) -> Result<(), core::fmt::Error> {
83 self.components().write_to(write)
84 }
85}
86
87pub trait AnyUriRefExt: AnyUriRef {
92 #[must_use]
114 fn display(&self) -> UriDisplay<'_, Self> {
115 UriDisplay(self)
116 }
117
118 fn write_to<T: core::fmt::Write + ?Sized>(
131 &self,
132 write: &mut T,
133 ) -> Result<(), core::fmt::Error> {
134 unsafe { self.write_to_unsafe(write) }
135 }
136
137 fn write_resolved<T: core::fmt::Write + ?Sized, D: AnyUriRef + ?Sized>(
140 &self,
141 target: &D,
142 f: &mut T,
143 ) -> Result<(), ResolveError> {
144
145 if target.is_empty() {
146 self.write_to(f)?;
147 return Ok(());
148 }
149
150 let target_type = target.uri_type();
151
152 let target_components = target.components();
153
154 let base_type = self.uri_type();
155
156 if base_type.cannot_be_a_base() {
158 match target_type {
159 UriType::Fragment => {
160 self.components().trim_fragment().write_to(f)?;
161 target.write_to(f)?;
162 return Ok(());
163 }
164 UriType::Query => {
165 self.components().trim_query().write_to(f)?;
166 target.write_to(f)?;
167 return Ok(());
168 }
169 x if x.is_ietf_rfc3986_relative_reference() => {
170 return Err(ResolveError::CannotBeABase);
171 }
172 _ => (),
173 }
174 }
175
176 if target_components.scheme.is_some() {
177 target.write_to(f)?;
178 return Ok(());
179 }
180
181 let mut components = self.components();
182
183 if target_components.authority.is_some() {
184 components.authority = target_components.authority;
185 }
186
187 components.fragment = target_components.fragment;
189 if target_components.query.is_some() {
190 components.query = target_components.query;
191 } else if !target_components.path.is_empty() || target_components.authority.is_some() {
192 components.query = None;
193 }
194
195 if let Some(scheme) = components.scheme {
196 f.write_str(scheme)?;
197 f.write_char(':')?;
198 }
199
200 if let Some(authority) = components.authority {
201 f.write_str("//")?;
202 f.write_str(authority)?;
203 }
204
205 let mut base_path = components.path_as_rel_ref();
206 let target_path = target_components.path_as_rel_ref();
207
208 if !target_path.is_empty() || !target_type.has_absolute_path() {
209 let target_starts_with_slash = target_path.starts_with('/');
210 let base_starts_with_slash = base_path.starts_with('/');
211
212 if target_type.has_absolute_path() {
213 if base_starts_with_slash {
214 base_path = irel_ref!("");
215 } else {
216 base_path = irel_ref!("/");
217 }
218 } else if !target_path.is_empty() {
219 base_path = base_path.trim_resource();
220 }
221
222 let mut out_path_vec = heapless::Vec::<&str, 64>::new();
223
224 let seg_iter = base_path
225 .raw_path_segments()
226 .chain(target_path.raw_path_segments());
227
228 let path_will_be_absolute = target_starts_with_slash
229 || base_starts_with_slash
230 || (base_type.has_absolute_path() && !target_path.is_empty());
231
232 for seg in seg_iter {
233 match seg {
234 "." => {
235 let last = out_path_vec.last().copied();
236
237 if last.map(str::is_empty) == Some(false) {
238 out_path_vec.push("").map_err(|_| ResolveError::PathTooLong)?;
239 }
240 continue;
241 }
242 ".." => {
243 let mut last = out_path_vec.pop();
244
245 if last == Some("") {
246 last = out_path_vec.pop();
247 }
248
249 match (last, path_will_be_absolute, out_path_vec.is_empty()) {
250 (Some("."), false, _) => {
251 out_path_vec.push("..").map_err(|_| ResolveError::PathTooLong)?;
252 }
253 (Some(".."), false, _) => {
254 out_path_vec.push("..").map_err(|_| ResolveError::PathTooLong)?;
255 out_path_vec.push("..").map_err(|_| ResolveError::PathTooLong)?;
256 }
257 (Some(_), true, _) => {
258 out_path_vec.push("").map_err(|_| ResolveError::PathTooLong)?;
259 }
260 (Some(_), false, false) => {
261 out_path_vec.push("").map_err(|_| ResolveError::PathTooLong)?;
262 }
263 (Some(_), false, true) => {
264 out_path_vec.push(".").map_err(|_| ResolveError::PathTooLong)?;
265 }
266 (None, _, _) => (),
267 };
268 }
269 seg => {
270 match out_path_vec.last().copied() {
271 Some(".") if seg.is_empty() => continue,
272 Some(".") | Some("") => {
273 out_path_vec.pop();
274 }
275 _ => (),
276 };
277 out_path_vec.push(seg).map_err(|_| ResolveError::PathTooLong)?;
278 }
279 }
280 }
281
282 if path_will_be_absolute {
283 f.write_char('/')?;
284 }
285
286 for (n, seg) in out_path_vec.into_iter().enumerate() {
287 if n != 0 {
288 f.write_char('/')?;
289 }
290 f.write_str(seg)?;
291 }
292 }
293
294 if let Some(query) = components.query {
295 f.write_char('?')?;
296 f.write_str(query)?;
297 }
298
299 if let Some(fragment) = components.fragment {
300 f.write_char('#')?;
301 f.write_str(fragment)?;
302 }
303
304 Ok(())
305 }
306
307 #[cfg(feature = "alloc")]
310 fn resolved<T: AnyUriRef + ?Sized>(&self, dest: &T) -> Result<UriRefBuf, ResolveError> {
311 if dest.is_empty() {
312 return Ok(self.to_uri_ref_buf());
313 }
314
315 let mut ret = String::new();
316
317 self.write_resolved(dest, &mut ret)?;
318
319 Ok(unsafe { UriRefBuf::from_string_unchecked(ret) })
321 }
322}
323
324impl<T: AnyUriRef + ?Sized> AnyUriRefExt for T {}
326
327#[cfg(feature = "alloc")]
329impl<'a, T: AnyUriRef + Clone + ?Sized> AnyUriRef for Cow<'a, T> {
330 fn components(&self) -> UriRawComponents<'_> {
331 self.deref().components()
332 }
333
334 fn is_empty(&self) -> bool {
335 self.deref().is_empty()
336 }
337
338 fn uri_type(&self) -> UriType {
339 self.deref().uri_type()
340 }
341
342 #[cfg(feature = "alloc")]
343 fn to_uri_ref_buf(&self) -> UriRefBuf {
344 self.deref().to_uri_ref_buf()
345 }
346
347 unsafe fn write_to_unsafe<W: core::fmt::Write + ?Sized>(
348 &self,
349 write: &mut W,
350 ) -> Result<(), core::fmt::Error> {
351 self.deref().write_to_unsafe(write)
352 }
353}
354
355#[derive(Debug, Copy, Clone)]
360pub struct UriDisplay<'a, T: AnyUriRef + ?Sized>(&'a T);
361
362impl<'a, T: AnyUriRef + ?Sized> Display for UriDisplay<'a, T> {
363 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
364 self.0.write_to(f)
365 }
366}
367
368#[cfg(test)]
369mod test {
370 use super::*;
371 #[cfg(feature = "alloc")]
372 use alloc::{vec, vec::Vec, string::ToString, borrow::ToOwned};
373
374 #[test]
375 fn resolve_simple() {
376 let uri_test_table = vec![
377 (
378 "http://x/a/b/c",
379 "/abs-path",
380 Some(iuri_ref!("http://x/abs-path")),
381 ),
382 (
383 "http://x/a/b/c",
384 "f/s?c",
385 Some(iuri_ref!("http://x/a/b/f/s?c")),
386 ),
387 (
388 "http://x/a/b/c",
389 "/abs-path",
390 Some(iuri_ref!("http://x/abs-path")),
391 ),
392 (
393 "http://x/a/b/c",
394 "path",
395 Some(iuri_ref!("http://x/a/b/path")),
396 ),
397 (
398 "http://x/a/b/c/",
399 "path",
400 Some(iuri_ref!("http://x/a/b/c/path")),
401 ),
402 (
403 "http://x/a/b/c/",
404 "//y/d/e/f/",
405 Some(iuri_ref!("http://y/d/e/f/")),
406 ),
407 ("http://x/a/b/c", "?", Some(iuri_ref!("http://x/a/b/c?"))),
408 ("http://x", "a/b/c", Some(iuri_ref!("http://x/a/b/c"))),
409 ("http://x", "/a/b/c", Some(iuri_ref!("http://x/a/b/c"))),
410 ("http://x/", "a/b/c", Some(iuri_ref!("http://x/a/b/c"))),
411 ("http://x/a/b/c", "coap://x", Some(iuri_ref!("coap://x"))),
412 ];
413
414 for (a, b, c) in uri_test_table {
415 let uri_a = UriRef::from_str(a).expect(a);
416 let uri_b = UriRef::from_str(b).expect(b);
417 assert_eq!(
418 uri_a.resolved(uri_b).ok(),
419 c.map(|x| x.to_owned()),
420 "uri_a.resolved(): a:{} b:{} c:{:?}",
421 a,
422 b,
423 c
424 );
425 }
426 }
427
428 #[test]
429 fn resolve_relative_base() {
430 let uri_test_table = vec![
431 ("b/c/d;p?q", "g:h", Some(iuri_ref!("g:h"))),
432 ("b/c/d;p?q", "g", Some(iuri_ref!("b/c/g"))),
433 ("b/c/d;p?q", "./g", Some(iuri_ref!("b/c/g"))),
434 ("b/c/d;p?q", "g/", Some(iuri_ref!("b/c/g/"))),
435 ("b/c/d;p?q", "/g", Some(iuri_ref!("/g"))),
436 ("b/c/d;p?q", "//g", Some(iuri_ref!("//g"))),
437 ("b/c/d;p?q", "?y", Some(iuri_ref!("b/c/d;p?y"))),
438 ("b/c/d;p?q", "g?y", Some(iuri_ref!("b/c/g?y"))),
439 ("b/c/d;p?q", "#s", Some(iuri_ref!("b/c/d;p?q#s"))),
440 ("b/c/d;p?q", "g#s", Some(iuri_ref!("b/c/g#s"))),
441 ("b/c/d;p?q", "g?y#s", Some(iuri_ref!("b/c/g?y#s"))),
442 ("b/c/d;p?q", ";x", Some(iuri_ref!("b/c/;x"))),
443 ("b/c/d;p?q", "g;x", Some(iuri_ref!("b/c/g;x"))),
444 ("b/c/d;p?q", "g;x?y#s", Some(iuri_ref!("b/c/g;x?y#s"))),
445 ("b/c/d;p?q", "", Some(iuri_ref!("b/c/d;p?q"))),
446 ("b/c/d;p?q", ".", Some(iuri_ref!("b/c/"))),
447 ("b/c/d;p?q", "./", Some(iuri_ref!("b/c/"))),
448 ("b/c/d;p?q", "/./g", Some(iuri_ref!("/g"))),
449 ("b/c/d;p?q", "g.", Some(iuri_ref!("b/c/g."))),
450 ("b/c/d;p?q", ".g", Some(iuri_ref!("b/c/.g"))),
451 ("b/c/d;p?q", "g..", Some(iuri_ref!("b/c/g.."))),
452 ("b/c/d;p?q", "..g", Some(iuri_ref!("b/c/..g"))),
453 ("b/c/d;p?q", "g?y/./x", Some(iuri_ref!("b/c/g?y/./x"))),
454 ("b/c/d;p?q", "g?y/../x", Some(iuri_ref!("b/c/g?y/../x"))),
455 ("b/c/d;p?q", "g#s/./x", Some(iuri_ref!("b/c/g#s/./x"))),
456 ("b/c/d;p?q", "g#s/../x", Some(iuri_ref!("b/c/g#s/../x"))),
457 ("b/c/d;p?q", "..", Some(iuri_ref!("b/"))),
458 ("b/c/d;p?q", "../", Some(iuri_ref!("b/"))),
459 ("b/c/d;p?q", "../g", Some(iuri_ref!("b/g"))),
460 ("b/c/d;p?q", "../..", Some(iuri_ref!("."))),
461 ("b/c/d;p?q", "../../", Some(iuri_ref!("."))),
462 ("b/c/d;p?q", "../../g", Some(iuri_ref!("g"))),
463 ("b/c/d;p?q", "../../../g", Some(iuri_ref!("../g"))),
464 ("b/c/d;p?q", "../../../../g", Some(iuri_ref!("../../g"))),
465 ("b/c/d;p?q", "/../g", Some(iuri_ref!("/g"))),
466 ("b/c/d;p?q", "./../g", Some(iuri_ref!("b/g"))),
467 ("b/c/d;p?q", "./g/.", Some(iuri_ref!("b/c/g/"))),
468 ("b/c/d;p?q", "g/./h", Some(iuri_ref!("b/c/g/h"))),
469 ("b/c/d;p?q", "g/../h", Some(iuri_ref!("b/c/h"))),
470 ("b/c/d;p?q", "g;x=1/./y", Some(iuri_ref!("b/c/g;x=1/y"))),
471 ("b/c/d;p?q", "g;x=1/../y", Some(iuri_ref!("b/c/y"))),
472 ];
473
474 for (a, b, c) in uri_test_table {
475 let uri_a = UriRef::from_str(a).expect(a);
476 let uri_b = UriRef::from_str(b).expect(b);
477 assert_eq!(
478 uri_a.resolved(uri_b).ok(),
479 c.map(|x| x.to_owned()),
480 "uri_a.resolved(): a:{} b:{} c:{:?}",
481 a,
482 b,
483 c
484 );
485 }
486 }
487
488 #[test]
489 fn resolve_cannot_be_a_base() {
490 let uri_test_table = vec![
491 ("s:123", "/a/b/c", None),
492 ("s:123", "//a/b/c", None),
493 ("s:123", ".", None),
494 ("s:123", "", Some(iuri_ref!("s:123"))),
495 ("s:123", "?q=123", Some(iuri_ref!("s:123?q=123"))),
496 ("s:123", "#frag", Some(iuri_ref!("s:123#frag"))),
497 ("s:123", "#frag", Some(iuri_ref!("s:123#frag"))),
498 ("s:123", "file:/d/e/f", Some(iuri_ref!("file:/d/e/f"))),
499 ];
500
501 for (a, b, c) in uri_test_table {
502 let uri_a = UriRef::from_str(a).expect(a);
503 let uri_b = UriRef::from_str(b).expect(b);
504 assert_eq!(
505 uri_a.resolved(uri_b).ok(),
506 c.map(|x| x.to_owned()),
507 "uri_a.resolved(): a:{} b:{} c:{:?}",
508 a,
509 b,
510 c
511 );
512 }
513 }
514
515 #[test]
516 fn resolve_no_authority() {
517 let uri_test_table = vec![
518 ("file:/d/e/f", "//a/b/c", Some(iuri_ref!("file://a/b/c"))),
519 ("file:/d/e/f", "g", Some(iuri_ref!("file:/d/e/g"))),
520 ];
521
522 for (a, b, c) in uri_test_table {
523 let uri_a = UriRef::from_str(a).expect(a);
524 let uri_b = UriRef::from_str(b).expect(b);
525 assert_eq!(
526 uri_a.resolved(uri_b).ok(),
527 c.map(|x| x.to_owned()),
528 "uri_a.resolved(): a:{} b:{} c:{:?}",
529 a,
530 b,
531 c
532 );
533 }
534 }
535
536 #[test]
537 fn resolve_rfc3986_simple() {
538 let uri_test_table = vec![
539 ("http://a/b/c/d;p?q", "g:h", Some(iuri_ref!("g:h"))),
541 ("http://a/b/c/d;p?q", "g", Some(iuri_ref!("http://a/b/c/g"))),
542 (
543 "http://a/b/c/d;p?q",
544 "./g",
545 Some(iuri_ref!("http://a/b/c/g")),
546 ),
547 (
548 "http://a/b/c/d;p?q",
549 "g/",
550 Some(iuri_ref!("http://a/b/c/g/")),
551 ),
552 ("http://a/b/c/d;p?q", "/g", Some(iuri_ref!("http://a/g"))),
553 ("http://a/b/c/d;p?q", "//g", Some(iuri_ref!("http://g"))),
554 (
555 "http://a/b/c/d;p?q",
556 "?y",
557 Some(iuri_ref!("http://a/b/c/d;p?y")),
558 ),
559 (
560 "http://a/b/c/d;p?q",
561 "g?y",
562 Some(iuri_ref!("http://a/b/c/g?y")),
563 ),
564 (
565 "http://a/b/c/d;p?q",
566 "#s",
567 Some(iuri_ref!("http://a/b/c/d;p?q#s")),
568 ),
569 (
570 "http://a/b/c/d;p?q",
571 "g#s",
572 Some(iuri_ref!("http://a/b/c/g#s")),
573 ),
574 (
575 "http://a/b/c/d;p?q",
576 "g?y#s",
577 Some(iuri_ref!("http://a/b/c/g?y#s")),
578 ),
579 (
580 "http://a/b/c/d;p?q",
581 ";x",
582 Some(iuri_ref!("http://a/b/c/;x")),
583 ),
584 (
585 "http://a/b/c/d;p?q",
586 "g;x",
587 Some(iuri_ref!("http://a/b/c/g;x")),
588 ),
589 (
590 "http://a/b/c/d;p?q",
591 "g;x?y#s",
592 Some(iuri_ref!("http://a/b/c/g;x?y#s")),
593 ),
594 (
595 "http://a/b/c/d;p?q",
596 "",
597 Some(iuri_ref!("http://a/b/c/d;p?q")),
598 ),
599 ("http://a/b/c/d;p?q", ".", Some(iuri_ref!("http://a/b/c/"))),
600 ("http://a/b/c/d;p?q", "./", Some(iuri_ref!("http://a/b/c/"))),
601 ("http://a/b/c/d;p?q", "/./g", Some(iuri_ref!("http://a/g"))),
602 (
603 "http://a/b/c/d;p?q",
604 "g.",
605 Some(iuri_ref!("http://a/b/c/g.")),
606 ),
607 (
608 "http://a/b/c/d;p?q",
609 ".g",
610 Some(iuri_ref!("http://a/b/c/.g")),
611 ),
612 (
613 "http://a/b/c/d;p?q",
614 "g..",
615 Some(iuri_ref!("http://a/b/c/g..")),
616 ),
617 (
618 "http://a/b/c/d;p?q",
619 "..g",
620 Some(iuri_ref!("http://a/b/c/..g")),
621 ),
622 (
623 "http://a/b/c/d;p?q",
624 "g?y/./x",
625 Some(iuri_ref!("http://a/b/c/g?y/./x")),
626 ),
627 (
628 "http://a/b/c/d;p?q",
629 "g?y/../x",
630 Some(iuri_ref!("http://a/b/c/g?y/../x")),
631 ),
632 (
633 "http://a/b/c/d;p?q",
634 "g#s/./x",
635 Some(iuri_ref!("http://a/b/c/g#s/./x")),
636 ),
637 (
638 "http://a/b/c/d;p?q",
639 "g#s/../x",
640 Some(iuri_ref!("http://a/b/c/g#s/../x")),
641 ),
642 ];
643
644 for (a, b, c) in uri_test_table {
645 let uri_a = UriRef::from_str(a).expect(a);
646 let uri_b = UriRef::from_str(b).expect(b);
647 assert_eq!(
648 uri_a.resolved(uri_b).ok(),
649 c.map(|x| x.to_owned()),
650 "uri_a.resolved(): a:{} b:{} c:{:?}",
651 a,
652 b,
653 c
654 );
655 }
656 }
657
658 #[test]
659 fn resolve_rfc3986_dot_dot() {
660 let uri_test_table = vec![
661 ("http://a/b/c/d;p?q", "..", Some(iuri_ref!("http://a/b/"))),
663 ("http://a/b/c/d;p?q", "../", Some(iuri_ref!("http://a/b/"))),
664 (
665 "http://a/b/c/d;p?q",
666 "../g",
667 Some(iuri_ref!("http://a/b/g")),
668 ),
669 ("http://a/b/c/d;p?q", "../..", Some(iuri_ref!("http://a/"))),
670 ("http://a/b/c/d;p?q", "../../", Some(iuri_ref!("http://a/"))),
671 (
672 "http://a/b/c/d;p?q",
673 "../../g",
674 Some(iuri_ref!("http://a/g")),
675 ),
676 (
677 "http://a/b/c/d;p?q",
678 "../../../g",
679 Some(iuri_ref!("http://a/g")),
680 ),
681 (
682 "http://a/b/c/d;p?q",
683 "../../../../g",
684 Some(iuri_ref!("http://a/g")),
685 ),
686 ("http://a/b/c/d;p?q", "/../g", Some(iuri_ref!("http://a/g"))),
687 (
688 "http://a/b/c/d;p?q",
689 "./../g",
690 Some(iuri_ref!("http://a/b/g")),
691 ),
692 (
693 "http://a/b/c/d;p?q",
694 "./g/.",
695 Some(iuri_ref!("http://a/b/c/g/")),
696 ),
697 (
698 "http://a/b/c/d;p?q",
699 "g/./h",
700 Some(iuri_ref!("http://a/b/c/g/h")),
701 ),
702 (
703 "http://a/b/c/d;p?q",
704 "g/../h",
705 Some(iuri_ref!("http://a/b/c/h")),
706 ),
707 (
708 "http://a/b/c/d;p?q",
709 "g;x=1/./y",
710 Some(iuri_ref!("http://a/b/c/g;x=1/y")),
711 ),
712 (
713 "http://a/b/c/d;p?q",
714 "g;x=1/../y",
715 Some(iuri_ref!("http://a/b/c/y")),
716 ),
717 ];
718
719 for (a, b, c) in uri_test_table {
720 let uri_a = UriRef::from_str(a).expect(a);
721 let uri_b = UriRef::from_str(b).expect(b);
722 assert_eq!(
723 uri_a.resolved(uri_b).ok(),
724 c.map(|x| x.to_owned()),
725 "uri_a.resolved(): a:{} b:{} c:{:?}",
726 a,
727 b,
728 c
729 );
730 }
731 }
732}