iref_core/uri/
reference.rs

1use std::hash::{self, Hash};
2
3use static_regular_grammar::RegularGrammar;
4
5use crate::{
6	common::{parse, RiRefBufImpl, RiRefImpl},
7	InvalidIri, InvalidUri, Iri, IriBuf, IriRef, IriRefBuf, Uri, UriBuf,
8};
9
10use super::{bytestr_eq, Authority, AuthorityMut, Fragment, Path, PathBuf, PathMut, Query, Scheme};
11
12/// URI reference.
13#[derive(RegularGrammar)]
14#[grammar(
15	file = "src/uri/grammar.abnf",
16	entry_point = "URI-reference",
17	name = "URI reference",
18	cache = "automata/uri/reference.aut.cbor",
19	ascii
20)]
21#[grammar(sized(
22	UriRefBuf,
23	derive(Debug, Display, PartialEq, Eq, PartialOrd, Ord, Hash)
24))]
25#[cfg_attr(feature = "serde", grammar(serde))]
26#[cfg_attr(feature = "ignore-grammars", grammar(disable))]
27pub struct UriRef([u8]);
28
29impl RiRefImpl for UriRef {
30	type Authority = Authority;
31	type Path = Path;
32	type Query = Query;
33	type Fragment = Fragment;
34
35	type RiRefBuf = UriRefBuf;
36
37	fn as_bytes(&self) -> &[u8] {
38		&self.0
39	}
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
43pub struct UriRefParts<'a> {
44	pub scheme: Option<&'a Scheme>,
45	pub authority: Option<&'a Authority>,
46	pub path: &'a Path,
47	pub query: Option<&'a Query>,
48	pub fragment: Option<&'a Fragment>,
49}
50
51impl UriRef {
52	pub fn parts(&self) -> UriRefParts {
53		let bytes = self.as_bytes();
54		let ranges = parse::reference_parts(bytes, 0);
55
56		UriRefParts {
57			scheme: ranges
58				.scheme
59				.map(|r| unsafe { Scheme::new_unchecked(&bytes[r]) }),
60			authority: ranges
61				.authority
62				.map(|r| unsafe { Authority::new_unchecked(&self.0[r]) }),
63			path: unsafe { Path::new_unchecked(&self.0[ranges.path]) },
64			query: ranges
65				.query
66				.map(|r| unsafe { Query::new_unchecked(&self.0[r]) }),
67			fragment: ranges
68				.fragment
69				.map(|r| unsafe { Fragment::new_unchecked(&self.0[r]) }),
70		}
71	}
72
73	#[inline]
74	pub fn as_uri(&self) -> Option<&Uri> {
75		if self.scheme().is_some() {
76			Some(unsafe { Uri::new_unchecked(&self.0) })
77		} else {
78			None
79		}
80	}
81
82	#[inline]
83	pub fn as_iri(&self) -> Option<&Iri> {
84		if self.scheme().is_some() {
85			Some(unsafe { Iri::new_unchecked(std::str::from_utf8_unchecked(&self.0)) })
86		} else {
87			None
88		}
89	}
90
91	#[inline]
92	pub fn as_iri_ref(&self) -> &IriRef {
93		unsafe { IriRef::new_unchecked(std::str::from_utf8_unchecked(&self.0)) }
94	}
95
96	/// Returns the scheme of the URI reference, if any.
97	#[inline]
98	pub fn scheme(&self) -> Option<&Scheme> {
99		RiRefImpl::scheme_opt(self)
100	}
101
102	/// Returns the authority part of the URI reference, if any.
103	pub fn authority(&self) -> Option<&Authority> {
104		RiRefImpl::authority(self)
105	}
106
107	/// Returns the path of the URI reference.
108	pub fn path(&self) -> &Path {
109		RiRefImpl::path(self)
110	}
111
112	pub fn query(&self) -> Option<&Query> {
113		RiRefImpl::query(self)
114	}
115
116	pub fn fragment(&self) -> Option<&Fragment> {
117		RiRefImpl::fragment(self)
118	}
119
120	/// Resolve the URI reference against the given *base URI*.
121	///
122	/// Return the resolved URI.
123	/// See the [`UriRefBuf::resolve`] method for more information about the resolution process.
124	#[inline]
125	pub fn resolved(&self, base_iri: &(impl ?Sized + AsRef<Uri>)) -> UriBuf {
126		let iri_ref = self.to_owned();
127		iri_ref.into_resolved(base_iri)
128	}
129
130	/// Get this URI reference relatively to the given one.
131	///
132	/// # Example
133	/// ```
134	/// # use iref_core::UriRef;
135	/// let a = UriRef::new(b"https://crates.io/").unwrap();
136	/// let b = UriRef::new(b"https://crates.io/crates/iref").unwrap();
137	/// let c = UriRef::new(b"https://crates.io/crates/json-ld").unwrap();
138	/// assert_eq!(b.relative_to(a), "crates/iref");
139	/// assert_eq!(a.relative_to(b), "..");
140	/// assert_eq!(b.relative_to(c), "iref");
141	/// assert_eq!(c.relative_to(b), "json-ld");
142	/// ```
143	pub fn relative_to(&self, other: &(impl ?Sized + AsRef<UriRef>)) -> UriRefBuf {
144		RiRefImpl::relative_to(self, other.as_ref())
145	}
146
147	/// Get the suffix of this URI reference, if any, with regard to the given prefix URI reference..
148	///
149	/// Returns `Some((suffix, query, fragment))` if this URI reference is of the form
150	/// `prefix/suffix?query#fragment` where `prefix` is given as parameter.
151	/// Returns `None` otherwise.
152	/// If the `suffix` scheme or authority is different from this path, it will return `None`.
153	///
154	/// See [`Path::suffix`] for more details.
155	#[inline]
156	pub fn suffix(
157		&self,
158		prefix: &(impl ?Sized + AsRef<UriRef>),
159	) -> Option<(PathBuf, Option<&Query>, Option<&Fragment>)> {
160		RiRefImpl::suffix(self, prefix.as_ref())
161	}
162
163	/// The URI reference without the file name, query and fragment.
164	///
165	/// # Example
166	/// ```
167	/// # use iref_core::UriRef;
168	/// let a = UriRef::new(b"https://crates.io/crates/iref?query#fragment").unwrap();
169	/// let b = UriRef::new(b"https://crates.io/crates/iref/?query#fragment").unwrap();
170	/// assert_eq!(a.base(), b"https://crates.io/crates/");
171	/// assert_eq!(b.base(), b"https://crates.io/crates/iref/")
172	/// ```
173	#[inline]
174	pub fn base(&self) -> &Self {
175		unsafe { Self::new_unchecked(RiRefImpl::base(self)) }
176	}
177}
178
179impl AsRef<IriRef> for UriRef {
180	fn as_ref(&self) -> &IriRef {
181		self.as_iri_ref()
182	}
183}
184
185impl<'a> From<&'a UriRef> for &'a IriRef {
186	fn from(value: &'a UriRef) -> Self {
187		value.as_iri_ref()
188	}
189}
190
191impl<'a> TryFrom<&'a UriRef> for &'a Uri {
192	type Error = InvalidUri<&'a UriRef>;
193
194	fn try_from(value: &'a UriRef) -> Result<Self, Self::Error> {
195		value.as_uri().ok_or(InvalidUri(value))
196	}
197}
198
199impl<'a> TryFrom<&'a UriRef> for &'a Iri {
200	type Error = InvalidIri<&'a UriRef>;
201
202	fn try_from(value: &'a UriRef) -> Result<Self, Self::Error> {
203		value.as_iri().ok_or(InvalidIri(value))
204	}
205}
206
207bytestr_eq!(UriRef);
208
209impl PartialEq for UriRef {
210	fn eq(&self, other: &Self) -> bool {
211		self.parts() == other.parts()
212	}
213}
214
215impl<'a> PartialEq<&'a UriRef> for UriRef {
216	fn eq(&self, other: &&'a Self) -> bool {
217		*self == **other
218	}
219}
220
221impl PartialEq<UriRefBuf> for UriRef {
222	fn eq(&self, other: &UriRefBuf) -> bool {
223		*self == *other.as_uri_ref()
224	}
225}
226
227impl PartialEq<Uri> for UriRef {
228	fn eq(&self, other: &Uri) -> bool {
229		*self == *other.as_uri_ref()
230	}
231}
232
233impl<'a> PartialEq<&'a Uri> for UriRef {
234	fn eq(&self, other: &&'a Uri) -> bool {
235		*self == *other.as_uri_ref()
236	}
237}
238
239impl PartialEq<UriBuf> for UriRef {
240	fn eq(&self, other: &UriBuf) -> bool {
241		*self == *other.as_uri_ref()
242	}
243}
244
245impl Eq for UriRef {}
246
247impl PartialOrd for UriRef {
248	fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
249		Some(self.cmp(other))
250	}
251}
252
253impl<'a> PartialOrd<&'a UriRef> for UriRef {
254	fn partial_cmp(&self, other: &&'a Self) -> Option<std::cmp::Ordering> {
255		self.partial_cmp(*other)
256	}
257}
258
259impl PartialOrd<UriRefBuf> for UriRef {
260	fn partial_cmp(&self, other: &UriRefBuf) -> Option<std::cmp::Ordering> {
261		self.partial_cmp(other.as_uri_ref())
262	}
263}
264
265impl PartialOrd<Uri> for UriRef {
266	fn partial_cmp(&self, other: &Uri) -> Option<std::cmp::Ordering> {
267		self.partial_cmp(other.as_uri_ref())
268	}
269}
270
271impl<'a> PartialOrd<&'a Uri> for UriRef {
272	fn partial_cmp(&self, other: &&'a Uri) -> Option<std::cmp::Ordering> {
273		self.partial_cmp(other.as_uri_ref())
274	}
275}
276
277impl PartialOrd<UriBuf> for UriRef {
278	fn partial_cmp(&self, other: &UriBuf) -> Option<std::cmp::Ordering> {
279		self.partial_cmp(other.as_uri_ref())
280	}
281}
282
283impl Ord for UriRef {
284	fn cmp(&self, other: &Self) -> std::cmp::Ordering {
285		self.parts().cmp(&other.parts())
286	}
287}
288
289impl Hash for UriRef {
290	fn hash<H: hash::Hasher>(&self, state: &mut H) {
291		self.parts().hash(state)
292	}
293}
294
295impl RiRefImpl for UriRefBuf {
296	type Authority = Authority;
297	type Path = Path;
298	type Query = Query;
299	type Fragment = Fragment;
300
301	type RiRefBuf = Self;
302
303	fn as_bytes(&self) -> &[u8] {
304		&self.0
305	}
306}
307
308impl RiRefBufImpl for UriRefBuf {
309	type Ri = Uri;
310	type RiBuf = UriBuf;
311
312	unsafe fn new_unchecked(bytes: Vec<u8>) -> Self {
313		Self::new_unchecked(bytes)
314	}
315
316	unsafe fn as_mut_vec(&mut self) -> &mut Vec<u8> {
317		&mut self.0
318	}
319
320	fn into_bytes(self) -> Vec<u8> {
321		self.0
322	}
323}
324
325impl UriRefBuf {
326	/// Returns a mutable reference to the underlying `Vec<u8>` buffer
327	/// representing the URI reference.
328	///
329	/// # Safety
330	///
331	/// The caller must ensure that once the mutable reference is dropped, its
332	/// content is still a valid URI reference.
333	pub unsafe fn as_mut_vec(&mut self) -> &mut Vec<u8> {
334		&mut self.0
335	}
336
337	pub fn into_iri_ref(self) -> IriRefBuf {
338		unsafe { IriRefBuf::new_unchecked(String::from_utf8_unchecked(self.0)) }
339	}
340
341	pub fn try_into_uri(self) -> Result<UriBuf, InvalidUri<Self>> {
342		if self.scheme().is_some() {
343			unsafe { Ok(UriBuf::new_unchecked(self.0)) }
344		} else {
345			Err(InvalidUri(self))
346		}
347	}
348
349	pub fn try_into_iri(self) -> Result<IriBuf, InvalidIri<Self>> {
350		if self.scheme().is_some() {
351			unsafe { Ok(IriBuf::new_unchecked(String::from_utf8_unchecked(self.0))) }
352		} else {
353			Err(InvalidIri(self))
354		}
355	}
356
357	pub fn path_mut(&mut self) -> PathMut {
358		PathMut::from_impl(RiRefBufImpl::path_mut(self))
359	}
360
361	pub fn authority_mut(&mut self) -> Option<AuthorityMut> {
362		RiRefBufImpl::authority_mut(self).map(AuthorityMut::from_impl)
363	}
364
365	/// Sets the scheme part.
366	///
367	/// If there is no authority and the start of the path looks like a scheme
368	/// (e.g. `foo:`) then the path is prefixed with `./` to avoid being
369	/// confused with a scheme.
370	///
371	/// # Example
372	///
373	/// ```
374	/// # use iref_core as iref;
375	/// use iref::{UriRefBuf, uri::Scheme};
376	///
377	/// let mut a = UriRefBuf::new(b"foo/bar".to_vec()).unwrap();
378	/// a.set_scheme(Some(Scheme::new(b"http").unwrap()));
379	/// assert_eq!(a, "http:foo/bar");
380	///
381	/// let mut b = UriRefBuf::new(b"scheme://example.org/foo/bar".to_vec()).unwrap();
382	/// b.set_scheme(None);
383	/// assert_eq!(b, "//example.org/foo/bar");
384	///
385	/// let mut c = UriRefBuf::new(b"scheme:foo:bar".to_vec()).unwrap();
386	/// c.set_scheme(None);
387	/// assert_eq!(c, "./foo:bar");
388	/// ```
389	pub fn set_scheme(&mut self, scheme: Option<&Scheme>) {
390		RiRefBufImpl::set_scheme(self, scheme)
391	}
392
393	/// Sets the authority part.
394	///
395	/// If the path is relative, this also turns it into an absolute path,
396	/// since an authority cannot be followed by a relative path.
397	///
398	/// To avoid any ambiguity, if `authority` is `None` and the path starts
399	/// with `//`, it will be changed into `/.//` as to not be interpreted as
400	/// an authority part.
401	///
402	/// # Example
403	///
404	/// ```
405	/// # use iref_core as iref;
406	/// use iref::{UriRefBuf, uri::Authority};
407	///
408	/// let mut a = UriRefBuf::new(b"scheme:/path".to_vec()).unwrap();
409	/// a.set_authority(Some(Authority::new(b"example.org").unwrap()));
410	/// assert_eq!(a, b"scheme://example.org/path");
411	///
412	/// // When an authority is added before a relative path,
413	/// // the path becomes absolute.
414	/// let mut b = UriRefBuf::new(b"scheme:path".to_vec()).unwrap();
415	/// b.set_authority(Some(Authority::new(b"example.org").unwrap()));
416	/// assert_eq!(b, b"scheme://example.org/path");
417	///
418	/// // When an authority is removed and the path starts with `//`,
419	/// // a `/.` prefix is added to the path to avoid any ambiguity.
420	/// let mut c = UriRefBuf::new(b"scheme://example.org//path".to_vec()).unwrap();
421	/// c.set_authority(None);
422	/// assert_eq!(c, b"scheme:/.//path");
423	/// ```
424	pub fn set_authority(&mut self, authority: Option<&Authority>) {
425		RiRefBufImpl::set_authority(self, authority)
426	}
427
428	/// Sets the path part.
429	///
430	/// If there is an authority and the path is relative, this also turns it
431	/// into an absolute path, since an authority cannot be followed by a
432	/// relative path.
433	///
434	/// To avoid any ambiguity, if there is no authority and the path starts
435	/// with `//`, it will be changed into `/.//` as to not be interpreted as
436	/// an authority part. Similarly if there is no scheme nor authority and the
437	/// beginning of the new path looks like a scheme, it is prefixed with `./`
438	/// to not be confused with a scheme.
439	///
440	/// # Example
441	///
442	/// ```
443	/// # use iref_core as iref;
444	/// use iref::{UriRefBuf, uri::Path};
445	///
446	/// let mut a = UriRefBuf::new(b"http://example.org/old/path".to_vec()).unwrap();
447	/// a.set_path(Path::new(b"/foo/bar").unwrap());
448	/// assert_eq!(a, b"http://example.org/foo/bar");
449	///
450	/// // If there is an authority and the new path is relative,
451	/// // it is turned into an absolute path.
452	/// let mut b = UriRefBuf::new(b"http://example.org/old/path".to_vec()).unwrap();
453	/// b.set_path(Path::new(b"relative/path").unwrap());
454	/// assert_eq!(b, b"http://example.org/relative/path");
455	///
456	/// // If there is no authority and the path starts with `//`,
457	/// // it is prefixed with `/.` to avoid being confused with an authority.
458	/// let mut c = UriRefBuf::new(b"http:old/path".to_vec()).unwrap();
459	/// c.set_path(Path::new(b"//foo/bar").unwrap());
460	/// assert_eq!(c, b"http:/.//foo/bar");
461	///
462	/// // If there is no authority nor scheme, and the path beginning looks
463	/// // like a scheme, it is prefixed with `./` to avoid being confused with
464	/// // a scheme.
465	/// let mut d = UriRefBuf::new(b"old/path".to_vec()).unwrap();
466	/// d.set_path(Path::new(b"foo:bar").unwrap());
467	/// assert_eq!(d, b"./foo:bar");
468	/// ```
469	pub fn set_path(&mut self, path: &Path) {
470		RiRefBufImpl::set_path(self, path)
471	}
472
473	/// Sets the query part.
474	pub fn set_query(&mut self, query: Option<&Query>) {
475		RiRefBufImpl::set_query(self, query)
476	}
477
478	/// Sets the fragment part.
479	pub fn set_fragment(&mut self, fragment: Option<&Fragment>) {
480		RiRefBufImpl::set_fragment(self, fragment)
481	}
482
483	/// Resolve the URI reference.
484	///
485	/// ## Abnormal use of dot segments.
486	///
487	/// See <https://www.rfc-editor.org/errata/eid4547>
488	pub fn resolve(&mut self, base_iri: &(impl ?Sized + AsRef<Uri>)) {
489		RiRefBufImpl::resolve(self, base_iri.as_ref())
490	}
491
492	pub fn into_resolved(self, base_iri: &(impl ?Sized + AsRef<Uri>)) -> UriBuf {
493		RiRefBufImpl::into_resolved(self, base_iri.as_ref())
494	}
495}
496
497impl AsRef<IriRef> for UriRefBuf {
498	fn as_ref(&self) -> &IriRef {
499		self.as_iri_ref()
500	}
501}
502
503impl From<UriRefBuf> for IriRefBuf {
504	fn from(value: UriRefBuf) -> Self {
505		value.into_iri_ref()
506	}
507}
508
509impl TryFrom<UriRefBuf> for UriBuf {
510	type Error = InvalidUri<UriRefBuf>;
511
512	fn try_from(value: UriRefBuf) -> Result<Self, Self::Error> {
513		value.try_into_uri()
514	}
515}
516
517impl TryFrom<UriRefBuf> for IriBuf {
518	type Error = InvalidIri<UriRefBuf>;
519
520	fn try_from(value: UriRefBuf) -> Result<Self, Self::Error> {
521		value.try_into_iri()
522	}
523}
524
525bytestr_eq!(UriRefBuf);
526
527impl PartialEq<Uri> for UriRefBuf {
528	fn eq(&self, other: &Uri) -> bool {
529		*self.as_uri_ref() == *other.as_uri_ref()
530	}
531}
532
533impl<'a> PartialEq<&'a Uri> for UriRefBuf {
534	fn eq(&self, other: &&'a Uri) -> bool {
535		*self.as_uri_ref() == *other.as_uri_ref()
536	}
537}
538
539impl PartialEq<UriBuf> for UriRefBuf {
540	fn eq(&self, other: &UriBuf) -> bool {
541		*self.as_uri_ref() == *other.as_uri_ref()
542	}
543}
544
545impl PartialOrd<Uri> for UriRefBuf {
546	fn partial_cmp(&self, other: &Uri) -> Option<std::cmp::Ordering> {
547		self.as_uri_ref().partial_cmp(other.as_uri_ref())
548	}
549}
550
551impl<'a> PartialOrd<&'a Uri> for UriRefBuf {
552	fn partial_cmp(&self, other: &&'a Uri) -> Option<std::cmp::Ordering> {
553		self.as_uri_ref().partial_cmp(other.as_uri_ref())
554	}
555}
556
557impl PartialOrd<UriBuf> for UriRefBuf {
558	fn partial_cmp(&self, other: &UriBuf) -> Option<std::cmp::Ordering> {
559		self.as_uri_ref().partial_cmp(other.as_uri_ref())
560	}
561}
562
563#[cfg(test)]
564mod tests {
565	use super::*;
566
567	const PARTS: [(
568		&[u8],
569		(
570			Option<&[u8]>,
571			Option<&[u8]>,
572			&[u8],
573			Option<&[u8]>,
574			Option<&[u8]>,
575		),
576	); 36] = [
577		// 0 components.
578		(b"", (None, None, b"", None, None)),
579		// 1 component.
580		(b"scheme:", (Some(b"scheme"), None, b"", None, None)),
581		(b"//authority", (None, Some(b"authority"), b"", None, None)),
582		(b"path", (None, None, b"path", None, None)),
583		(b"/path", (None, None, b"/path", None, None)),
584		(b"/", (None, None, b"/", None, None)),
585		(b"foo//bar", (None, None, b"foo//bar", None, None)),
586		(b"?query", (None, None, b"", Some(b"query"), None)),
587		(b"#fragment", (None, None, b"", None, Some(b"fragment"))),
588		(
589			b"scheme:?query",
590			(Some(b"scheme"), None, b"", Some(b"query"), None),
591		),
592		// 2 components.
593		(
594			b"scheme://authority",
595			(Some(b"scheme"), Some(b"authority"), b"", None, None),
596		),
597		(b"scheme:path", (Some(b"scheme"), None, b"path", None, None)),
598		(
599			b"scheme:/path",
600			(Some(b"scheme"), None, b"/path", None, None),
601		),
602		(
603			b"scheme:?query",
604			(Some(b"scheme"), None, b"", Some(b"query"), None),
605		),
606		(
607			b"scheme:#fragment",
608			(Some(b"scheme"), None, b"", None, Some(b"fragment")),
609		),
610		(
611			b"//authority/path",
612			(None, Some(b"authority"), b"/path", None, None),
613		),
614		(
615			b"//authority?query",
616			(None, Some(b"authority"), b"", Some(b"query"), None),
617		),
618		(
619			b"//authority#fragment",
620			(None, Some(b"authority"), b"", None, Some(b"fragment")),
621		),
622		(b"path?query", (None, None, b"path", Some(b"query"), None)),
623		(b"/path?query", (None, None, b"/path", Some(b"query"), None)),
624		(
625			b"path#fragment",
626			(None, None, b"path", None, Some(b"fragment")),
627		),
628		(
629			b"?query#fragment",
630			(None, None, b"", Some(b"query"), Some(b"fragment")),
631		),
632		// 3 components
633		(
634			b"scheme://authority/path",
635			(Some(b"scheme"), Some(b"authority"), b"/path", None, None),
636		),
637		(
638			b"scheme://authority?query",
639			(
640				Some(b"scheme"),
641				Some(b"authority"),
642				b"",
643				Some(b"query"),
644				None,
645			),
646		),
647		(
648			b"scheme://authority#fragment",
649			(
650				Some(b"scheme"),
651				Some(b"authority"),
652				b"",
653				None,
654				Some(b"fragment"),
655			),
656		),
657		(
658			b"scheme:path?query",
659			(Some(b"scheme"), None, b"path", Some(b"query"), None),
660		),
661		(
662			b"scheme:path#fragment",
663			(Some(b"scheme"), None, b"path", None, Some(b"fragment")),
664		),
665		(
666			b"//authority/path?query",
667			(None, Some(b"authority"), b"/path", Some(b"query"), None),
668		),
669		(
670			b"//authority/path#fragment",
671			(None, Some(b"authority"), b"/path", None, Some(b"fragment")),
672		),
673		(
674			b"//authority?query#fragment",
675			(
676				None,
677				Some(b"authority"),
678				b"",
679				Some(b"query"),
680				Some(b"fragment"),
681			),
682		),
683		(
684			b"path?query#fragment",
685			(None, None, b"path", Some(b"query"), Some(b"fragment")),
686		),
687		// 4 components
688		(
689			b"scheme://authority/path?query",
690			(
691				Some(b"scheme"),
692				Some(b"authority"),
693				b"/path",
694				Some(b"query"),
695				None,
696			),
697		),
698		(
699			b"scheme://authority/path#fragment",
700			(
701				Some(b"scheme"),
702				Some(b"authority"),
703				b"/path",
704				None,
705				Some(b"fragment"),
706			),
707		),
708		(
709			b"scheme://authority?query#fragment",
710			(
711				Some(b"scheme"),
712				Some(b"authority"),
713				b"",
714				Some(b"query"),
715				Some(b"fragment"),
716			),
717		),
718		(
719			b"scheme:path?query#fragment",
720			(
721				Some(b"scheme"),
722				None,
723				b"path",
724				Some(b"query"),
725				Some(b"fragment"),
726			),
727		),
728		// 5 components
729		(
730			b"scheme://authority/path?query#fragment",
731			(
732				Some(b"scheme"),
733				Some(b"authority"),
734				b"/path",
735				Some(b"query"),
736				Some(b"fragment"),
737			),
738		),
739	];
740
741	#[test]
742	fn parts() {
743		for (input, expected) in PARTS {
744			let input = UriRef::new(input).unwrap();
745			let parts = input.parts();
746
747			assert_eq!(parts.scheme.map(Scheme::as_bytes), expected.0);
748			assert_eq!(parts.authority.map(Authority::as_bytes), expected.1);
749			assert_eq!(parts.path.as_bytes(), expected.2);
750			assert_eq!(parts.query.map(Query::as_bytes), expected.3);
751			assert_eq!(parts.fragment.map(Fragment::as_bytes), expected.4)
752		}
753	}
754
755	#[test]
756	fn scheme() {
757		for (input, expected) in PARTS {
758			let input = UriRef::new(input).unwrap();
759			assert_eq!(input.scheme().map(Scheme::as_bytes), expected.0)
760		}
761	}
762
763	#[test]
764	fn authority() {
765		for (input, expected) in PARTS {
766			let input = UriRef::new(input).unwrap();
767			// eprintln!("{input}: {expected:?}");
768			assert_eq!(input.authority().map(Authority::as_bytes), expected.1)
769		}
770	}
771
772	#[test]
773	fn set_authority() {
774		let vectors: [(&[u8], Option<&[u8]>, &[u8]); 3] = [
775			(
776				b"scheme:/path",
777				Some(b"authority"),
778				b"scheme://authority/path",
779			),
780			(
781				b"scheme:path",
782				Some(b"authority"),
783				b"scheme://authority/path",
784			),
785			(b"scheme://authority//path", None, b"scheme:/.//path"),
786		];
787
788		for (input, authority, expected) in vectors {
789			let mut buffer = UriRefBuf::new(input.to_vec()).unwrap();
790			let authority = authority.map(Authority::new).transpose().unwrap();
791			buffer.set_authority(authority);
792			// eprintln!("{input:?}, {authority:?} => {buffer}, {expected:?}");
793			assert_eq!(buffer.as_bytes(), expected)
794		}
795	}
796
797	#[test]
798	fn path() {
799		for (input, expected) in PARTS {
800			let input = UriRef::new(input).unwrap();
801			// eprintln!("{input}: {expected:?}");
802			assert_eq!(input.path().as_bytes(), expected.2)
803		}
804	}
805
806	#[test]
807	fn query() {
808		for (input, expected) in PARTS {
809			let input = UriRef::new(input).unwrap();
810			// eprintln!("{input}: {expected:?}");
811			assert_eq!(input.query().map(Query::as_bytes), expected.3)
812		}
813	}
814
815	#[test]
816	fn fragment() {
817		for (input, expected) in PARTS {
818			let input = UriRef::new(input).unwrap();
819			// eprintln!("{input}: {expected:?}");
820			assert_eq!(input.fragment().map(Fragment::as_bytes), expected.4)
821		}
822	}
823}