iref_core/iri/
authority.rs1use std::{
2 cmp,
3 hash::{self, Hash},
4};
5
6use static_regular_grammar::RegularGrammar;
7
8mod host;
9mod userinfo;
10
11use crate::common::authority::AuthorityImpl;
12pub use crate::uri::{InvalidPort, Port, PortBuf};
13pub use crate::uri::{InvalidScheme, Scheme, SchemeBuf};
14pub use host::*;
15pub use userinfo::*;
16
17#[derive(RegularGrammar)]
18#[grammar(
19 file = "src/iri/grammar.abnf",
20 entry_point = "iauthority",
21 name = "IRI authority",
22 cache = "automata/iri/authority.aut.cbor"
23)]
24#[grammar(sized(
25 AuthorityBuf,
26 derive(Debug, Display, PartialEq, Eq, PartialOrd, Ord, Hash)
27))]
28#[cfg_attr(feature = "serde", grammar(serde))]
29#[cfg_attr(feature = "ignore-grammars", grammar(disable))]
30pub struct Authority(str);
31
32impl AuthorityImpl for Authority {
33 type UserInfo = UserInfo;
34 type Host = Host;
35
36 unsafe fn new_unchecked(bytes: &[u8]) -> &Self {
37 Self::new_unchecked(std::str::from_utf8_unchecked(bytes))
38 }
39
40 fn as_bytes(&self) -> &[u8] {
41 self.0.as_bytes()
42 }
43}
44
45#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
46pub struct AuthorityParts<'a> {
47 pub user_info: Option<&'a UserInfo>,
48 pub host: &'a Host,
49 pub port: Option<&'a Port>,
50}
51
52impl Authority {
53 pub fn user_info(&self) -> Option<&UserInfo> {
54 AuthorityImpl::user_info(self)
55 }
56
57 pub fn host(&self) -> &Host {
58 AuthorityImpl::host(self)
59 }
60
61 pub fn port(&self) -> Option<&Port> {
62 AuthorityImpl::port(self)
63 }
64
65 pub fn parts(&self) -> AuthorityParts {
66 let ranges = AuthorityImpl::parts(self);
67
68 AuthorityParts {
69 user_info: ranges
70 .user_info
71 .map(|r| unsafe { UserInfo::new_unchecked(&self.0[r]) }),
72 host: unsafe { Host::new_unchecked(&self.0[ranges.host]) },
73 port: ranges
74 .port
75 .map(|r| unsafe { Port::new_unchecked(&self.0.as_bytes()[r]) }),
76 }
77 }
78}
79
80impl cmp::PartialEq for Authority {
81 #[inline]
82 fn eq(&self, other: &Authority) -> bool {
83 self.parts() == other.parts()
84 }
85}
86
87impl Eq for Authority {}
88
89impl<'a> PartialEq<&'a str> for Authority {
90 #[inline]
91 fn eq(&self, other: &&'a str) -> bool {
92 self.as_str() == *other
93 }
94}
95
96impl PartialOrd for Authority {
97 #[inline]
98 fn partial_cmp(&self, other: &Authority) -> Option<cmp::Ordering> {
99 Some(self.cmp(other))
100 }
101}
102
103impl Ord for Authority {
104 #[inline]
105 fn cmp(&self, other: &Authority) -> cmp::Ordering {
106 self.parts().cmp(&other.parts())
107 }
108}
109
110impl Hash for Authority {
111 #[inline]
112 fn hash<H: hash::Hasher>(&self, hasher: &mut H) {
113 self.parts().hash(hasher)
114 }
115}
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120
121 #[test]
122 fn parts() {
123 let vectors = [
124 ("host", (None, "host", None)),
125 ("user@host", (Some("user"), "host", None)),
126 ("host:123", (None, "host", Some("123"))),
127 ("user@host:123", (Some("user"), "host", Some("123"))),
128 ("a:b@host", (Some("a:b"), "host", None)),
129 ("a:b@host:123", (Some("a:b"), "host", Some("123"))),
130 ];
131
132 for (input, expected) in vectors {
133 let input = Authority::new(input).unwrap();
135 let parts = input.parts();
136
137 assert_eq!(parts.user_info.map(UserInfo::as_str), expected.0);
138 assert_eq!(parts.host.as_str(), expected.1);
139 assert_eq!(parts.port.map(Port::as_str), expected.2)
140 }
141 }
142}