iref_core/iri/
authority.rs

1use 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			// eprintln!("{input} => {expected:?}");
134			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}