async_coap_uri/
rel_ref_buf.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 std::ops::Deref;
18
19/// Sized, heap-allocated string type guaranteed to contain a well-formed [IETF-RFC3986]
20/// [relative-reference].
21///
22/// The unsized counterpart is [`RelRef`](crate::RelRef).
23///
24/// *This type cannot hold a network path*. If this type contains a path that looks like a network
25/// path, it will be considered [degenerate](crate::RelRef::is_degenerate) and you will not be able
26/// to losslessly convert it to a [`UriRef`](crate::UriRef) or [`UriRefBuf`](crate::UriRefBuf).
27/// See ["Network Path Support"](index.html#network-path-support) for more details.
28///
29/// This type implements [`std::ops::Deref<RelRef>`], so you can also use all of the
30/// methods from [`RelRef`] on this type.
31///
32/// [IETF-RFC3986]: https://tools.ietf.org/html/rfc3986
33/// [relative-reference]: https://tools.ietf.org/html/rfc3986#section-4.2
34#[derive(Clone, Eq, Hash)]
35pub struct RelRefBuf(pub(super) UriRefBuf);
36
37impl_uri_buf_traits!(RelRefBuf, RelRef);
38
39impl Default for RelRefBuf {
40    fn default() -> Self {
41        Self::new()
42    }
43}
44
45impl std::fmt::Display for RelRefBuf {
46    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
47        self.write_to(f)
48    }
49}
50
51impl Deref for RelRefBuf {
52    type Target = RelRef;
53
54    fn deref(&self) -> &Self::Target {
55        self.as_rel_ref()
56    }
57}
58
59impl AsRef<RelRef> for RelRefBuf {
60    fn as_ref(&self) -> &RelRef {
61        self.as_rel_ref()
62    }
63}
64
65impl From<&RelRef> for RelRefBuf {
66    fn from(x: &RelRef) -> Self {
67        x.to_rel_ref_buf()
68    }
69}
70
71/// # Constructors
72impl RelRefBuf {
73    /// Constructs a new, empty relative reference buffer.
74    pub fn new() -> RelRefBuf {
75        RelRefBuf(UriRefBuf::new())
76    }
77
78    /// Creates a new, empty [`RelRefBuf`] with a capacity of `capacity`.
79    pub fn with_capacity(capacity: usize) -> RelRefBuf {
80        RelRefBuf(UriRefBuf::with_capacity(capacity))
81    }
82
83    /// Attempts to create a new [`RelRefBuf`] from a string reference.
84    pub fn from_str<S: AsRef<str>>(s: S) -> Result<RelRefBuf, ParseError> {
85        RelRef::from_str(s.as_ref()).map(Self::from_rel_ref)
86    }
87
88    /// Attempts to create a new [`RelRefBuf`] from a [`String`].
89    pub fn from_string(s: String) -> Result<RelRefBuf, ParseError> {
90        if let Some(first_error) = s.as_str().unescape_uri().first_error() {
91            return Err(ParseError::new(
92                "Bad percent encoding or illegal characters",
93                Some(first_error..s.len()),
94            ));
95        } else {
96            let mut ret = unsafe { Self::from_string_unchecked(s) };
97
98            ret.disambiguate();
99
100            Ok(ret)
101        }
102    }
103
104    /// Attempts to create a new [`RelRefBuf`] from a [`UriRef`] reference.
105    pub fn from_uri_ref<S: AsRef<UriRef>>(s: S) -> Option<RelRefBuf> {
106        s.as_ref()
107            .as_rel_ref()
108            .map(ToString::to_string)
109            .map(|s| unsafe { Self::from_string_unchecked(s) })
110    }
111
112    /// Attempts to create a new [`RelRefBuf`] from a [`RelRef`] reference.
113    pub fn from_rel_ref<S: AsRef<RelRef>>(s: S) -> RelRefBuf {
114        unsafe { Self::from_string_unchecked(s.as_ref().to_string()) }
115    }
116}
117
118/// # Conversions
119impl RelRefBuf {
120    /// Borrows a [`RelRef`] slice containing this relative reference.
121    #[inline(always)]
122    pub fn as_rel_ref(&self) -> &RelRef {
123        unsafe { RelRef::from_str_unchecked(self.as_str()) }
124    }
125
126    /// Borrows a mutable [`RelRef`] slice containing this relative reference.
127    #[inline(always)]
128    pub fn as_mut_rel_ref(&mut self) -> &mut RelRef {
129        unsafe { RelRef::from_str_unchecked_mut(self.as_mut_str()) }
130    }
131}
132
133/// # Manipulations
134impl RelRefBuf {
135    /// Modifies this relative reference to be unambiguous.
136    ///
137    /// Specifically:
138    ///
139    /// * `this:that` is converted to `this%3Athat`, to avoid confusion with a URI.
140    /// * `//this/that` is converted to `/.//this/that`, to avoid confusion with a network path.
141    ///
142    /// Note that ambiguous `RelRefBuf` instances are always displayed/formatted unambiguously,
143    /// so there should be little need to ever call this method.
144    pub fn disambiguate(&mut self) -> bool {
145        if let Some(i) = self.colon_in_first_path_segment() {
146            (self.0).0.replace_range(i..i + 1, "%3A");
147            true
148        } else if self.starts_with("//") {
149            (self.0).0.insert_str(0, "/.");
150            true
151        } else {
152            false
153        }
154    }
155
156    /// **Experimental**: Takes ownership of the [`RelRefBuf`] and returns a `UriUnescapeBuf` that allows for
157    /// in-line unescaping during iteration of path segments and query items without additional
158    /// memory allocations.
159    ///
160    /// Example:
161    ///
162    /// ```
163    /// use async_coap_uri::prelude::*;
164    /// let rel_ref_buf = rel_ref!(unsafe "g:a/b/bl%c3%a5b%c3%a6r?q=g:a&q=foo&q=syltet%c3%b8y").to_rel_ref_buf();
165    /// let mut unescape_buf = rel_ref_buf.into_unescape_buf();
166    ///
167    /// let mut query_item_iter = unescape_buf.query_items();
168    ///
169    /// assert_eq!(query_item_iter.next(), Some("q=g:a"));
170    /// assert_eq!(query_item_iter.next(), Some("q=foo"));
171    /// assert_eq!(query_item_iter.next(), Some("q=syltetøy"));
172    /// assert_eq!(query_item_iter.next(), None);
173    ///
174    /// core::mem::drop(query_item_iter);
175    ///
176    /// let mut path_seg_iter = unescape_buf.path_segments();
177    ///
178    /// assert_eq!(path_seg_iter.next(), Some("g:a"));
179    /// assert_eq!(path_seg_iter.next(), Some("b"));
180    /// assert_eq!(path_seg_iter.next(), Some("blåbær"));
181    /// assert_eq!(path_seg_iter.next(), None);
182    /// ```
183    ///
184    /// Lifetimes are enforced on the returned path items, meaning the following code
185    /// does not compile:
186    ///
187    /// ```compile_fail
188    /// # use async_coap_uri::*;
189    /// # fn main() {
190    ///     let mut uri = rel_ref!("this:is/fun/bl%c3%a5b%c3%a6rsyltet%c3%b8y").to_owned();
191    ///     let first_part;
192    ///     let second_part;
193    ///     {
194    ///         // This line takes ownership of `uri`...
195    ///         let mut path_set = uri.into_unescape_buf();
196    ///         let mut iter = path_set.path_segments();
197    ///
198    ///         // ...so these string slices will be valid
199    ///         // for the lifetime of `iter`.
200    ///         first_part = iter.next().unwrap();
201    ///         second_part = iter.next().unwrap();
202    ///     }
203    ///     // Iter has been dropped at this point, but the next
204    ///     // line uses both `first_part` and `second_part`---so
205    ///     // the compiler will flag an error. If it does compile,
206    ///     // it represents a use-after-free error.
207    ///     panic!("This should not have compiled! {} {}", first_part, second_part);
208    /// # }
209    /// ```
210    #[must_use]
211    pub fn into_unescape_buf(self) -> UriUnescapeBuf {
212        UriUnescapeBuf::new(self)
213    }
214
215    /// Using this relative-reference as the base, performs "URI-reference resolution" on another
216    /// relative reference, updating the content of this `RelRefBuf` with the result.
217    pub fn resolve<T: AsRef<RelRef>>(&mut self, dest: T) {
218        if !dest.as_ref().is_empty() {
219            *self = self.resolved_rel_ref(dest);
220        }
221    }
222
223    /// Completely clears this `RelRefBuf`, leaving it empty.
224    #[inline(always)]
225    pub fn clear(&mut self) {
226        self.0.clear()
227    }
228}
229
230inherits_uri_ref_buf!(RelRefBuf);
231
232/// # Unsafe Methods
233///
234/// `RelRefBuf` needs some unsafe methods in order to function properly. This section is where
235/// they are all located.
236impl RelRefBuf {
237    /// Unchecked version of [`RelRefBuf::from_string`].
238    ///
239    /// # Safety
240    ///
241    /// This method is marked as unsafe because it allows you to construct a `RelRefBuf` with
242    /// a value that is not a well-formed relative-reference.
243    #[inline(always)]
244    pub unsafe fn from_string_unchecked(s: String) -> RelRefBuf {
245        RelRefBuf(UriRefBuf::from_string_unchecked(s))
246    }
247}