async_coap_uri/
any_uri_ref.rs

1// Copyright 2019 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//
15
16use super::*;
17use core::fmt::Display;
18
19/// Trait for objects that represent logical URI-references. Useful for generic programming.
20///
21pub trait AnyUriRef {
22    /// Returns a `UriRawComponents` instance which contains all of the components for this
23    /// URI reference.
24    ///
25    /// This is the only method that is required to be implemented---all other methods have
26    /// defaults in place which use this method, but they may be inefficient.
27    #[must_use]
28    fn components(&self) -> UriRawComponents<'_>;
29
30    /// Serializes this URI to anything implementing [`core::fmt::Write`]. **Override with care!**
31    ///
32    /// The purpose of this method is to provide a uniform way for a type that implements this
33    /// trait to write out a well-formed URI; without making any assumptions on what that type
34    /// might write out for [`std::fmt::Display`] (or even if it implements it at all). Thus, traits
35    /// can be a bit more fast-and-loose with their implementations of [`std::fmt::Display`].
36    ///
37    /// See the documentation for [`display`](AnyUriRef::display) and [`UriDisplay`] for examples
38    /// of usage.
39    ///
40    /// # Safety
41    ///
42    /// Calling this method is not unsafe, *but implementing it is*! The underlying
43    /// guarantee is that the written URI reference SHALL be well-formed.
44    /// If this method writes out something that is not a valid URI reference, the
45    /// resulting behavior is undefined.
46    ///
47    /// TODO: Investigate implications of making this method unsafe.
48    fn write_to<T: core::fmt::Write + ?Sized>(
49        &self,
50        write: &mut T,
51    ) -> Result<(), core::fmt::Error> {
52        self.components().write_to(write)
53    }
54
55    /// Wraps this `AnyUriRef` instance in a [`UriDisplay`] object for use with formatting
56    /// macros like `write!` and `format!`.
57    ///
58    /// The resulting instance will use the [`AnyUriRef::write_to`] method on this trait.
59    ///
60    /// This method should not be overridden: override [`AnyUriRef::write_to`] instead.
61    ///
62    /// This method is similar to the [`display`][display-path] method on [`std::path::Path`].
63    ///
64    /// [display-path]: std::path::Path::display
65    ///
66    /// ## Example
67    ///
68    /// ```
69    /// use async_coap_uri::prelude::*;
70    ///
71    /// let uri_ref = uri_ref!("http://example.com/");
72    ///
73    /// println!("uri_ref = {}", uri_ref.display());
74    /// ```
75    ///
76    /// [`UriDisplay`]: struct.UriDisplay.html
77    #[must_use]
78    fn display(&self) -> UriDisplay<'_, Self> {
79        UriDisplay(self)
80    }
81
82    /// Returns true if the underlying URI-reference is actually the empty reference.
83    #[must_use]
84    fn is_empty(&self) -> bool {
85        self.components().is_empty()
86    }
87
88    /// Gets the [`UriType`] of the underlying URI-reference.
89    ///
90    /// [`UriType`]: enum.UriType.html
91    #[must_use]
92    fn uri_type(&self) -> UriType {
93        self.components().uri_type()
94    }
95
96    /// Creates a new [`UriRefBuf`] from this [`AnyUriRef`].
97    ///
98    /// The default implementation uses the [`AnyUriRef::write_to`] method to render out
99    /// the content of the URI-reference.
100    #[cfg(feature = "std")]
101    #[must_use]
102    fn to_uri_ref_buf(&self) -> UriRefBuf {
103        unsafe { UriRefBuf::from_string_unchecked(self.display().to_string()) }
104    }
105
106    /// Writes out to a [`core::fmt::Write`] instance the result of performing URI resolution
107    /// against `target`, with `self` being the base URI.
108    ///
109    fn write_resolved<T: core::fmt::Write + ?Sized, D: AnyUriRef + ?Sized>(
110        &self,
111        target: &D,
112        f: &mut T,
113    ) -> Result<(), ResolveError> {
114        // This implementation is kind of a mess, but it does work and it does
115        // pass the rather large corpus of unit tests. It eventually needs to be
116        // rewritten to avoid memory allocation.
117        // TODO: Rewrite `AnyUriRef::write_resolved` to not use any memory allocation.
118
119        if target.is_empty() {
120            self.write_to(f)?;
121            return Ok(());
122        }
123
124        let target_type = target.uri_type();
125
126        let target_components = target.components();
127
128        let base_type = self.uri_type();
129
130        // Handle some exceptions.
131        if base_type.cannot_be_a_base() {
132            match target_type {
133                UriType::Fragment => {
134                    self.components().trim_fragment().write_to(f)?;
135                    target.write_to(f)?;
136                    return Ok(());
137                }
138                UriType::Query => {
139                    self.components().trim_query().write_to(f)?;
140                    target.write_to(f)?;
141                    return Ok(());
142                }
143                x if x.is_ietf_rfc3986_relative_reference() => {
144                    return Err(ResolveError::CannotBeABase);
145                }
146                _ => (),
147            }
148        }
149
150        if target_components.scheme.is_some() {
151            target.write_to(f)?;
152            return Ok(());
153        }
154
155        let mut components = self.components();
156
157        if target_components.authority.is_some() {
158            components.authority = target_components.authority;
159        }
160
161        // Target fragment always gets used.
162        components.fragment = target_components.fragment;
163        if target_components.query.is_some() {
164            components.query = target_components.query;
165        } else if !target_components.path.is_empty() || target_components.authority.is_some() {
166            components.query = None;
167        }
168
169        if let Some(scheme) = components.scheme {
170            f.write_str(scheme)?;
171            f.write_char(':')?;
172        }
173
174        if let Some(authority) = components.authority {
175            f.write_str("//")?;
176            f.write_str(authority)?;
177        }
178
179        let mut base_path = components.path_as_rel_ref();
180        let target_path = target_components.path_as_rel_ref();
181
182        if !target_path.is_empty() || !target_type.has_absolute_path() {
183            let target_starts_with_slash = target_path.starts_with("/");
184            let base_starts_with_slash = base_path.starts_with("/");
185
186            if target_type.has_absolute_path() {
187                if base_starts_with_slash {
188                    base_path = rel_ref!("");
189                } else {
190                    base_path = rel_ref!("/");
191                }
192            } else if !target_path.is_empty() {
193                base_path = base_path.trim_resource();
194            }
195
196            let mut out_path_vec = Vec::new();
197
198            let seg_iter = base_path
199                .raw_path_segments()
200                .chain(target_path.raw_path_segments());
201
202            let path_will_be_absolute = target_starts_with_slash
203                || base_starts_with_slash
204                || (base_type.has_absolute_path() && !target_path.is_empty());
205
206            for seg in seg_iter {
207                match seg {
208                    "." => {
209                        let last = out_path_vec.last().map(|x| *x);
210
211                        if last.map(str::is_empty) == Some(false) {
212                            out_path_vec.push("");
213                        }
214                        continue;
215                    }
216                    ".." => {
217                        let mut last = out_path_vec.pop();
218
219                        if last == Some("") {
220                            last = out_path_vec.pop();
221                        }
222
223                        match (last, path_will_be_absolute, out_path_vec.is_empty()) {
224                            (Some("."), false, _) => out_path_vec.push(".."),
225                            (Some(".."), false, _) => {
226                                out_path_vec.push("..");
227                                out_path_vec.push("..");
228                            }
229                            (Some(_), true, _) => out_path_vec.push(""),
230                            (Some(_), false, false) => out_path_vec.push(""),
231                            (Some(_), false, true) => out_path_vec.push("."),
232                            (None, _, _) => (),
233                        };
234                    }
235                    seg => {
236                        match out_path_vec.last().map(|x| *x) {
237                            Some(".") if seg.is_empty() => continue,
238                            Some(".") | Some("") => {
239                                out_path_vec.pop();
240                            }
241                            _ => (),
242                        };
243                        out_path_vec.push(seg)
244                    }
245                }
246            }
247
248            if path_will_be_absolute {
249                f.write_char('/')?;
250            }
251
252            for (n, seg) in out_path_vec.into_iter().enumerate() {
253                if n != 0 {
254                    f.write_char('/')?;
255                }
256                f.write_str(seg)?;
257            }
258        }
259
260        if let Some(query) = components.query {
261            f.write_char('?')?;
262            f.write_str(query)?;
263        }
264
265        if let Some(fragment) = components.fragment {
266            f.write_char('#')?;
267            f.write_str(fragment)?;
268        }
269
270        Ok(())
271    }
272
273    /// Creates a new [`UriRefBuf`] that contains the result of performing URI resolution with
274    /// `dest`.
275    #[cfg(feature = "std")]
276    #[must_use]
277    fn resolved<T: AnyUriRef + ?Sized>(&self, dest: &T) -> Result<UriRefBuf, ResolveError> {
278        if dest.is_empty() {
279            return Ok(self.to_uri_ref_buf());
280        }
281
282        let mut ret = String::new();
283
284        self.write_resolved(dest, &mut ret)?;
285
286        // SAFETY: `write_resolved` is guaranteed to write well-formed UriRefs.
287        Ok(unsafe { UriRefBuf::from_string_unchecked(ret) })
288    }
289}
290
291use core::ops::Deref;
292
293/// Blanket implementation for Copy-On-Write types.
294impl<'a, T: AnyUriRef + Clone + ?Sized> AnyUriRef for Cow<'a, T> {
295    fn components(&self) -> UriRawComponents<'_> {
296        self.deref().components()
297    }
298
299    fn write_to<W: core::fmt::Write + ?Sized>(
300        &self,
301        write: &mut W,
302    ) -> Result<(), core::fmt::Error> {
303        self.deref().write_to(write)
304    }
305
306    fn is_empty(&self) -> bool {
307        self.deref().is_empty()
308    }
309
310    fn uri_type(&self) -> UriType {
311        self.deref().uri_type()
312    }
313
314    #[cfg(feature = "std")]
315    fn to_uri_ref_buf(&self) -> UriRefBuf {
316        self.deref().to_uri_ref_buf()
317    }
318
319    fn write_resolved<W: core::fmt::Write + ?Sized, D: AnyUriRef + ?Sized>(
320        &self,
321        dest: &D,
322        output: &mut W,
323    ) -> Result<(), ResolveError> {
324        self.deref().write_resolved(dest, output)
325    }
326
327    fn resolved<W: AnyUriRef + ?Sized>(&self, dest: &W) -> Result<UriRefBuf, ResolveError> {
328        self.deref().resolved(dest)
329    }
330}
331
332/// Helper class to assist with using [`AnyUriRef`] with formatters; instantiated by
333/// [`AnyUriRef::display`].
334///
335/// This type is similar to [`std::path::Display`].
336#[derive(Debug, Copy, Clone)]
337pub struct UriDisplay<'a, T: AnyUriRef + ?Sized>(&'a T);
338
339impl<'a, T: AnyUriRef + ?Sized> Display for UriDisplay<'a, T> {
340    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
341        self.0.write_to(f)
342    }
343}
344
345#[cfg(test)]
346mod test {
347    use super::*;
348
349    #[test]
350    fn resolve_simple() {
351        let uri_test_table = vec![
352            (
353                "http://x/a/b/c",
354                "/abs-path",
355                Some(uri_ref!("http://x/abs-path")),
356            ),
357            (
358                "http://x/a/b/c",
359                "f/s?c",
360                Some(uri_ref!("http://x/a/b/f/s?c")),
361            ),
362            (
363                "http://x/a/b/c",
364                "/abs-path",
365                Some(uri_ref!("http://x/abs-path")),
366            ),
367            (
368                "http://x/a/b/c",
369                "path",
370                Some(uri_ref!("http://x/a/b/path")),
371            ),
372            (
373                "http://x/a/b/c/",
374                "path",
375                Some(uri_ref!("http://x/a/b/c/path")),
376            ),
377            (
378                "http://x/a/b/c/",
379                "//y/d/e/f/",
380                Some(uri_ref!("http://y/d/e/f/")),
381            ),
382            ("http://x/a/b/c", "?", Some(uri_ref!("http://x/a/b/c?"))),
383            ("http://x", "a/b/c", Some(uri_ref!("http://x/a/b/c"))),
384            ("http://x", "/a/b/c", Some(uri_ref!("http://x/a/b/c"))),
385            ("http://x/", "a/b/c", Some(uri_ref!("http://x/a/b/c"))),
386            ("http://x/a/b/c", "coap://x", Some(uri_ref!("coap://x"))),
387        ];
388
389        for (a, b, c) in uri_test_table {
390            let uri_a = UriRef::from_str(a).expect(a);
391            let uri_b = UriRef::from_str(b).expect(b);
392            assert_eq!(
393                uri_a.resolved(uri_b).ok(),
394                c.map(|x| x.to_owned()),
395                "uri_a.resolved(): a:{} b:{} c:{:?}",
396                a,
397                b,
398                c
399            );
400        }
401    }
402
403    #[test]
404    fn resolve_relative_base() {
405        let uri_test_table = vec![
406            ("b/c/d;p?q", "g:h", Some(uri_ref!("g:h"))),
407            ("b/c/d;p?q", "g", Some(uri_ref!("b/c/g"))),
408            ("b/c/d;p?q", "./g", Some(uri_ref!("b/c/g"))),
409            ("b/c/d;p?q", "g/", Some(uri_ref!("b/c/g/"))),
410            ("b/c/d;p?q", "/g", Some(uri_ref!("/g"))),
411            ("b/c/d;p?q", "//g", Some(uri_ref!("//g"))),
412            ("b/c/d;p?q", "?y", Some(uri_ref!("b/c/d;p?y"))),
413            ("b/c/d;p?q", "g?y", Some(uri_ref!("b/c/g?y"))),
414            ("b/c/d;p?q", "#s", Some(uri_ref!("b/c/d;p?q#s"))),
415            ("b/c/d;p?q", "g#s", Some(uri_ref!("b/c/g#s"))),
416            ("b/c/d;p?q", "g?y#s", Some(uri_ref!("b/c/g?y#s"))),
417            ("b/c/d;p?q", ";x", Some(uri_ref!("b/c/;x"))),
418            ("b/c/d;p?q", "g;x", Some(uri_ref!("b/c/g;x"))),
419            ("b/c/d;p?q", "g;x?y#s", Some(uri_ref!("b/c/g;x?y#s"))),
420            ("b/c/d;p?q", "", Some(uri_ref!("b/c/d;p?q"))),
421            ("b/c/d;p?q", ".", Some(uri_ref!("b/c/"))),
422            ("b/c/d;p?q", "./", Some(uri_ref!("b/c/"))),
423            ("b/c/d;p?q", "/./g", Some(uri_ref!("/g"))),
424            ("b/c/d;p?q", "g.", Some(uri_ref!("b/c/g."))),
425            ("b/c/d;p?q", ".g", Some(uri_ref!("b/c/.g"))),
426            ("b/c/d;p?q", "g..", Some(uri_ref!("b/c/g.."))),
427            ("b/c/d;p?q", "..g", Some(uri_ref!("b/c/..g"))),
428            ("b/c/d;p?q", "g?y/./x", Some(uri_ref!("b/c/g?y/./x"))),
429            ("b/c/d;p?q", "g?y/../x", Some(uri_ref!("b/c/g?y/../x"))),
430            ("b/c/d;p?q", "g#s/./x", Some(uri_ref!("b/c/g#s/./x"))),
431            ("b/c/d;p?q", "g#s/../x", Some(uri_ref!("b/c/g#s/../x"))),
432            ("b/c/d;p?q", "..", Some(uri_ref!("b/"))),
433            ("b/c/d;p?q", "../", Some(uri_ref!("b/"))),
434            ("b/c/d;p?q", "../g", Some(uri_ref!("b/g"))),
435            ("b/c/d;p?q", "../..", Some(uri_ref!("."))),
436            ("b/c/d;p?q", "../../", Some(uri_ref!("."))),
437            ("b/c/d;p?q", "../../g", Some(uri_ref!("g"))),
438            ("b/c/d;p?q", "../../../g", Some(uri_ref!("../g"))),
439            ("b/c/d;p?q", "../../../../g", Some(uri_ref!("../../g"))),
440            ("b/c/d;p?q", "/../g", Some(uri_ref!("/g"))),
441            ("b/c/d;p?q", "./../g", Some(uri_ref!("b/g"))),
442            ("b/c/d;p?q", "./g/.", Some(uri_ref!("b/c/g/"))),
443            ("b/c/d;p?q", "g/./h", Some(uri_ref!("b/c/g/h"))),
444            ("b/c/d;p?q", "g/../h", Some(uri_ref!("b/c/h"))),
445            ("b/c/d;p?q", "g;x=1/./y", Some(uri_ref!("b/c/g;x=1/y"))),
446            ("b/c/d;p?q", "g;x=1/../y", Some(uri_ref!("b/c/y"))),
447        ];
448
449        for (a, b, c) in uri_test_table {
450            let uri_a = UriRef::from_str(a).expect(a);
451            let uri_b = UriRef::from_str(b).expect(b);
452            assert_eq!(
453                uri_a.resolved(uri_b).ok(),
454                c.map(|x| x.to_owned()),
455                "uri_a.resolved(): a:{} b:{} c:{:?}",
456                a,
457                b,
458                c
459            );
460        }
461    }
462
463    #[test]
464    fn resolve_cannot_be_a_base() {
465        let uri_test_table = vec![
466            ("s:123", "/a/b/c", None),
467            ("s:123", "//a/b/c", None),
468            ("s:123", ".", None),
469            ("s:123", "", Some(uri_ref!("s:123"))),
470            ("s:123", "?q=123", Some(uri_ref!("s:123?q=123"))),
471            ("s:123", "#frag", Some(uri_ref!("s:123#frag"))),
472            ("s:123", "#frag", Some(uri_ref!("s:123#frag"))),
473            ("s:123", "file:/d/e/f", Some(uri_ref!("file:/d/e/f"))),
474        ];
475
476        for (a, b, c) in uri_test_table {
477            let uri_a = UriRef::from_str(a).expect(a);
478            let uri_b = UriRef::from_str(b).expect(b);
479            assert_eq!(
480                uri_a.resolved(uri_b).ok(),
481                c.map(|x| x.to_owned()),
482                "uri_a.resolved(): a:{} b:{} c:{:?}",
483                a,
484                b,
485                c
486            );
487        }
488    }
489
490    #[test]
491    fn resolve_no_authority() {
492        let uri_test_table = vec![
493            ("file:/d/e/f", "//a/b/c", Some(uri_ref!("file://a/b/c"))),
494            ("file:/d/e/f", "g", Some(uri_ref!("file:/d/e/g"))),
495        ];
496
497        for (a, b, c) in uri_test_table {
498            let uri_a = UriRef::from_str(a).expect(a);
499            let uri_b = UriRef::from_str(b).expect(b);
500            assert_eq!(
501                uri_a.resolved(uri_b).ok(),
502                c.map(|x| x.to_owned()),
503                "uri_a.resolved(): a:{} b:{} c:{:?}",
504                a,
505                b,
506                c
507            );
508        }
509    }
510
511    #[test]
512    fn resolve_rfc3986_simple() {
513        let uri_test_table = vec![
514            // The following test vectors came directly from RFC3986
515            ("http://a/b/c/d;p?q", "g:h", Some(uri_ref!("g:h"))),
516            ("http://a/b/c/d;p?q", "g", Some(uri_ref!("http://a/b/c/g"))),
517            (
518                "http://a/b/c/d;p?q",
519                "./g",
520                Some(uri_ref!("http://a/b/c/g")),
521            ),
522            (
523                "http://a/b/c/d;p?q",
524                "g/",
525                Some(uri_ref!("http://a/b/c/g/")),
526            ),
527            ("http://a/b/c/d;p?q", "/g", Some(uri_ref!("http://a/g"))),
528            ("http://a/b/c/d;p?q", "//g", Some(uri_ref!("http://g"))),
529            (
530                "http://a/b/c/d;p?q",
531                "?y",
532                Some(uri_ref!("http://a/b/c/d;p?y")),
533            ),
534            (
535                "http://a/b/c/d;p?q",
536                "g?y",
537                Some(uri_ref!("http://a/b/c/g?y")),
538            ),
539            (
540                "http://a/b/c/d;p?q",
541                "#s",
542                Some(uri_ref!("http://a/b/c/d;p?q#s")),
543            ),
544            (
545                "http://a/b/c/d;p?q",
546                "g#s",
547                Some(uri_ref!("http://a/b/c/g#s")),
548            ),
549            (
550                "http://a/b/c/d;p?q",
551                "g?y#s",
552                Some(uri_ref!("http://a/b/c/g?y#s")),
553            ),
554            (
555                "http://a/b/c/d;p?q",
556                ";x",
557                Some(uri_ref!("http://a/b/c/;x")),
558            ),
559            (
560                "http://a/b/c/d;p?q",
561                "g;x",
562                Some(uri_ref!("http://a/b/c/g;x")),
563            ),
564            (
565                "http://a/b/c/d;p?q",
566                "g;x?y#s",
567                Some(uri_ref!("http://a/b/c/g;x?y#s")),
568            ),
569            (
570                "http://a/b/c/d;p?q",
571                "",
572                Some(uri_ref!("http://a/b/c/d;p?q")),
573            ),
574            ("http://a/b/c/d;p?q", ".", Some(uri_ref!("http://a/b/c/"))),
575            ("http://a/b/c/d;p?q", "./", Some(uri_ref!("http://a/b/c/"))),
576            ("http://a/b/c/d;p?q", "/./g", Some(uri_ref!("http://a/g"))),
577            (
578                "http://a/b/c/d;p?q",
579                "g.",
580                Some(uri_ref!("http://a/b/c/g.")),
581            ),
582            (
583                "http://a/b/c/d;p?q",
584                ".g",
585                Some(uri_ref!("http://a/b/c/.g")),
586            ),
587            (
588                "http://a/b/c/d;p?q",
589                "g..",
590                Some(uri_ref!("http://a/b/c/g..")),
591            ),
592            (
593                "http://a/b/c/d;p?q",
594                "..g",
595                Some(uri_ref!("http://a/b/c/..g")),
596            ),
597            (
598                "http://a/b/c/d;p?q",
599                "g?y/./x",
600                Some(uri_ref!("http://a/b/c/g?y/./x")),
601            ),
602            (
603                "http://a/b/c/d;p?q",
604                "g?y/../x",
605                Some(uri_ref!("http://a/b/c/g?y/../x")),
606            ),
607            (
608                "http://a/b/c/d;p?q",
609                "g#s/./x",
610                Some(uri_ref!("http://a/b/c/g#s/./x")),
611            ),
612            (
613                "http://a/b/c/d;p?q",
614                "g#s/../x",
615                Some(uri_ref!("http://a/b/c/g#s/../x")),
616            ),
617        ];
618
619        for (a, b, c) in uri_test_table {
620            let uri_a = UriRef::from_str(a).expect(a);
621            let uri_b = UriRef::from_str(b).expect(b);
622            assert_eq!(
623                uri_a.resolved(uri_b).ok(),
624                c.map(|x| x.to_owned()),
625                "uri_a.resolved(): a:{} b:{} c:{:?}",
626                a,
627                b,
628                c
629            );
630        }
631    }
632
633    #[test]
634    fn resolve_rfc3986_dot_dot() {
635        let uri_test_table = vec![
636            // The following test vectors came directly from RFC3986
637            ("http://a/b/c/d;p?q", "..", Some(uri_ref!("http://a/b/"))),
638            ("http://a/b/c/d;p?q", "../", Some(uri_ref!("http://a/b/"))),
639            ("http://a/b/c/d;p?q", "../g", Some(uri_ref!("http://a/b/g"))),
640            ("http://a/b/c/d;p?q", "../..", Some(uri_ref!("http://a/"))),
641            ("http://a/b/c/d;p?q", "../../", Some(uri_ref!("http://a/"))),
642            (
643                "http://a/b/c/d;p?q",
644                "../../g",
645                Some(uri_ref!("http://a/g")),
646            ),
647            (
648                "http://a/b/c/d;p?q",
649                "../../../g",
650                Some(uri_ref!("http://a/g")),
651            ),
652            (
653                "http://a/b/c/d;p?q",
654                "../../../../g",
655                Some(uri_ref!("http://a/g")),
656            ),
657            ("http://a/b/c/d;p?q", "/../g", Some(uri_ref!("http://a/g"))),
658            (
659                "http://a/b/c/d;p?q",
660                "./../g",
661                Some(uri_ref!("http://a/b/g")),
662            ),
663            (
664                "http://a/b/c/d;p?q",
665                "./g/.",
666                Some(uri_ref!("http://a/b/c/g/")),
667            ),
668            (
669                "http://a/b/c/d;p?q",
670                "g/./h",
671                Some(uri_ref!("http://a/b/c/g/h")),
672            ),
673            (
674                "http://a/b/c/d;p?q",
675                "g/../h",
676                Some(uri_ref!("http://a/b/c/h")),
677            ),
678            (
679                "http://a/b/c/d;p?q",
680                "g;x=1/./y",
681                Some(uri_ref!("http://a/b/c/g;x=1/y")),
682            ),
683            (
684                "http://a/b/c/d;p?q",
685                "g;x=1/../y",
686                Some(uri_ref!("http://a/b/c/y")),
687            ),
688        ];
689
690        for (a, b, c) in uri_test_table {
691            let uri_a = UriRef::from_str(a).expect(a);
692            let uri_b = UriRef::from_str(b).expect(b);
693            assert_eq!(
694                uri_a.resolved(uri_b).ok(),
695                c.map(|x| x.to_owned()),
696                "uri_a.resolved(): a:{} b:{} c:{:?}",
697                a,
698                b,
699                c
700            );
701        }
702    }
703}