async_coap_uri/
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::ops::{Deref, Range};
18use core::str::FromStr;
19
20#[cfg(feature = "std")]
21use std::borrow::Cow;
22
23/// Unsized string-slice type guaranteed to contain a well-formed [IETF-RFC3986] [URI-reference].
24///
25/// From [IETF-RFC3986 Section 4.1][URI-reference]:
26///
27/// > \[A\] URI-reference is used to denote the most common usage of a resource
28/// > identifier.
29/// >
30/// > ```abnf
31/// > URI-reference = URI / relative-ref
32/// > ```
33/// >
34/// > A URI-reference is either a URI or a relative reference.  If the
35/// > URI-reference's prefix does not match the syntax of a scheme followed
36/// > by its colon separator, then the URI-reference is a relative
37/// > reference.
38///
39/// [`UriRef`] is similar to [`str`] in that it is an unsized type and is generally only seen
40/// in its borrowed form: `&UriRef`. The big difference between `str` and `UriRef` is that `UriRef`
41/// guarantees that it contains a well-formed URI-reference.
42///
43/// The sized counterpart is [`UriRefBuf`], but the underlying data can be owned by just about
44/// anything.
45///
46/// ## Examples
47///
48/// String literals can be made into `URI-reference` using the [`uri_ref!`] macro:
49///
50/// [`uri_ref!`]: macro.uri_ref.html
51///
52/// ```
53/// use async_coap_uri::prelude::*;
54///
55/// let uri = uri_ref!("http://example.com/test");
56/// ```
57///
58/// Depending on your needs, you can access the raw (escaped) components individually, or
59/// calculate them all at once using [`UriRawComponents`]:
60///
61/// ```
62/// # use async_coap_uri::*;
63/// # let uri = uri_ref!("http://example.com/test");
64/// #
65/// // Accessed and calculated individually...
66/// assert_eq!(Some("http"), uri.scheme());
67/// assert_eq!(Some("example.com"), uri.raw_authority());
68/// assert_eq!("/test", uri.raw_path());
69///
70/// // ...or calculate all of them at once.
71/// let components = uri.components();
72/// assert_eq!(Some("http"), components.scheme());
73/// assert_eq!(Some("example.com"), components.raw_authority());
74/// assert_eq!("/test", components.raw_path());
75/// ```
76///
77/// [IETF-RFC3986]: https://tools.ietf.org/html/rfc3986
78/// [URI-reference]: https://tools.ietf.org/html/rfc3986#section-4.1
79#[derive(Eq, Hash)]
80pub struct UriRef(pub(super) str);
81
82_impl_uri_traits_base!(UriRef);
83
84impl Deref for UriRef {
85    type Target = str;
86    fn deref(&self) -> &Self::Target {
87        self.as_str()
88    }
89}
90
91/// The default `&UriRef` is an empty URI-reference.
92impl Default for &UriRef {
93    fn default() -> Self {
94        uri_ref!("")
95    }
96}
97
98/// The default `&mut UriRef` is an empty URI-reference.
99///
100/// Note that even though it is technically mutable, because the slice
101/// is empty it is mutable in name only.
102impl Default for &mut UriRef {
103    fn default() -> Self {
104        use std::slice::from_raw_parts_mut;
105        use std::str::from_utf8_unchecked_mut;
106        unsafe {
107            // SAFETY: An empty slice is pretty harmless, mutable or not.
108            let empty_slice = from_raw_parts_mut(0usize as *mut u8, 0);
109            let empty_string = from_utf8_unchecked_mut(empty_slice);
110            UriRef::from_str_unchecked_mut(empty_string)
111        }
112    }
113}
114
115impl AnyUriRef for UriRef {
116    fn write_to<T: core::fmt::Write + ?Sized>(
117        &self,
118        write: &mut T,
119    ) -> Result<(), core::fmt::Error> {
120        write.write_str(self.as_str())
121    }
122
123    fn is_empty(&self) -> bool {
124        self.0.is_empty()
125    }
126
127    fn uri_type(&self) -> UriType {
128        if self.starts_with('#') {
129            return UriType::Fragment;
130        }
131
132        if self.starts_with('?') {
133            return UriType::Query;
134        }
135
136        if self.starts_with("//") {
137            return UriType::NetworkPath;
138        }
139
140        if self.starts_with('/') {
141            return UriType::AbsolutePath;
142        }
143
144        let pat = |c| c == ':' || c == '/' || c == '?' || c == '#';
145        if let Some(i) = self.find(pat) {
146            if self[i..].starts_with("://") {
147                return UriType::Uri;
148            } else if self[i..].starts_with(":/") {
149                return UriType::UriNoAuthority;
150            } else if self[i..].starts_with(':') {
151                return UriType::UriCannotBeABase;
152            }
153        }
154
155        return UriType::RelativePath;
156    }
157
158    fn components(&self) -> UriRawComponents<'_> {
159        UriRawComponents::from_str(self.as_str()).unwrap()
160    }
161}
162
163impl std::fmt::Display for UriRef {
164    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
165        self.write_to(f)
166    }
167}
168
169impl UriRef {
170    /// Attempts to convert a string slice into a [`&UriRef`](UriRef), returning `Err(ParseError)`
171    /// if the string slice contains data that is not a valid URI reference.
172    ///
173    /// Example:
174    ///
175    /// ```
176    /// use async_coap_uri::prelude::*;
177    /// assert_eq!(UriRef::from_str("http://example.com"), Ok(uri_ref!("http://example.com")));
178    /// assert_eq!(UriRef::from_str("/a/b/c"), Ok(uri_ref!("/a/b/c")));
179    /// assert_eq!(UriRef::from_str("I N V A L I D").err().unwrap().span(), Some(1..2));
180    /// ```
181    pub fn from_str(s: &str) -> Result<&UriRef, ParseError> {
182        UriRawComponents::from_str(s)?;
183        Ok(unsafe { Self::from_str_unchecked(s.as_ref()) })
184    }
185
186    /// Determines if the given string can be considered a valid URI reference.
187    ///
188    /// The key difference between this and [`Uri::is_str_valid`] is that this
189    /// function will return true for relative-references like `/a/b/c`, whereas
190    /// [`Uri::is_str_valid`] would return false.
191    ///
192    /// Example:
193    ///
194    /// ```
195    /// use async_coap_uri::UriRef;
196    /// assert!(UriRef::is_str_valid("http://example.com"));
197    /// assert!(UriRef::is_str_valid("/a/b/c"));
198    /// assert!(!UriRef::is_str_valid("Not a URI or relative reference"));
199    /// ```
200    pub fn is_str_valid<S: AsRef<str>>(s: S) -> bool {
201        let str_ref = s.as_ref();
202
203        // TODO: Replace this with an optimized validity check.
204        //       We are currently using `UriRawComponents::from_str()` as a crutch here;
205        //       it includes extraneous operations that are not related to verifying if a
206        //       URI is well-formed.
207        UriRawComponents::from_str(str_ref).is_ok()
208    }
209
210    /// Returns this URI-reference as a string slice.
211    #[inline(always)]
212    pub const fn as_str(&self) -> &str {
213        &self.0
214    }
215
216    /// Attempts to interpret this [`&UriRef`][UriRef] as a [`&Uri`][Uri], returning `None`
217    /// if this `UriRef` doesn't contain a proper URI.
218    pub fn as_uri(&self) -> Option<&Uri> {
219        if self.uri_type().can_borrow_as_uri() {
220            Some(unsafe { Uri::from_str_unchecked(self.as_str()) })
221        } else {
222            None
223        }
224    }
225
226    /// Attempts to interpret this [`&UriRef`][UriRef] as a [`&RelRef`][RelRef], returning `None`
227    /// if this `UriRef` doesn't contain a relative-reference.
228    pub fn as_rel_ref(&self) -> Option<&RelRef> {
229        if self.uri_type().can_borrow_as_rel_ref() {
230            Some(unsafe { RelRef::from_str_unchecked(self.as_str()) })
231        } else {
232            None
233        }
234    }
235}
236
237/// ## Indexing Methods
238impl UriRef {
239    /// Returns the index to the start of `heir-part`, as defined in IETF-RFC3986.
240    pub fn heir_part_start(&self) -> usize {
241        let pat = |c| c == ':' || c == '/' || c == '?' || c == '#';
242        if let Some(i) = self.find(pat) {
243            if self[i..].starts_with(':') {
244                return i + 1;
245            }
246        }
247        0
248    }
249
250    /// Returns the index to the first character in the path. Will
251    /// return `len()` if there is no path, query, or fragment.
252    /// If the return value is zero, then `self` is guaranteed to be
253    /// a relative reference.
254    ///
255    /// ## Examples
256    ///
257    /// ```
258    /// use async_coap_uri::prelude::*;
259    /// assert_eq!(uri_ref!("a").path_start(),               0);
260    /// assert_eq!(uri_ref!("a/b/c?blah#frag").path_start(), 0);
261    /// assert_eq!(uri_ref!("/a").path_start(),              0);
262    /// assert_eq!(uri_ref!("//foo/?bar").path_start(),      5);
263    /// assert_eq!(uri_ref!("http://a/#frag").path_start(),  8);
264    /// assert_eq!(uri_ref!("http://a").path_start(),  8);
265    /// ```
266    pub fn path_start(&self) -> usize {
267        let heir_part_start = self.heir_part_start();
268        let heir_part = &self[heir_part_start..];
269
270        if heir_part.starts_with("//") {
271            let authority = &heir_part[2..];
272
273            // Find the end of the authority.
274            let pat = |c| c == '/' || c == '?' || c == '#';
275            if let Some(j) = authority.find(pat) {
276                heir_part_start + 2 + j
277            } else {
278                self.len()
279            }
280        } else {
281            heir_part_start
282        }
283    }
284
285    /// Returns the index of the end of the path
286    pub fn path_end(&self) -> usize {
287        let pat = |c| c == '?' || c == '#';
288        if let Some(i) = self.find(pat) {
289            i
290        } else {
291            self.len()
292        }
293    }
294
295    /// Returns the index of the start of the query, including the `?`.
296    /// If there is no query, returns `None`.
297    pub fn query_start(&self) -> Option<usize> {
298        let pat = |c| c == '?' || c == '#';
299        if let Some(i) = self.find(pat) {
300            if self[i..].starts_with('?') {
301                return Some(i);
302            }
303        }
304        None
305    }
306
307    /// Returns the index of the start of the fragment, including the `#`.
308    /// If there is no fragment, returns `None`.
309    pub fn fragment_start(&self) -> Option<usize> {
310        self.find('#')
311    }
312
313    /// Returns the byte index range that contains the authority, if present.
314    pub fn authority_range(&self) -> Option<Range<usize>> {
315        let pat = |c| c == '/' || c == '?' || c == '#';
316        if let Some(i) = self.find(pat) {
317            let step1 = &self[i..];
318            if !step1.starts_with("//") {
319                return None;
320            }
321            let step2 = &step1[2..];
322            if let Some(j) = step2.find(pat) {
323                return Some(i + 2..i + 2 + j);
324            } else {
325                return Some(i + 2..self.len());
326            }
327        }
328        None
329    }
330}
331
332/// ## Splitting
333impl UriRef {
334    /// Splits this URI into the base and relative portions.
335    pub fn split(&self) -> (Option<&Uri>, &RelRef) {
336        let path_start = self.path_start();
337        if path_start == 0 {
338            // This is a relative URI, so there is no base part.
339            (None, unsafe { RelRef::from_str_unchecked(self.as_str()) })
340        } else {
341            let (base, rel) = self.split_at(path_start);
342            let base = unsafe { Uri::from_str_unchecked(base) };
343            let rel = unsafe { RelRef::from_str_unchecked(rel) };
344
345            (Some(base), rel)
346        }
347    }
348
349    /// Splits this URI into the base and relative portions.
350    pub fn split_mut(&mut self) -> (Option<&mut Uri>, &mut RelRef) {
351        let path_start = self.path_start();
352        if path_start == 0 {
353            // This is a relative URI, so there is no base part.
354            (None, unsafe {
355                // SAFETY: If path_start() returns zero, this is guaranteed to be a
356                //         URI-reference.
357                RelRef::from_str_unchecked_mut(self.as_mut_str())
358            })
359        } else {
360            unsafe {
361                let (base, rel) = self.as_mut_str().split_at_mut(path_start);
362                (
363                    Some(Uri::from_str_unchecked_mut(base)),
364                    RelRef::from_str_unchecked_mut(rel),
365                )
366            }
367        }
368    }
369
370    /// Returns the subset of this URI that contains the scheme and authority, without the
371    /// path, query, or fragment. If this is possible, the result is a `&Uri`.
372    pub fn base(&self) -> Option<&Uri> {
373        self.split().0
374    }
375
376    /// Returns this URI as a `&RelRef`, including only the path, query, and fragment.
377    /// If this URI is already relative, this method simply returns the given URI
378    /// unchanged.
379    pub fn rel(&self) -> &RelRef {
380        self.split().1
381    }
382
383    /// Returns this URI as a `&mut RelRef`, including only the path, query, and fragment.
384    /// If this URI is already relative, this method simply returns the given URI
385    /// unchanged.
386    pub fn rel_mut(&mut self) -> &mut RelRef {
387        self.split_mut().1
388    }
389}
390
391/// ## Component Accessors
392impl UriRef {
393    /// Returns the scheme of this URI, if it has a scheme.
394    /// The returned string can be used directly and does not need to be unescaped
395    /// or percent-decoded.
396    pub fn scheme(&self) -> Option<&str> {
397        let pat = |c| c == ':' || c == '/' || c == '?' || c == '#';
398        if let Some(i) = self.find(pat) {
399            if self[i..].starts_with(':') {
400                return Some(&self[..i]);
401            }
402        }
403        None
404    }
405
406    /// Returns the percent-encoded authority part of this URI, if it has one.
407    ///
408    /// The unescaped version of this method is [`UriRef::authority`].
409    ///
410    /// In general, this value should not be used directly. Most users will find the
411    /// method [`raw_userinfo_host_port`](#method.raw_userinfo_host_port) to be a more
412    /// useful extraction.
413    ///
414    /// See [`StrExt`] for more details on unescaping.
415    pub fn raw_authority(&self) -> Option<&str> {
416        Some(&self[self.authority_range()?])
417    }
418
419    /// Percent-decoded version of [`UriRef::raw_authority`], using `std::borrow::Cow<str>` instead of `&str`.
420    #[cfg(feature = "std")]
421    pub fn authority(&self) -> Option<Cow<'_, str>> {
422        self.raw_authority().map(|f| f.unescape_uri().to_cow())
423    }
424
425    /// Returns a tuple containing the raw userinfo, raw host, and port number from the
426    /// authority component.
427    ///
428    /// The percent-decoded version of this method is [`UriRef::userinfo_host_port`].
429    ///
430    /// The userinfo and host should be unescaped before being used. See [`StrExt`]
431    /// for more details.
432    pub fn raw_userinfo_host_port(&self) -> Option<(Option<&str>, &str, Option<u16>)> {
433        let authority = self.raw_authority()?;
434        let userinfo;
435        let host_and_port;
436
437        if let Some(i) = authority.find('@') {
438            userinfo = Some(&authority[..i]);
439            host_and_port = &authority[i + 1..];
440        } else {
441            userinfo = None;
442            host_and_port = authority;
443        }
444
445        let host;
446        let port;
447
448        if host_and_port.starts_with('[') {
449            if let Some(i) = host_and_port.rfind("]:") {
450                host = &host_and_port[1..i];
451                port = u16::from_str(&host_and_port[i + 2..]).ok();
452            } else {
453                host = &host_and_port[1..host_and_port.len() - 1];
454                port = None;
455            }
456        } else {
457            if let Some(i) = host_and_port.rfind(':') {
458                host = &host_and_port[..i];
459                port = u16::from_str(&host_and_port[i + 1..]).ok();
460            } else {
461                host = host_and_port;
462                port = None;
463            }
464        }
465
466        Some((userinfo, host, port))
467    }
468
469    /// Percent-decoded version of [`UriRef::raw_userinfo_host_port`], where the unescaped parts are
470    /// represented as `std::borrow::Cow<str>` instances.
471    #[cfg(feature = "std")]
472    pub fn userinfo_host_port(&self) -> Option<(Option<Cow<'_, str>>, Cow<'_, str>, Option<u16>)> {
473        self.raw_userinfo_host_port().map(|item| {
474            (
475                item.0.map(|s| s.unescape_uri().to_cow()),
476                item.1.unescape_uri().to_cow(),
477                item.2,
478            )
479        })
480    }
481
482    /// Percent-decoded *host* as a `std::borrow::Cow<str>`, if present.
483    #[cfg(feature = "std")]
484    pub fn host(&self) -> Option<Cow<'_, str>> {
485        self.raw_userinfo_host_port()
486            .map(|item| item.1.unescape_uri().to_cow())
487    }
488
489    /// Returns a string slice containing the raw, percent-encoded value of the path.
490    ///
491    /// There is no unescaped version of this method because the resulting ambiguity
492    /// of percent-encoded slashes (`/`) present a security risk. Use [`path_segments`] if
493    /// you need an escaped version.
494    ///
495    /// If you absolutely must, you can use the following
496    /// code to obtain a lossy, percent-decoded version of the path that doesn't decode
497    /// `%2F` into slash characters:
498    ///
499    /// ```
500    /// # use async_coap_uri::prelude::*;
501    /// let path = uri_ref!("%2F../a/%23/")
502    ///     .raw_path()
503    ///     .unescape_uri()
504    ///     .skip_slashes()
505    ///     .to_string();
506    ///
507    /// assert_eq!(path, "%2F../a/#/");
508    /// ```
509    ///
510    /// [`path_segments`]: UriRef::path_segments
511    #[must_use]
512    #[inline(always)]
513    pub fn raw_path(&self) -> &str {
514        self.path_as_rel_ref().as_str()
515    }
516
517    /// Returns the subset of this URI that is a path, without the
518    /// scheme, authority, query, or fragment. Since this is itself
519    /// a valid relative URI, it returns a `&RelRef`.
520    pub fn path_as_rel_ref(&self) -> &RelRef {
521        self.rel().path_as_rel_ref()
522    }
523
524    /// Returns the subset of this URI that is a path and query, without the
525    /// scheme, authority, or fragment. Since this is itself
526    /// a valid relative URI, it returns a `&RelRef`.
527    pub fn path_query_as_rel_ref(&self) -> &RelRef {
528        self.trim_fragment().rel()
529    }
530
531    /// An iterator which returns each individual raw, percent-encoded *path segment*.
532    ///
533    /// The percent-decoded (unescaped) version of this method is [`UriRef::path_segments`].
534    ///
535    /// The values returned by this iterator should be unescaped before being used.
536    /// See [`StrExt`] and [`StrExt::unescape_uri`] for more details.
537    ///
538    /// ## Example
539    ///
540    /// ```
541    /// use async_coap_uri::prelude::*;
542    /// let rel_ref = uri_ref!("g:a/%2F/bl%c3%a5b%c3%a6r");
543    /// let mut iter = rel_ref.raw_path_segments();
544    ///
545    /// assert_eq!(iter.next(), Some("a"));
546    /// assert_eq!(iter.next(), Some("%2F"));
547    /// assert_eq!(iter.next(), Some("bl%c3%a5b%c3%a6r"));
548    /// assert_eq!(iter.next(), None);
549    /// ```
550    pub fn raw_path_segments(&self) -> impl Iterator<Item = &str> {
551        self.rel().raw_path_segments()
552    }
553
554    /// Percent-decoded (unescaped) version of [`UriRef::raw_path_segments`], using
555    /// `std::borrow::Cow<str>` instead of `&str`.
556    ///
557    /// ## Example
558    ///
559    /// ```
560    /// use async_coap_uri::prelude::*;
561    /// use std::borrow::Cow;
562    /// let uri_ref = uri_ref!("g:a/%2F/bl%c3%a5b%c3%a6r");
563    /// let mut iter = uri_ref.path_segments();
564    ///
565    /// assert_eq!(iter.next(), Some(Cow::from("a")));
566    /// assert_eq!(iter.next(), Some(Cow::from("/")));
567    /// assert_eq!(iter.next(), Some(Cow::from("blåbær")));
568    /// assert_eq!(iter.next(), None);
569    /// ```
570    #[cfg(feature = "std")]
571    pub fn path_segments(&self) -> impl Iterator<Item = Cow<'_, str>> {
572        self.raw_path_segments()
573            .map(|item| item.unescape_uri().to_cow())
574    }
575
576    /// Returns the subset of this URI that is the query and fragment, without the
577    /// scheme, authority, or path. This method includes the
578    /// `?` prefix, making it a valid relative URI.
579    pub fn query_fragment_as_rel_ref(&self) -> Option<&RelRef> {
580        if let Some(i) = self.query_start() {
581            Some(unsafe { RelRef::from_str_unchecked(&self[i..]) })
582        } else {
583            None
584        }
585    }
586
587    /// Returns the subset of this URI that is the query, without the
588    /// scheme, authority, path, or fragment. This method includes the
589    /// `?` prefix, making it a valid relative URI.
590    pub fn query_as_rel_ref(&self) -> Option<&RelRef> {
591        self.trim_fragment().query_fragment_as_rel_ref()
592    }
593
594    /// Returns the escaped slice of the URI that contains the "query", if present.
595    ///
596    /// There is no unescaped version of this method because the resulting ambiguity
597    /// of percent-encoded ampersands (`&`) and semicolons (`;`) present a security risk.
598    /// Use [`query_items`] or [`query_key_values`] if you need a percent-decoded version.
599    ///
600    /// [`query_items`]: UriRef::query_items
601    /// [`query_key_values`]: UriRef::query_key_values
602    pub fn raw_query(&self) -> Option<&str> {
603        self.query_as_rel_ref().map(|s| &s[1..])
604    }
605
606    /// Returns an iterator that iterates over all of the query items.
607    ///
608    /// Both `;` and `&` are acceptable query item delimiters.
609    ///
610    /// The percent-decoded version of this method is [`UriRef::query_items`].
611    ///
612    /// The values returned by this iterator should be unescaped before being used.
613    /// See [`StrExt`] and [`StrExt::unescape_uri`] for more details.
614    ///
615    /// ## Example
616    ///
617    /// ```
618    /// use async_coap_uri::prelude::*;
619    /// let uri_ref = uri_ref!("/a/b/c?q=this:is&q=fun&q=bl%c3%a5b%c3%a6rsyltet%c3%b8y");
620    /// let mut iter = uri_ref.raw_query_items();
621    ///
622    /// assert_eq!(iter.next(), Some("q=this:is"));
623    /// assert_eq!(iter.next(), Some("q=fun"));
624    /// assert_eq!(iter.next(), Some("q=bl%c3%a5b%c3%a6rsyltet%c3%b8y"));
625    /// assert_eq!(iter.next(), None);
626    /// ```
627    pub fn raw_query_items(&self) -> impl Iterator<Item = &str> {
628        let pattern = |c| c == '&' || c == ';';
629        match self.raw_query() {
630            Some(query) => query.split(pattern),
631            None => {
632                let mut ret = "".split(pattern);
633                let _ = ret.next();
634                return ret;
635            }
636        }
637    }
638
639    /// Similar to [`raw_query_items()`], but additionally separates the key
640    /// from the value for each query item.
641    ///
642    /// The percent-decoded version of this method is [`UriRef::query_key_values`].
643    ///
644    /// Both keys and values are in their raw, escaped form. If you want escaped
645    /// values, consider [`query_key_values()`].
646    ///
647    /// [`raw_query_items()`]: #method.raw_query_items
648    /// [`query_key_values()`]: #method.query_key_values
649    ///
650    /// ## Example
651    ///
652    /// ```
653    /// use async_coap_uri::prelude::*;
654    /// let uri_ref = uri_ref!("/a/b/c?inc&a=ok&b=q=q&c=bl%c3%a5b%c3%a6r");
655    /// let mut iter = uri_ref.raw_query_key_values();
656    ///
657    /// assert_eq!(iter.next(), Some(("inc", "")));
658    /// assert_eq!(iter.next(), Some(("a", "ok")));
659    /// assert_eq!(iter.next(), Some(("b", "q=q")));
660    /// assert_eq!(iter.next(), Some(("c", "bl%c3%a5b%c3%a6r")));
661    /// assert_eq!(iter.next(), None);
662    /// ```
663    pub fn raw_query_key_values(&self) -> impl Iterator<Item = (&str, &str)> {
664        self.raw_query_items().map(|comp| match comp.find('=') {
665            Some(x) => {
666                let split = comp.split_at(x);
667                (split.0, &split.1[1..])
668            }
669            None => (comp, ""),
670        })
671    }
672
673    /// Percent-decoded version of [`UriRef::raw_query_items`], using `std::borrow::Cow<str>` instead of `&str`.
674    #[cfg(feature = "std")]
675    pub fn query_items(&self) -> impl Iterator<Item = Cow<'_, str>> {
676        self.raw_query_items()
677            .map(|item| item.unescape_uri().to_cow())
678    }
679
680    /// Similar to [`query_items()`], but additionally separates the key
681    /// from the value for each query item.
682    ///
683    /// Both keys and values are percent-decoded and ready-to-use. If you want them in their raw,
684    /// percent-encoded in form, use [`raw_query_key_values()`].
685    ///
686    /// This method uses the Copy-on-write type ([`std::borrow::Cow`]) to avoid unnecessary memory allocations.
687    ///
688    /// [`query_items()`]: #method.query_items
689    /// [`raw_query_key_values()`]: #method.raw_query_key_values
690    ///
691    /// ## Example
692    ///
693    /// ```
694    /// use async_coap_uri::prelude::*;
695    /// use std::borrow::Cow;
696    /// let uri_ref = uri_ref!("/a/b/c?inc&a=ok&b=q=q&c=bl%c3%a5b%c3%a6r");
697    /// let mut iter = uri_ref.query_key_values();
698    ///
699    /// assert_eq!(iter.next(), Some((Cow::from("inc"), Cow::from(""))));
700    /// assert_eq!(iter.next(), Some((Cow::from("a"), Cow::from("ok"))));
701    /// assert_eq!(iter.next(), Some((Cow::from("b"), Cow::from("q=q"))));
702    /// assert_eq!(iter.next(), Some((Cow::from("c"), Cow::from("blåbær"))));
703    /// assert_eq!(iter.next(), None);
704    /// ```
705    #[cfg(feature = "std")]
706    pub fn query_key_values(&self) -> impl Iterator<Item = (Cow<'_, str>, Cow<'_, str>)> {
707        self.raw_query_key_values().map(|item| {
708            (
709                item.0.unescape_uri().to_cow(),
710                item.1.unescape_uri().to_cow(),
711            )
712        })
713    }
714
715    /// Returns the subset of this URI that is the query, without the
716    /// scheme, authority, path, or query. This method includes the
717    /// `#` prefix, making it a valid relative URI.
718    pub fn fragment_as_rel_ref(&self) -> Option<&RelRef> {
719        if let Some(i) = self.fragment_start() {
720            Some(unsafe { RelRef::from_str_unchecked(&self[i..]) })
721        } else {
722            None
723        }
724    }
725
726    /// Returns a string slice containing the fragment, if any.
727    ///
728    /// The percent-decoded version of this method is [`UriRef::fragment`].
729    ///
730    /// This value should be unescaped before being used. See [`StrExt`] for more details.
731    pub fn raw_fragment(&self) -> Option<&str> {
732        self.fragment_as_rel_ref().map(|s| &s[1..])
733    }
734
735    /// Percent-decoded version of [`UriRef::raw_fragment`], using `std::borrow::Cow<str>` instead of `&str`.
736    #[cfg(feature = "std")]
737    pub fn fragment(&self) -> Option<Cow<'_, str>> {
738        self.raw_fragment().map(|f| f.unescape_uri().to_cow())
739    }
740}
741
742impl UriRef {
743    /// Returns true if the path has a trailing slash.
744    ///
745    /// ## Examples
746    ///
747    /// ```
748    /// use async_coap_uri::prelude::*;
749    /// assert!(uri_ref!("http://a/").has_trailing_slash());
750    /// assert!(uri_ref!("/a/").has_trailing_slash());
751    /// assert!(uri_ref!("a/").has_trailing_slash());
752    /// assert!(uri_ref!("http://a/?q=foo").has_trailing_slash());
753    ///
754    /// assert!(!uri_ref!("http://a").has_trailing_slash());
755    /// assert!(!uri_ref!("a/b").has_trailing_slash());
756    /// assert!(!uri_ref!("").has_trailing_slash());
757    /// ```
758    pub fn has_trailing_slash(&self) -> bool {
759        let path_end = self.path_end();
760        path_end > 0 && &self[path_end - 1..path_end] == "/"
761    }
762}
763
764/// ## Trimming
765impl UriRef {
766    /// Returns this URI-reference without a fragment.
767    ///
768    /// ## Examples
769    ///
770    /// ```
771    /// use async_coap_uri::prelude::*;
772    /// assert_eq!(uri_ref!("http://a/#frag").trim_fragment(),  uri_ref!("http://a/"));
773    /// assert_eq!(uri_ref!("a/b/c?blah#frag").trim_fragment(), uri_ref!("a/b/c?blah"));
774    /// ```
775    #[must_use = "this returns the trimmed uri as a new slice, \
776                  without modifying the original"]
777    pub fn trim_fragment(&self) -> &UriRef {
778        if let Some(i) = self.fragment_start() {
779            unsafe { Self::from_str_unchecked(&self[..i]) }
780        } else {
781            self
782        }
783    }
784
785    /// Returns this URI without a query or fragment.
786    ///
787    /// ## Examples
788    ///
789    /// ```
790    /// use async_coap_uri::prelude::*;
791    /// assert_eq!(uri_ref!("//foo/?bar").trim_query(),      uri_ref!("//foo/"));
792    /// assert_eq!(uri_ref!("a/b/c?blah#frag").trim_query(), uri_ref!("a/b/c"));
793    /// assert_eq!(uri_ref!("http://a/#frag").trim_query(),  uri_ref!("http://a/"));
794    /// ```
795    #[must_use = "this returns the trimmed uri as a new slice, \
796                  without modifying the original"]
797    pub fn trim_query(&self) -> &UriRef {
798        if let Some(i) = self.find(|c| c == '?' || c == '#') {
799            // SAFETY: Trimming on a boundary guaranteed not to be inside of an escaped byte.
800            unsafe { Self::from_str_unchecked(&self[..i]) }
801        } else {
802            self
803        }
804    }
805
806    /// Returns this URI without a path, query, or fragment.
807    ///
808    /// ## Examples
809    ///
810    /// ```
811    /// use async_coap_uri::prelude::*;
812    /// assert_eq!(uri_ref!("a/b/c?blah#frag").trim_path(), uri_ref!(""));
813    /// assert_eq!(uri_ref!("//foo/?bar").trim_path(),      uri_ref!("//foo"));
814    /// assert_eq!(uri_ref!("http://a/#frag").trim_path(),  uri_ref!("http://a"));
815    /// ```
816    #[must_use = "this returns the trimmed uri as a new slice, \
817                  without modifying the original"]
818    pub fn trim_path(&self) -> &UriRef {
819        let i = self.path_start();
820        unsafe { Self::from_str_unchecked(&self[..i]) }
821    }
822
823    /// Returns this URI without the "heir-part", or anything thereafter.
824    ///
825    /// ## Examples
826    ///
827    /// ```
828    /// use async_coap_uri::prelude::*;
829    /// assert_eq!(uri_ref!("a/b/c?blah#frag").trim_heir_part(), uri_ref!(""));
830    /// assert_eq!(uri_ref!("//foo/?bar").trim_heir_part(), uri_ref!(""));
831    /// assert_eq!(uri_ref!("http://a/#frag").trim_heir_part(), uri_ref!("http:"));
832    /// ```
833    #[must_use = "this returns the trimmed uri as a new slice, \
834                  without modifying the original"]
835    pub fn trim_heir_part(&self) -> &UriRef {
836        let i = self.heir_part_start();
837        unsafe { Self::from_str_unchecked(&self[..i]) }
838    }
839
840    /// Returns this URI without the trailing part of the path that would be
841    /// removed during relative-reference resolution.
842    ///
843    /// ## Examples
844    ///
845    /// ```
846    /// use async_coap_uri::prelude::*;
847    /// assert_eq!(uri_ref!("a").trim_resource(),               uri_ref!(""));
848    /// assert_eq!(uri_ref!("a/b/c?blah#frag").trim_resource(), uri_ref!("a/b/"));
849    /// assert_eq!(uri_ref!("/a").trim_resource(),              uri_ref!("/"));
850    /// assert_eq!(uri_ref!("//foo/?bar").trim_resource(),      uri_ref!("//foo/"));
851    /// assert_eq!(uri_ref!("http://a/#frag").trim_resource(),  uri_ref!("http://a/"));
852    /// ```
853    #[must_use = "this returns the trimmed uri as a new slice, \
854                  without modifying the original"]
855    pub fn trim_resource(&self) -> &UriRef {
856        let mut ret = self.trim_query();
857
858        let path_start = self.path_start();
859
860        if let Some(i) = ret.rfind('/') {
861            if i + 1 > path_start {
862                ret = unsafe { Self::from_str_unchecked(&self[..i + 1]) };
863            }
864        } else if path_start == 0 {
865            ret = uri_ref!("");
866        }
867
868        ret
869    }
870
871    /// Removes any trailing slash that might be at the end of the path, along with
872    /// the query and fragment.
873    ///
874    /// If the path consists of a single slash ("`/`"), then it is not removed.
875    ///
876    /// ## Examples
877    ///
878    /// ```
879    /// use async_coap_uri::prelude::*;
880    /// assert_eq!(uri_ref!("a").trim_trailing_slash(),                uri_ref!("a"));
881    /// assert_eq!(uri_ref!("a/b/c/?blah#frag").trim_trailing_slash(), uri_ref!("a/b/c"));
882    /// assert_eq!(uri_ref!("/").trim_trailing_slash(),                uri_ref!("/"));
883    /// assert_eq!(uri_ref!("//foo/?bar").trim_trailing_slash(),       uri_ref!("//foo/"));
884    /// assert_eq!(uri_ref!("http://a/#frag").trim_trailing_slash(),   uri_ref!("http://a/"));
885    /// ```
886    ///
887    /// Note that the uri-ref "`//`" (a network path with an empty authority and an empty path)
888    /// does not get its trailing slash removed because it technically isn't a part of the path.
889    /// Likewise, the uri-ref "`///`" doesn't get the last slash removed because this method
890    /// won't remove the first slash in the path. The uri-ref "`////`" however will have its
891    /// trailing slash removed:
892    ///
893    /// ```
894    /// # use async_coap_uri::prelude::*;
895    /// assert_eq!(uri_ref!("//").trim_trailing_slash(),    uri_ref!("//"));
896    /// assert_eq!(uri_ref!("///").trim_trailing_slash(),   uri_ref!("///"));
897    /// assert_eq!(uri_ref!("////").trim_trailing_slash(),  uri_ref!("///"));
898    /// ```
899    ///
900    #[must_use = "this returns the trimmed uri as a new slice, \
901                  without modifying the original"]
902    pub fn trim_trailing_slash(&self) -> &UriRef {
903        let path_end = self.path_end();
904        if path_end > self.path_start() + 1 && &self[path_end - 1..path_end] == "/" {
905            unsafe { Self::from_str_unchecked(&self[..path_end - 1]) }
906        } else {
907            self.trim_query()
908        }
909    }
910
911    /// Attempts to shorten this URI-reference given a base URI reference.
912    ///
913    /// The returned reference can then be resolved using the base to recover the
914    /// original URI reference.
915    ///
916    /// ```
917    /// use async_coap_uri::prelude::*;
918    /// let base = uri_ref!("http://example.com/a/b");
919    /// let target = uri_ref!("http://example.com/a/x/y/");
920    ///
921    /// let shortened = target.trim_to_shorten(base).expect("Unable to shorten");
922    /// assert_eq!(shortened, rel_ref!("x/y/"));
923    ///
924    /// let resolved = base.resolved(shortened).expect("Unable to resolve");
925    /// assert_eq!(resolved, target);
926    /// ```
927    #[must_use]
928    pub fn trim_to_shorten(&self, base: &UriRef) -> Option<&RelRef> {
929        let (base_abs_part, base_rel_part) = base.trim_resource().split();
930        let (self_abs_part, self_rel_part) = self.split();
931
932        if self_abs_part.is_some() {
933            if base_abs_part.is_none() || base_abs_part != self_abs_part {
934                return None;
935            }
936        }
937
938        if self_rel_part.starts_with(base_rel_part.as_str()) {
939            Some(unsafe { RelRef::from_str_unchecked(&self_rel_part[base_rel_part.len()..]) })
940        } else {
941            None
942        }
943    }
944}
945
946/// # Unsafe Methods
947///
948/// `UriRef` needs some unsafe methods in order to function properly. This section is where
949/// they are all located.
950impl UriRef {
951    /// Converts a string slice to a UriRef slice without checking
952    /// that the string contains valid URI-Reference.
953    ///
954    /// See the safe version, [`from_str`](#method.from_str), for more information.
955    ///
956    /// ## Safety
957    ///
958    /// This function is unsafe because it does not check that the string passed to
959    /// it is a valid URI-reference. If this constraint is violated, undefined behavior
960    /// results.
961    #[inline(always)]
962    pub unsafe fn from_str_unchecked(s: &str) -> &UriRef {
963        &*(s as *const str as *const UriRef)
964    }
965
966    /// Converts a string slice to a UriRef slice without checking
967    /// that the string contains valid URI-Reference; mutable version.
968    ///
969    /// See the immutable version, [`from_str_unchecked`](#method.from_str), for more information.
970    #[inline(always)]
971    pub unsafe fn from_str_unchecked_mut(s: &mut str) -> &mut UriRef {
972        &mut *(s as *mut str as *mut UriRef)
973    }
974
975    /// Returns this slice as a mutable `str` slice.
976    ///
977    /// ## Safety
978    ///
979    /// This is unsafe because it allows you to change the contents of the slice in
980    /// such a way that would make it no longer consistent with the `UriRef`'s promise
981    /// that it can only ever contain a valid URI-reference.
982    #[inline(always)]
983    pub unsafe fn as_mut_str(&mut self) -> &mut str {
984        &mut self.0
985    }
986
987    /// Same as [`query_as_rel_ref()`](#method.query_as_rel), but mutable.
988    #[doc(hidden)]
989    #[must_use = "this returns a new slice, without modifying the original"]
990    pub unsafe fn query_as_rel_ref_mut(&mut self) -> Option<&mut RelRef> {
991        let self_ptr = self.as_ptr();
992        let no_mut = self.query_as_rel_ref()?;
993        let begin = no_mut.as_ptr() as usize - self_ptr as usize;
994        let end = begin + no_mut.len();
995
996        // SAFETY: We want to convert a `&UriRef` to be a `&mut UriRef`, and we
997        //         and the behavior of using transmute to change mutability is
998        //         undefined. So we figure out the begin and end of the query and
999        //         use that range to make a new mutable slice. Queries with the
1000        //         `?` prepended are valid relative URIs, so we do an unchecked cast.
1001        //         to get those extra mechanisms.
1002
1003        Some(RelRef::from_str_unchecked_mut(
1004            &mut self.as_mut_str()[begin..end],
1005        ))
1006    }
1007}
1008
1009#[cfg(test)]
1010mod tests {
1011    use super::*;
1012
1013    #[test]
1014    fn test_from_str() {
1015        assert!(UriRef::from_str("http://example.com/").is_ok());
1016        assert!(UriRef::from_str("//example.com/").is_ok());
1017        assert!(UriRef::from_str("/a/b/c").is_ok());
1018        assert!(UriRef::from_str("a/b/c").is_ok());
1019        assert!(UriRef::from_str("?q=123").is_ok());
1020        assert!(UriRef::from_str("#frag").is_ok());
1021        assert!(UriRef::from_str("not%auri://a/b/c").is_err());
1022        assert!(UriRef::from_str("coap+sms://+1-234-567-8901/1/s/levl/v?inc").is_ok());
1023        assert!(UriRef::from_str("not a uri://a/b/c").is_err());
1024    }
1025
1026    #[test]
1027    fn path_as_rel_ref() {
1028        assert_eq!(rel_ref!("example/"), uri_ref!("example/").path_as_rel_ref());
1029        assert_eq!(
1030            rel_ref!("/blah/"),
1031            uri_ref!("http://example.com/blah/").path_as_rel_ref()
1032        );
1033        assert_eq!(
1034            rel_ref!("example.com/blah/"),
1035            uri_ref!("http:example.com/blah/?q").path_as_rel_ref()
1036        );
1037    }
1038
1039    #[test]
1040    fn has_trailing_slash() {
1041        assert_eq!(true, uri_ref!("example/").has_trailing_slash());
1042        assert_eq!(true, uri_ref!("/example/").has_trailing_slash());
1043        assert_eq!(true, uri_ref!("/example/#frag").has_trailing_slash());
1044        assert_eq!(true, uri_ref!("example/?query#frag").has_trailing_slash());
1045        assert_eq!(true, uri_ref!("coap://example//").has_trailing_slash());
1046        assert_eq!(false, uri_ref!("example").has_trailing_slash());
1047        assert_eq!(false, uri_ref!("example?/").has_trailing_slash());
1048        assert_eq!(false, uri_ref!("example#/").has_trailing_slash());
1049        assert_eq!(false, uri_ref!("example/x").has_trailing_slash());
1050        assert_eq!(false, uri_ref!("e/x/a/m/p/l/e?/#/").has_trailing_slash());
1051    }
1052
1053    #[test]
1054    fn try_trim_resource() {
1055        assert_eq!(uri_ref!("example/"), uri_ref!("example/").trim_resource());
1056        assert_eq!(uri_ref!("/example/"), uri_ref!("/example/").trim_resource());
1057        assert_eq!(
1058            uri_ref!("/example/"),
1059            uri_ref!("/example/#frag").trim_resource()
1060        );
1061        assert_eq!(
1062            uri_ref!("example/"),
1063            uri_ref!("example/?query#frag").trim_resource()
1064        );
1065        assert_eq!(
1066            uri_ref!("coap://example//"),
1067            uri_ref!("coap://example//").trim_resource()
1068        );
1069        assert_eq!(uri_ref!(""), uri_ref!("example").trim_resource());
1070        assert_eq!(uri_ref!(""), uri_ref!("example?/").trim_resource());
1071        assert_eq!(uri_ref!(""), uri_ref!("example#/").trim_resource());
1072        assert_eq!(uri_ref!("example/"), uri_ref!("example/x").trim_resource());
1073        assert_eq!(
1074            uri_ref!("e/x/a/m/p/l/"),
1075            uri_ref!("e/x/a/m/p/l/e?/#/").trim_resource()
1076        );
1077    }
1078
1079    #[test]
1080    fn trim_to_shorten() {
1081        assert_eq!(
1082            Some(rel_ref!("c")),
1083            uri_ref!("/a/b/c").trim_to_shorten(uri_ref!("/a/b/"))
1084        );
1085        assert_eq!(
1086            Some(rel_ref!("c/d/e")),
1087            uri_ref!("/a/b/c/d/e").trim_to_shorten(uri_ref!("/a/b/"))
1088        );
1089        assert_eq!(
1090            None,
1091            uri_ref!("/a/b/c/d/e").trim_to_shorten(uri_ref!("/a/c/"))
1092        );
1093        assert_eq!(
1094            Some(rel_ref!("c/d/e")),
1095            uri_ref!("/a/b/c/d/e").trim_to_shorten(uri_ref!("coap://blah/a/b/"))
1096        );
1097        assert_eq!(
1098            Some(rel_ref!("c/d/e")),
1099            uri_ref!("coap://blah/a/b/c/d/e").trim_to_shorten(uri_ref!("coap://blah/a/b/"))
1100        );
1101        assert_eq!(
1102            None,
1103            uri_ref!("coap://blah/a/b/c/d/e").trim_to_shorten(uri_ref!("/a/b/"))
1104        );
1105        assert_eq!(
1106            Some(rel_ref!("c")),
1107            uri_ref!("/a/b/c").trim_to_shorten(uri_ref!("/a/b/d"))
1108        );
1109    }
1110
1111    #[test]
1112    fn userinfo_host_port() {
1113        let uri_test_table = vec![
1114            (
1115                uri_ref!("http://example.com/a/b/c"),
1116                Some((None, "example.com", None)),
1117            ),
1118            (
1119                uri_ref!("http://example.com:1234/a/b/c"),
1120                Some((None, "example.com", Some(1234u16))),
1121            ),
1122            (
1123                uri_ref!("http://example.com:/a/b/c"),
1124                Some((None, "example.com", None)),
1125            ),
1126            (
1127                uri_ref!("http://username@example.com/a/b/c"),
1128                Some((Some("username"), "example.com", None)),
1129            ),
1130            (
1131                uri_ref!("http://username:password@example.com/a/b/c"),
1132                Some((Some("username:password"), "example.com", None)),
1133            ),
1134            (
1135                uri_ref!("http://username:password@example.com:1234/a/b/c"),
1136                Some((Some("username:password"), "example.com", Some(1234))),
1137            ),
1138            (
1139                uri_ref!("http://username:password@example.com:1234567/a/b/c"),
1140                Some((Some("username:password"), "example.com", None)),
1141            ),
1142            (uri_ref!("http://[::1]/a/b/c"), Some((None, "::1", None))),
1143            (
1144                uri_ref!("http://[::1]:1234/a/b/c"),
1145                Some((None, "::1", Some(1234))),
1146            ),
1147            (
1148                uri_ref!("http://username:password@[::1]:1234/a/b/c"),
1149                Some((Some("username:password"), "::1", Some(1234))),
1150            ),
1151        ];
1152
1153        for (a, b) in uri_test_table.iter() {
1154            assert_eq!(*b, a.raw_userinfo_host_port(), "Failed for: <{}>", a);
1155        }
1156    }
1157}