rocket_http_community/uri/
authority.rs

1use std::borrow::Cow;
2use std::fmt::{self, Display};
3
4use crate::ext::IntoOwned;
5use crate::parse::{Extent, IndexedStr};
6use crate::uri::{as_utf8_unchecked, error::Error};
7
8/// A URI with an authority only: `user:pass@host:8000`.
9///
10/// # Structure
11///
12/// The following diagram illustrates the syntactic structure of an authority
13/// URI:
14///
15/// ```text
16/// username:password@some.host:8088
17/// |---------------| |-------| |--|
18///     user info        host   port
19/// ```
20///
21/// Only the host part of the URI is required.
22///
23/// # (De)serialization
24///
25/// `Authority` is both `Serialize` and `Deserialize`:
26///
27/// ```rust
28/// # #[cfg(feature = "serde")] mod serde_impl {
29/// # use serde as serde;
30/// use serde::{Serialize, Deserialize};
31/// use rocket::http::uri::Authority;
32///
33/// #[derive(Deserialize, Serialize)]
34/// # #[serde(crate = "serde")]
35/// struct UriOwned {
36///     uri: Authority<'static>,
37/// }
38///
39/// #[derive(Deserialize, Serialize)]
40/// # #[serde(crate = "serde")]
41/// struct UriBorrowed<'a> {
42///     uri: Authority<'a>,
43/// }
44/// # }
45/// ```
46#[derive(Debug, Clone)]
47pub struct Authority<'a> {
48    pub(crate) source: Option<Cow<'a, str>>,
49    pub(crate) user_info: Option<IndexedStr<'a>>,
50    host: IndexedStr<'a>,
51    port: Option<u16>,
52}
53
54impl<'a> Authority<'a> {
55    // SAFETY: `source` must be valid UTF-8.
56    // CORRECTNESS: `host` must be non-empty.
57    pub(crate) unsafe fn raw(
58        source: Cow<'a, [u8]>,
59        user_info: Option<Extent<&'a [u8]>>,
60        host: Extent<&'a [u8]>,
61        port: Option<u16>,
62    ) -> Authority<'a> {
63        Authority {
64            source: Some(as_utf8_unchecked(source)),
65            user_info: user_info.map(IndexedStr::from),
66            host: IndexedStr::from(host),
67            port,
68        }
69    }
70
71    /// PRIVATE. Used by core.
72    #[doc(hidden)]
73    pub fn new(
74        user_info: impl Into<Option<&'a str>>,
75        host: &'a str,
76        port: impl Into<Option<u16>>,
77    ) -> Self {
78        Authority::const_new(user_info.into(), host, port.into())
79    }
80
81    /// PRIVATE. Used by codegen.
82    #[doc(hidden)]
83    pub const fn const_new(user_info: Option<&'a str>, host: &'a str, port: Option<u16>) -> Self {
84        Authority {
85            source: None,
86            user_info: match user_info {
87                Some(info) => Some(IndexedStr::Concrete(Cow::Borrowed(info))),
88                None => None,
89            },
90            host: IndexedStr::Concrete(Cow::Borrowed(host)),
91            port,
92        }
93    }
94
95    /// Parses the string `string` into an `Authority`. Parsing will never
96    /// allocate. Returns an `Error` if `string` is not a valid authority URI.
97    ///
98    /// # Example
99    ///
100    /// ```rust
101    /// # #[macro_use] extern crate rocket;
102    /// use rocket::http::uri::Authority;
103    ///
104    /// // Parse a valid authority URI.
105    /// let uri = Authority::parse("user:pass@host").expect("valid URI");
106    /// assert_eq!(uri.user_info(), Some("user:pass"));
107    /// assert_eq!(uri.host(), "host");
108    /// assert_eq!(uri.port(), None);
109    ///
110    /// // Invalid authority URIs fail to parse.
111    /// Authority::parse("https://rocket.rs").expect_err("invalid authority");
112    ///
113    /// // Prefer to use `uri!()` when the input is statically known:
114    /// let uri = uri!("user:pass@host");
115    /// assert_eq!(uri.user_info(), Some("user:pass"));
116    /// assert_eq!(uri.host(), "host");
117    /// assert_eq!(uri.port(), None);
118    /// ```
119    pub fn parse(string: &'a str) -> Result<Authority<'a>, Error<'a>> {
120        crate::parse::uri::authority_from_str(string)
121    }
122
123    /// Parses the string `string` into an `Authority`. Parsing never allocates
124    /// on success. May allocate on error.
125    ///
126    /// This method should be used instead of [`Authority::parse()`] when
127    /// the source URI is already a `String`. Returns an `Error` if `string` is
128    /// not a valid authority URI.
129    ///
130    /// # Example
131    ///
132    /// ```rust
133    /// # extern crate rocket;
134    /// use rocket::http::uri::Authority;
135    ///
136    /// let source = format!("rocket.rs:8000");
137    /// let uri = Authority::parse_owned(source).expect("valid URI");
138    /// assert!(uri.user_info().is_none());
139    /// assert_eq!(uri.host(), "rocket.rs");
140    /// assert_eq!(uri.port(), Some(8000));
141    /// ```
142    pub fn parse_owned(string: String) -> Result<Authority<'static>, Error<'static>> {
143        let authority = Authority::parse(&string).map_err(|e| e.into_owned())?;
144        debug_assert!(authority.source.is_some(), "Authority parsed w/o source");
145
146        let authority = Authority {
147            host: authority.host.into_owned(),
148            user_info: authority.user_info.into_owned(),
149            port: authority.port,
150            source: Some(Cow::Owned(string)),
151        };
152
153        Ok(authority)
154    }
155
156    /// Returns the user info part of the authority URI, if there is one.
157    ///
158    /// # Example
159    /// ```rust
160    /// # #[macro_use] extern crate rocket;
161    /// let uri = uri!("username:password@host");
162    /// assert_eq!(uri.user_info(), Some("username:password"));
163    /// ```
164    pub fn user_info(&self) -> Option<&str> {
165        self.user_info
166            .as_ref()
167            .map(|u| u.from_cow_source(&self.source))
168    }
169
170    /// Returns the host part of the authority URI.
171    ///
172    /// # Example
173    ///
174    /// ```rust
175    /// # #[macro_use] extern crate rocket;
176    /// let uri = uri!("domain.com:123");
177    /// assert_eq!(uri.host(), "domain.com");
178    ///
179    /// let uri = uri!("username:password@host:123");
180    /// assert_eq!(uri.host(), "host");
181    ///
182    /// let uri = uri!("username:password@[1::2]:123");
183    /// assert_eq!(uri.host(), "[1::2]");
184    /// ```
185    #[inline(always)]
186    pub fn host(&self) -> &str {
187        self.host.from_cow_source(&self.source)
188    }
189
190    /// Returns the `port` part of the authority URI, if there is one.
191    ///
192    /// # Example
193    ///
194    /// ```rust
195    /// # #[macro_use] extern crate rocket;
196    /// // With a port.
197    /// let uri = uri!("username:password@host:123");
198    /// assert_eq!(uri.port(), Some(123));
199    ///
200    /// let uri = uri!("domain.com:8181");
201    /// assert_eq!(uri.port(), Some(8181));
202    ///
203    /// // Without a port.
204    /// let uri = uri!("username:password@host");
205    /// assert_eq!(uri.port(), None);
206    /// ```
207    #[inline(always)]
208    pub fn port(&self) -> Option<u16> {
209        self.port
210    }
211
212    /// Set the `port` of the authority URI.
213    ///
214    /// # Example
215    ///
216    /// ```rust
217    /// # #[macro_use] extern crate rocket;
218    /// let mut uri = uri!("username:password@host:123");
219    /// assert_eq!(uri.port(), Some(123));
220    ///
221    /// uri.set_port(1024);
222    /// assert_eq!(uri.port(), Some(1024));
223    /// assert_eq!(uri, "username:password@host:1024");
224    ///
225    /// uri.set_port(None);
226    /// assert_eq!(uri.port(), None);
227    /// assert_eq!(uri, "username:password@host");
228    /// ```
229    #[inline(always)]
230    pub fn set_port<T: Into<Option<u16>>>(&mut self, port: T) {
231        self.port = port.into();
232    }
233}
234
235impl_serde!(Authority<'a>, "an authority-form URI");
236
237impl_traits!(Authority, user_info, host, port);
238
239impl Display for Authority<'_> {
240    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
241        if let Some(user_info) = self.user_info() {
242            write!(f, "{}@", user_info)?;
243        }
244
245        self.host().fmt(f)?;
246        if let Some(port) = self.port {
247            write!(f, ":{}", port)?;
248        }
249
250        Ok(())
251    }
252}