iri_string/components/
authority.rs

1//! Subcomponents of authority.
2
3use crate::parser::trusted as trusted_parser;
4use crate::spec::Spec;
5use crate::types::RiReferenceStr;
6
7/// Subcomponents of authority.
8///
9/// This is a return type of the `authority_components` method of the string
10/// types (for example [`RiStr::authority_components`].
11///
12/// [`RiStr::authority_components`]: `crate::types::RiStr::authority_components`
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
14pub struct AuthorityComponents<'a> {
15    /// Authority string, excluding the leading `//`.
16    pub(crate) authority: &'a str,
17    /// Start position of the `host`.
18    pub(crate) host_start: usize,
19    /// End position of the `host`.
20    pub(crate) host_end: usize,
21}
22
23impl<'a> AuthorityComponents<'a> {
24    /// Creates a new `AuthorityComponents` from the IRI.
25    pub fn from_iri<S: Spec>(iri: &'a RiReferenceStr<S>) -> Option<Self> {
26        iri.authority_str()
27            .map(trusted_parser::authority::decompose_authority)
28    }
29
30    /// Returns the `userinfo` part, excluding the following `@`.
31    #[must_use]
32    pub fn userinfo(&self) -> Option<&'a str> {
33        let userinfo_at = self.host_start.checked_sub(1)?;
34        debug_assert_eq!(self.authority.as_bytes()[userinfo_at], b'@');
35        Some(&self.authority[..userinfo_at])
36    }
37
38    /// Returns the `host` part.
39    #[inline]
40    #[must_use]
41    pub fn host(&self) -> &'a str {
42        // NOTE: RFC 6874 support may need the internal logic to change.
43        &self.authority[self.host_start..self.host_end]
44    }
45
46    /// Returns the `port` part, excluding the following `:`.
47    #[must_use]
48    pub fn port(&self) -> Option<&'a str> {
49        if self.host_end == self.authority.len() {
50            return None;
51        }
52        let port_colon = self.host_end;
53        debug_assert_eq!(self.authority.as_bytes()[port_colon], b':');
54        Some(&self.authority[(port_colon + 1)..])
55    }
56}
57
58#[cfg(test)]
59#[cfg(feature = "alloc")]
60mod tests {
61    use super::*;
62
63    #[cfg(all(feature = "alloc", not(feature = "std")))]
64    use alloc::string::String;
65
66    use crate::types::IriReferenceStr;
67
68    const USERINFO: &[&str] = &["", "user:password", "user"];
69
70    const PORT: &[&str] = &[
71        "",
72        "0",
73        "0000",
74        "80",
75        "1234567890123456789012345678901234567890",
76    ];
77
78    const HOST: &[&str] = &[
79        "",
80        "localhost",
81        "example.com",
82        "192.0.2.0",
83        "[2001:db8::1]",
84        "[2001:0db8:0:0:0:0:0:1]",
85        "[2001:0db8::192.0.2.255]",
86        "[v9999.this-is-futuristic-ip-address]",
87    ];
88
89    fn compose_to_relative_iri(userinfo: Option<&str>, host: &str, port: Option<&str>) -> String {
90        let mut buf = String::from("//");
91        if let Some(userinfo) = userinfo {
92            buf.push_str(userinfo);
93            buf.push('@');
94        }
95        buf.push_str(host);
96        if let Some(port) = port {
97            buf.push(':');
98            buf.push_str(port);
99        }
100        buf
101    }
102
103    #[test]
104    fn test_decompose_authority() {
105        for host in HOST.iter().copied() {
106            for userinfo in USERINFO.iter().map(|s| Some(*s)).chain(None) {
107                for port in PORT.iter().map(|s| Some(*s)).chain(None) {
108                    let authority = compose_to_relative_iri(userinfo, host, port);
109                    let authority =
110                        IriReferenceStr::new(&authority).expect("test case should be valid");
111                    let components = AuthorityComponents::from_iri(authority)
112                        .expect("relative path composed for this test should contain authority");
113
114                    assert_eq!(components.host(), host);
115                    assert_eq!(components.userinfo(), userinfo);
116                    assert_eq!(components.port(), port);
117                }
118            }
119        }
120    }
121}