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}