async_coap_uri/uri.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/// Unsized string-slice type guaranteed to contain a well-formed [IETF-RFC3986] URI
20/// *or* [network path](index.html#network-path-support).
21///
22/// The sized counterpart is [`crate::UriBuf`].
23///
24/// You can create static constants with this class by using the [`uri!`] macro:
25///
26/// ```
27/// # use async_coap_uri::*;
28/// # fn main() {
29/// let uri = uri!("http://example.com/test");
30/// let components = uri.components();
31/// assert_eq!(Some("http"), components.scheme());
32/// assert_eq!(Some("example.com"), components.raw_host());
33/// assert_eq!(None, components.port());
34/// assert_eq!("/test", components.raw_path());
35/// # }
36/// ```
37///
38/// [`uri!`]: macro.uri.html
39/// [IETF-RFC3986]: https://tools.ietf.org/html/rfc3986
40#[derive(Eq, Hash)]
41pub struct Uri(UriRef);
42
43_impl_uri_traits_base!(Uri);
44
45impl Deref for Uri {
46 type Target = UriRef;
47
48 fn deref(&self) -> &Self::Target {
49 self.as_uri_ref()
50 }
51}
52
53impl AsRef<UriRef> for Uri {
54 fn as_ref(&self) -> &UriRef {
55 &self.0
56 }
57}
58
59impl AnyUriRef for Uri {
60 fn write_to<T: core::fmt::Write + ?Sized>(
61 &self,
62 write: &mut T,
63 ) -> Result<(), core::fmt::Error> {
64 write.write_str(self.as_str())
65 }
66
67 fn is_empty(&self) -> bool {
68 self.0.is_empty()
69 }
70
71 /// Determines what kind of URI this is.
72 ///
73 /// This function may return any one of the following values:
74 ///
75 /// * [`UriType::Uri`](enum.UriType.html#variant.Uri)
76 /// * [`UriType::UriNoAuthority`](enum.UriType.html#variant.UriNoAuthority)
77 /// * [`UriType::UriCannotBeABase`](enum.UriType.html#variant.UriCannotBeABase)
78 /// * [`UriType::NetworkPath`](enum.UriType.html#variant.NetworkPath)
79 fn uri_type(&self) -> UriType {
80 if self.0.starts_with("//") {
81 UriType::NetworkPath
82 } else {
83 let i = self.find(':').expect("Uri contract broken");
84 if self[i..].starts_with("://") {
85 UriType::Uri
86 } else if self[i..].starts_with(":/") {
87 UriType::UriNoAuthority
88 } else {
89 UriType::UriCannotBeABase
90 }
91 }
92 }
93
94 fn components(&self) -> UriRawComponents<'_> {
95 self.0.components()
96 }
97}
98
99impl std::fmt::Display for Uri {
100 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
101 self.write_to(f)
102 }
103}
104
105impl Uri {
106 /// Attempts to convert a string slice into a [`&Uri`](Uri), returning `Err(ParseError)`
107 /// if the string slice contains data that is not a valid URI.
108 ///
109 /// Example:
110 ///
111 /// ```
112 /// use async_coap_uri::prelude::*;
113 /// assert_eq!(Uri::from_str("http://example.com"), Ok(uri!("http://example.com")));
114 /// assert!(Uri::from_str("/a/b/c").is_err());
115 /// ```
116 pub fn from_str(s: &str) -> Result<&Uri, ParseError> {
117 let str_ref = s.as_ref();
118 // TODO: Replace this with an optimized validity check.
119 // We are currently using `UriRawComponents::from_str()` as a crutch here;
120 // it includes extraneous operations that are not related to verifying if a
121 // URI is well-formed.
122 if UriRawComponents::from_str(str_ref)?
123 .uri_type()
124 .can_borrow_as_uri()
125 {
126 Ok(unsafe { Self::from_str_unchecked(s.as_ref()) })
127 } else {
128 Err(ParseError::new("Not a URI", None))
129 }
130 }
131
132 /// Determines if the given string can be considered a valid URI.
133 ///
134 /// The key difference between this and [`crate::UriRef::is_str_valid`] is that this
135 /// function will return false for [relative-references] like `/a/b/c`, whereas
136 /// `UriRef::is_str_valid` would return true.
137 ///
138 /// Example:
139 ///
140 /// ```
141 /// use async_coap_uri::Uri;
142 /// assert!(Uri::is_str_valid("http://example.com"));
143 /// assert!(!Uri::is_str_valid("/a/b/c"));
144 /// assert!(!Uri::is_str_valid("Not a URI"));
145 /// ```
146 /// [relative-reference]: https://tools.ietf.org/html/rfc3986#section-4.2
147 pub fn is_str_valid<S: AsRef<str>>(s: S) -> bool {
148 let str_ref = s.as_ref();
149 // TODO: Replace this with an optimized validity check.
150 // We are currently using `UriRawComponents::from_str()` as a crutch here;
151 // it includes extraneous operations that are not related to verifying if a
152 // URI is well-formed.
153 if let Ok(components) = UriRawComponents::from_str(str_ref) {
154 if components.uri_type().can_borrow_as_uri() {
155 return true;
156 }
157 }
158 return false;
159 }
160
161 /// Reinterpret this [`&Uri`][Uri] as a [`&UriRef`][UriRef].
162 #[inline(always)]
163 pub const fn as_uri_ref(&self) -> &UriRef {
164 &self.0
165 }
166
167 /// Copy the content of this [`&Uri`][Uri] into a new [`UriBuf`] and return it.
168 pub fn to_uri_buf(&self) -> UriBuf {
169 unsafe { UriBuf::from_string_unchecked(self.to_string()) }
170 }
171}
172
173/// ## Splitting
174impl Uri {
175 /// Splits this URI into the base and relative portions.
176 pub fn split(&self) -> (&Uri, &RelRef) {
177 let (uri_base, uri_rel) = self.0.split();
178 (uri_base.unwrap(), uri_rel)
179 }
180}
181
182/// ## Trimming
183impl Uri {
184 /// Returns this URI without a fragment.
185 ///
186 /// ## Examples
187 ///
188 /// ```
189 /// use async_coap_uri::prelude::*;
190 /// assert_eq!(uri!("http://a/#frag").trim_fragment(), uri!("http://a/"));
191 /// assert_eq!(uri!("//a/b/c?blah#frag").trim_fragment(), uri!("//a/b/c?blah"));
192 /// ```
193 pub fn trim_fragment(&self) -> &Uri {
194 unsafe { Uri::from_str_unchecked(self.0.trim_fragment().as_str()) }
195 }
196
197 /// Returns this URI without a query or fragment.
198 ///
199 /// ## Examples
200 ///
201 /// ```
202 /// use async_coap_uri::prelude::*;
203 /// assert_eq!(uri!("//foo/?bar").trim_query(), uri!("//foo/"));
204 /// assert_eq!(uri!("http://a/#frag").trim_query(), uri!("http://a/"));
205 /// ```
206 pub fn trim_query(&self) -> &Uri {
207 unsafe { Uri::from_str_unchecked(self.0.trim_query().as_str()) }
208 }
209
210 /// Returns this URI without a path, query, or fragment.
211 ///
212 /// ## Examples
213 ///
214 /// ```
215 /// use async_coap_uri::prelude::*;
216 /// assert_eq!(uri!("//foo/?bar").trim_path(), uri!("//foo"));
217 /// assert_eq!(uri!("http://a/#frag").trim_path(), uri!("http://a"));
218 /// ```
219 pub fn trim_path(&self) -> &Uri {
220 unsafe { Uri::from_str_unchecked(self.0.trim_path().as_str()) }
221 }
222
223 /// Returns this URI without the trailing part of the path that would be
224 /// removed during relative-reference resolution.
225 ///
226 /// ## Examples
227 ///
228 /// ```
229 /// use async_coap_uri::prelude::*;
230 /// assert_eq!(uri!("//foo/?bar").trim_resource(), uri!("//foo/"));
231 /// assert_eq!(uri!("http://a/#frag").trim_resource(), uri!("http://a/"));
232 /// ```
233 pub fn trim_resource(&self) -> &Uri {
234 unsafe { Uri::from_str_unchecked(self.0.trim_resource().as_str()) }
235 }
236}
237
238/// # Unsafe Methods
239///
240/// `Uri` needs some unsafe methods in order to function properly. This section is where
241/// they are all located.
242impl Uri {
243 /// Creates a `Uri` slice from a string slice without checking that the content
244 /// of the string slice is a valid URI.
245 ///
246 /// Since containing a valid URI is a fundamental guarantee of the `Uri` type, this method is
247 /// `unsafe`.
248 #[inline(always)]
249 pub unsafe fn from_str_unchecked(s: &str) -> &Uri {
250 &*(s as *const str as *const Uri)
251 }
252
253 /// Creates a mutable `Uri` slice from a mutable string slice without checking that the content
254 /// of the mutable string slice is a valid URI.
255 ///
256 /// Since containing a valid URI is a fundamental guarantee of the `Uri` type, this method is
257 /// `unsafe`.
258 #[inline(always)]
259 pub unsafe fn from_str_unchecked_mut(s: &mut str) -> &mut Uri {
260 &mut *(s as *mut str as *mut Uri)
261 }
262
263 /// Returns this slice as a mutable `str` slice.
264 ///
265 /// ## Safety
266 ///
267 /// This is unsafe because it allows you to change the contents of the slice in
268 /// such a way that would make it no longer consistent with the `Uri`'s promise
269 /// that it can only ever contain a valid URI.
270 #[inline(always)]
271 pub unsafe fn as_mut_str(&mut self) -> &mut str {
272 self.0.as_mut_str()
273 }
274}
275
276#[cfg(test)]
277mod tests {
278 use super::*;
279
280 #[test]
281 fn test_uri_type() {
282 assert_eq!(uri!("scheme://example").uri_type(), UriType::Uri);
283 assert_eq!(uri!("scheme:/example").uri_type(), UriType::UriNoAuthority);
284 assert_eq!(uri!("scheme:example").uri_type(), UriType::UriCannotBeABase);
285 assert_eq!(
286 uri!("scheme:example/://not_a_url").uri_type(),
287 UriType::UriCannotBeABase
288 );
289 }
290}