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}