iref_core/iri/
reference.rs

1use std::hash::{self, Hash};
2
3use static_regular_grammar::RegularGrammar;
4
5use crate::{
6	common::{parse, str_eq, RiRefBufImpl, RiRefImpl},
7	uri::InvalidUriRef,
8	InvalidIri, InvalidUri, Iri, IriBuf, Uri, UriBuf, UriRef, UriRefBuf,
9};
10
11use super::{Authority, AuthorityMut, Fragment, Path, PathBuf, PathMut, Query, Scheme};
12
13/// IRI reference.
14#[derive(RegularGrammar)]
15#[grammar(
16	file = "src/iri/grammar.abnf",
17	entry_point = "IRI-reference",
18	name = "IRI reference",
19	cache = "automata/iri/reference.aut.cbor"
20)]
21#[grammar(sized(
22	IriRefBuf,
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 IriRef(str);
28
29impl RiRefImpl for IriRef {
30	type Authority = Authority;
31	type Path = Path;
32	type Query = Query;
33	type Fragment = Fragment;
34
35	type RiRefBuf = IriRefBuf;
36
37	fn as_bytes(&self) -> &[u8] {
38		self.0.as_bytes()
39	}
40}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
43pub struct IriRefParts<'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 IriRef {
52	pub fn parts(&self) -> IriRefParts {
53		let bytes = self.as_bytes();
54		let ranges = parse::reference_parts(bytes, 0);
55
56		IriRefParts {
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	/// Converts this IRI reference into an IRI, if possible.
74	#[inline]
75	pub fn as_iri(&self) -> Option<&Iri> {
76		if self.scheme().is_some() {
77			Some(unsafe { Iri::new_unchecked(&self.0) })
78		} else {
79			None
80		}
81	}
82
83	/// Converts this IRI reference into an URI, if possible.
84	pub fn as_uri(&self) -> Option<&Uri> {
85		Uri::new(self.as_bytes()).ok()
86	}
87
88	/// Converts this IRI reference into an URI reference, if possible.
89	pub fn as_uri_ref(&self) -> Option<&UriRef> {
90		UriRef::new(self.as_bytes()).ok()
91	}
92
93	/// Returns the scheme of the IRI reference, if any.
94	#[inline]
95	pub fn scheme(&self) -> Option<&Scheme> {
96		RiRefImpl::scheme_opt(self)
97	}
98
99	/// Returns the authority part of the IRI reference, if any.
100	pub fn authority(&self) -> Option<&Authority> {
101		RiRefImpl::authority(self)
102	}
103
104	/// Returns the path of the IRI reference.
105	pub fn path(&self) -> &Path {
106		RiRefImpl::path(self)
107	}
108
109	pub fn query(&self) -> Option<&Query> {
110		RiRefImpl::query(self)
111	}
112
113	pub fn fragment(&self) -> Option<&Fragment> {
114		RiRefImpl::fragment(self)
115	}
116
117	/// Resolve the IRI reference against the given *base IRI*.
118	///
119	/// Return the resolved IRI.
120	/// See the [`IriRefBuf::resolve`] method for more information about the resolution process.
121	#[inline]
122	pub fn resolved(&self, base_iri: &(impl ?Sized + AsRef<Iri>)) -> IriBuf {
123		let iri_ref = self.to_owned();
124		iri_ref.into_resolved(base_iri)
125	}
126
127	/// Get this IRI reference relatively to the given one.
128	///
129	/// # Example
130	/// ```
131	/// # use iref_core::IriRef;
132	/// let a = IriRef::new("https://crates.io/").unwrap();
133	/// let b = IriRef::new("https://crates.io/crates/iref").unwrap();
134	/// let c = IriRef::new("https://crates.io/crates/json-ld").unwrap();
135	/// assert_eq!(b.relative_to(a), "crates/iref");
136	/// assert_eq!(a.relative_to(b), "..");
137	/// assert_eq!(b.relative_to(c), "iref");
138	/// assert_eq!(c.relative_to(b), "json-ld");
139	/// ```
140	pub fn relative_to(&self, other: &(impl ?Sized + AsRef<IriRef>)) -> IriRefBuf {
141		RiRefImpl::relative_to(self, other.as_ref())
142	}
143
144	/// Get the suffix of this IRI reference, if any, with regard to the given prefix IRI reference..
145	///
146	/// Returns `Some((suffix, query, fragment))` if this IRI reference is of the form
147	/// `prefix/suffix?query#fragment` where `prefix` is given as parameter.
148	/// Returns `None` otherwise.
149	/// If the `suffix` scheme or authority is different from this path, it will return `None`.
150	///
151	/// See [`Path::suffix`] for more details.
152	#[inline]
153	pub fn suffix(
154		&self,
155		prefix: &(impl ?Sized + AsRef<IriRef>),
156	) -> Option<(PathBuf, Option<&Query>, Option<&Fragment>)> {
157		RiRefImpl::suffix(self, prefix.as_ref())
158	}
159
160	/// The IRI reference without the file name, query and fragment.
161	///
162	/// # Example
163	/// ```
164	/// # use iref_core::IriRef;
165	/// let a = IriRef::new("https://crates.io/crates/iref?query#fragment").unwrap();
166	/// let b = IriRef::new("https://crates.io/crates/iref/?query#fragment").unwrap();
167	/// assert_eq!(a.base(), "https://crates.io/crates/");
168	/// assert_eq!(b.base(), "https://crates.io/crates/iref/")
169	/// ```
170	#[inline]
171	pub fn base(&self) -> &Self {
172		unsafe { Self::new_unchecked(std::str::from_utf8_unchecked(RiRefImpl::base(self))) }
173	}
174}
175
176impl<'a> TryFrom<&'a IriRef> for &'a Iri {
177	type Error = InvalidIri<&'a IriRef>;
178
179	fn try_from(value: &'a IriRef) -> Result<Self, Self::Error> {
180		value.as_iri().ok_or(InvalidIri(value))
181	}
182}
183
184impl<'a> TryFrom<&'a IriRef> for &'a Uri {
185	type Error = InvalidUri<&'a IriRef>;
186
187	fn try_from(value: &'a IriRef) -> Result<Self, Self::Error> {
188		value.as_uri().ok_or(InvalidUri(value))
189	}
190}
191
192impl<'a> TryFrom<&'a IriRef> for &'a UriRef {
193	type Error = InvalidUriRef<&'a IriRef>;
194
195	fn try_from(value: &'a IriRef) -> Result<Self, Self::Error> {
196		value.as_uri_ref().ok_or(InvalidUriRef(value))
197	}
198}
199
200str_eq!(IriRef);
201
202impl PartialEq for IriRef {
203	fn eq(&self, other: &Self) -> bool {
204		self.parts() == other.parts()
205	}
206}
207
208impl<'a> PartialEq<&'a IriRef> for IriRef {
209	fn eq(&self, other: &&'a Self) -> bool {
210		*self == **other
211	}
212}
213
214impl PartialEq<IriRefBuf> for IriRef {
215	fn eq(&self, other: &IriRefBuf) -> bool {
216		*self == *other.as_iri_ref()
217	}
218}
219
220impl PartialEq<Iri> for IriRef {
221	fn eq(&self, other: &Iri) -> bool {
222		*self == *other.as_iri_ref()
223	}
224}
225
226impl<'a> PartialEq<&'a Iri> for IriRef {
227	fn eq(&self, other: &&'a Iri) -> bool {
228		*self == *other.as_iri_ref()
229	}
230}
231
232impl PartialEq<IriBuf> for IriRef {
233	fn eq(&self, other: &IriBuf) -> bool {
234		*self == *other.as_iri_ref()
235	}
236}
237
238impl Eq for IriRef {}
239
240impl PartialOrd for IriRef {
241	fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
242		Some(self.cmp(other))
243	}
244}
245
246impl<'a> PartialOrd<&'a IriRef> for IriRef {
247	fn partial_cmp(&self, other: &&'a Self) -> Option<std::cmp::Ordering> {
248		self.partial_cmp(*other)
249	}
250}
251
252impl PartialOrd<IriRefBuf> for IriRef {
253	fn partial_cmp(&self, other: &IriRefBuf) -> Option<std::cmp::Ordering> {
254		self.partial_cmp(other.as_iri_ref())
255	}
256}
257
258impl PartialOrd<Iri> for IriRef {
259	fn partial_cmp(&self, other: &Iri) -> Option<std::cmp::Ordering> {
260		self.partial_cmp(other.as_iri_ref())
261	}
262}
263
264impl<'a> PartialOrd<&'a Iri> for IriRef {
265	fn partial_cmp(&self, other: &&'a Iri) -> Option<std::cmp::Ordering> {
266		self.partial_cmp(other.as_iri_ref())
267	}
268}
269
270impl PartialOrd<IriBuf> for IriRef {
271	fn partial_cmp(&self, other: &IriBuf) -> Option<std::cmp::Ordering> {
272		self.partial_cmp(other.as_iri_ref())
273	}
274}
275
276impl Ord for IriRef {
277	fn cmp(&self, other: &Self) -> std::cmp::Ordering {
278		self.parts().cmp(&other.parts())
279	}
280}
281
282impl Hash for IriRef {
283	fn hash<H: hash::Hasher>(&self, state: &mut H) {
284		self.parts().hash(state)
285	}
286}
287
288impl RiRefImpl for IriRefBuf {
289	type Authority = Authority;
290	type Path = Path;
291	type Query = Query;
292	type Fragment = Fragment;
293
294	type RiRefBuf = Self;
295
296	fn as_bytes(&self) -> &[u8] {
297		self.0.as_bytes()
298	}
299}
300
301impl RiRefBufImpl for IriRefBuf {
302	type Ri = Iri;
303	type RiBuf = IriBuf;
304
305	unsafe fn new_unchecked(bytes: Vec<u8>) -> Self {
306		Self::new_unchecked(String::from_utf8_unchecked(bytes))
307	}
308
309	unsafe fn as_mut_vec(&mut self) -> &mut Vec<u8> {
310		self.0.as_mut_vec()
311	}
312
313	fn into_bytes(self) -> Vec<u8> {
314		self.0.into_bytes()
315	}
316}
317
318impl IriRefBuf {
319	/// Creates a new IRI reference from a byte string.
320	#[inline]
321	pub fn from_vec(buffer: Vec<u8>) -> Result<Self, InvalidIriRef<Vec<u8>>> {
322		match String::from_utf8(buffer) {
323			Ok(string) => {
324				Self::new(string).map_err(|InvalidIriRef(s)| InvalidIriRef(s.into_bytes()))
325			}
326			Err(e) => Err(InvalidIriRef(e.into_bytes())),
327		}
328	}
329
330	/// Creates a new IRI reference from a byte string without varidation.
331	///
332	/// # Safety
333	///
334	/// The input bytes must be a valid IRI reference.
335	#[inline]
336	pub unsafe fn from_vec_unchecked(buffer: Vec<u8>) -> Self {
337		Self::new_unchecked(String::from_utf8_unchecked(buffer))
338	}
339
340	/// Converts this IRI reference into an IRI, if possible.
341	pub fn try_into_iri(self) -> Result<IriBuf, InvalidIri<Self>> {
342		if self.scheme().is_some() {
343			unsafe { Ok(IriBuf::new_unchecked(self.0)) }
344		} else {
345			Err(InvalidIri(self))
346		}
347	}
348
349	/// Converts this IRI reference into an URI, if possible.
350	pub fn try_into_uri(self) -> Result<UriBuf, InvalidUri<Self>> {
351		UriBuf::new(self.into_bytes()).map_err(|InvalidUri(bytes)| unsafe {
352			InvalidUri(Self::new_unchecked(String::from_utf8_unchecked(bytes)))
353		})
354	}
355
356	/// Converts this IRI reference into an URI reference, if possible.
357	pub fn try_into_uri_ref(self) -> Result<UriRefBuf, InvalidUriRef<Self>> {
358		UriRefBuf::new(self.into_bytes()).map_err(|InvalidUriRef(bytes)| unsafe {
359			InvalidUriRef(Self::new_unchecked(String::from_utf8_unchecked(bytes)))
360		})
361	}
362
363	/// Returns the underlying bytes representing the IRI reference as a mutable
364	/// `Vec<u8>`.
365	///
366	/// # Safety
367	///
368	/// The caller must ensure that once the mutable reference is dropped, its
369	/// content is still a valid IRI reference.
370	pub unsafe fn as_mut_vec(&mut self) -> &mut Vec<u8> {
371		self.0.as_mut_vec()
372	}
373
374	pub fn path_mut(&mut self) -> PathMut {
375		PathMut::from_impl(RiRefBufImpl::path_mut(self))
376	}
377
378	pub fn authority_mut(&mut self) -> Option<AuthorityMut> {
379		RiRefBufImpl::authority_mut(self).map(AuthorityMut::from_impl)
380	}
381
382	/// Sets the scheme part.
383	///
384	/// If there is no authority and the start of the path looks like a scheme
385	/// (e.g. `foo:`) then the path is prefixed with `./` to avoid being
386	/// confused with a scheme.
387	///
388	/// # Example
389	///
390	/// ```
391	/// # use iref_core as iref;
392	/// use iref::{IriRefBuf, iri::Scheme};
393	///
394	/// let mut a = IriRefBuf::new("foo/bar".to_string()).unwrap();
395	/// a.set_scheme(Some(Scheme::new(b"http").unwrap()));
396	/// assert_eq!(a, "http:foo/bar");
397	///
398	/// let mut b = IriRefBuf::new("scheme://example.org/foo/bar".to_string()).unwrap();
399	/// b.set_scheme(None);
400	/// assert_eq!(b, "//example.org/foo/bar");
401	///
402	/// let mut c = IriRefBuf::new("scheme:foo:bar".to_string()).unwrap();
403	/// c.set_scheme(None);
404	/// assert_eq!(c, "./foo:bar");
405	/// ```
406	pub fn set_scheme(&mut self, scheme: Option<&Scheme>) {
407		RiRefBufImpl::set_scheme(self, scheme)
408	}
409
410	/// Sets the authority part.
411	///
412	/// If the path is relative, this also turns it into an absolute path,
413	/// since an authority cannot be followed by a relative path.
414	///
415	/// To avoid any ambiguity, if `authority` is `None` and the path starts
416	/// with `//`, it will be changed into `/.//` as to not be interpreted as
417	/// an authority part.
418	///
419	/// # Example
420	///
421	/// ```
422	/// # use iref_core as iref;
423	/// use iref::{IriRefBuf, iri::Authority};
424	///
425	/// let mut a = IriRefBuf::new("scheme:/path".to_string()).unwrap();
426	/// a.set_authority(Some(Authority::new("example.org").unwrap()));
427	/// assert_eq!(a, "scheme://example.org/path");
428	///
429	/// // When an authority is added before a relative path,
430	/// // the path becomes absolute.
431	/// let mut b = IriRefBuf::new("scheme:path".to_string()).unwrap();
432	/// b.set_authority(Some(Authority::new("example.org").unwrap()));
433	/// assert_eq!(b, "scheme://example.org/path");
434	///
435	/// // When an authority is removed and the path starts with `//`,
436	/// // a `/.` prefix is added to the path to avoid any ambiguity.
437	/// let mut c = IriRefBuf::new("scheme://example.org//path".to_string()).unwrap();
438	/// c.set_authority(None);
439	/// assert_eq!(c, "scheme:/.//path");
440	/// ```
441	pub fn set_authority(&mut self, authority: Option<&Authority>) {
442		RiRefBufImpl::set_authority(self, authority)
443	}
444
445	/// Sets the path part.
446	///
447	/// If there is an authority and the path is relative, this also turns it
448	/// into an absolute path, since an authority cannot be followed by a
449	/// relative path.
450	///
451	/// To avoid any ambiguity, if there is no authority and the path starts
452	/// with `//`, it will be changed into `/.//` as to not be interpreted as
453	/// an authority part. Similarly if there is no scheme nor authority and the
454	/// beginning of the new path looks like a scheme, it is prefixed with `./`
455	/// to not be confused with a scheme.
456	///
457	/// # Example
458	///
459	/// ```
460	/// # use iref_core as iref;
461	/// use iref::{IriRefBuf, iri::Path};
462	///
463	/// let mut a = IriRefBuf::new("http://example.org/old/path".to_string()).unwrap();
464	/// a.set_path(Path::new("/foo/bar").unwrap());
465	/// assert_eq!(a, "http://example.org/foo/bar");
466	///
467	/// // If there is an authority and the new path is relative,
468	/// // it is turned into an absolute path.
469	/// let mut b = IriRefBuf::new("http://example.org/old/path".to_string()).unwrap();
470	/// b.set_path(Path::new("relative/path").unwrap());
471	/// assert_eq!(b, "http://example.org/relative/path");
472	///
473	/// // If there is no authority and the path starts with `//`,
474	/// // it is prefixed with `/.` to avoid being confused with an authority.
475	/// let mut c = IriRefBuf::new("http:old/path".to_string()).unwrap();
476	/// c.set_path(Path::new("//foo/bar").unwrap());
477	/// assert_eq!(c, "http:/.//foo/bar");
478	///
479	/// // If there is no authority nor scheme, and the path beginning looks
480	/// // like a scheme, it is prefixed with `./` to avoid being confused with
481	/// // a scheme.
482	/// let mut d = IriRefBuf::new("old/path".to_string()).unwrap();
483	/// d.set_path(Path::new("foo:bar").unwrap());
484	/// assert_eq!(d, "./foo:bar");
485	/// ```
486	pub fn set_path(&mut self, path: &Path) {
487		RiRefBufImpl::set_path(self, path)
488	}
489
490	/// Sets the query part.
491	pub fn set_query(&mut self, query: Option<&Query>) {
492		RiRefBufImpl::set_query(self, query)
493	}
494
495	/// Sets the fragment part.
496	pub fn set_fragment(&mut self, fragment: Option<&Fragment>) {
497		RiRefBufImpl::set_fragment(self, fragment)
498	}
499
500	/// Resolve the IRI reference.
501	///
502	/// ## Abnormal use of dot segments.
503	///
504	/// See <https://www.rfc-editor.org/errata/eid4547>
505	pub fn resolve(&mut self, base_iri: &(impl ?Sized + AsRef<Iri>)) {
506		RiRefBufImpl::resolve(self, base_iri.as_ref())
507	}
508
509	pub fn into_resolved(self, base_iri: &(impl ?Sized + AsRef<Iri>)) -> IriBuf {
510		RiRefBufImpl::into_resolved(self, base_iri.as_ref())
511	}
512}
513
514impl TryFrom<IriRefBuf> for IriBuf {
515	type Error = InvalidIri<IriRefBuf>;
516
517	fn try_from(value: IriRefBuf) -> Result<Self, Self::Error> {
518		value.try_into_iri()
519	}
520}
521
522impl TryFrom<IriRefBuf> for UriBuf {
523	type Error = InvalidUri<IriRefBuf>;
524
525	fn try_from(value: IriRefBuf) -> Result<Self, Self::Error> {
526		value.try_into_uri()
527	}
528}
529
530impl TryFrom<IriRefBuf> for UriRefBuf {
531	type Error = InvalidUriRef<IriRefBuf>;
532
533	fn try_from(value: IriRefBuf) -> Result<Self, Self::Error> {
534		value.try_into_uri_ref()
535	}
536}
537
538str_eq!(IriRefBuf);
539
540impl PartialEq<Iri> for IriRefBuf {
541	fn eq(&self, other: &Iri) -> bool {
542		*self.as_iri_ref() == *other.as_iri_ref()
543	}
544}
545
546impl<'a> PartialEq<&'a Iri> for IriRefBuf {
547	fn eq(&self, other: &&'a Iri) -> bool {
548		*self.as_iri_ref() == *other.as_iri_ref()
549	}
550}
551
552impl PartialEq<IriBuf> for IriRefBuf {
553	fn eq(&self, other: &IriBuf) -> bool {
554		*self.as_iri_ref() == *other.as_iri_ref()
555	}
556}
557
558impl PartialOrd<Iri> for IriRefBuf {
559	fn partial_cmp(&self, other: &Iri) -> Option<std::cmp::Ordering> {
560		self.as_iri_ref().partial_cmp(other.as_iri_ref())
561	}
562}
563
564impl<'a> PartialOrd<&'a Iri> for IriRefBuf {
565	fn partial_cmp(&self, other: &&'a Iri) -> Option<std::cmp::Ordering> {
566		self.as_iri_ref().partial_cmp(other.as_iri_ref())
567	}
568}
569
570impl PartialOrd<IriBuf> for IriRefBuf {
571	fn partial_cmp(&self, other: &IriBuf) -> Option<std::cmp::Ordering> {
572		self.as_iri_ref().partial_cmp(other.as_iri_ref())
573	}
574}
575
576#[cfg(test)]
577mod tests {
578	use super::*;
579
580	const PARTS: [(
581		&str,
582		(Option<&str>, Option<&str>, &str, Option<&str>, Option<&str>),
583	); 36] = [
584		// 0 components.
585		("", (None, None, "", None, None)),
586		// 1 component.
587		("scheme:", (Some("scheme"), None, "", None, None)),
588		("//authority", (None, Some("authority"), "", None, None)),
589		("path", (None, None, "path", None, None)),
590		("/path", (None, None, "/path", None, None)),
591		("/", (None, None, "/", None, None)),
592		("foo//bar", (None, None, "foo//bar", None, None)),
593		("?query", (None, None, "", Some("query"), None)),
594		("#fragment", (None, None, "", None, Some("fragment"))),
595		(
596			"scheme:?query",
597			(Some("scheme"), None, "", Some("query"), None),
598		),
599		// 2 components.
600		(
601			"scheme://authority",
602			(Some("scheme"), Some("authority"), "", None, None),
603		),
604		("scheme:path", (Some("scheme"), None, "path", None, None)),
605		("scheme:/path", (Some("scheme"), None, "/path", None, None)),
606		(
607			"scheme:?query",
608			(Some("scheme"), None, "", Some("query"), None),
609		),
610		(
611			"scheme:#fragment",
612			(Some("scheme"), None, "", None, Some("fragment")),
613		),
614		(
615			"//authority/path",
616			(None, Some("authority"), "/path", None, None),
617		),
618		(
619			"//authority?query",
620			(None, Some("authority"), "", Some("query"), None),
621		),
622		(
623			"//authority#fragment",
624			(None, Some("authority"), "", None, Some("fragment")),
625		),
626		("path?query", (None, None, "path", Some("query"), None)),
627		("/path?query", (None, None, "/path", Some("query"), None)),
628		(
629			"path#fragment",
630			(None, None, "path", None, Some("fragment")),
631		),
632		(
633			"?query#fragment",
634			(None, None, "", Some("query"), Some("fragment")),
635		),
636		// 3 components
637		(
638			"scheme://authority/path",
639			(Some("scheme"), Some("authority"), "/path", None, None),
640		),
641		(
642			"scheme://authority?query",
643			(Some("scheme"), Some("authority"), "", Some("query"), None),
644		),
645		(
646			"scheme://authority#fragment",
647			(
648				Some("scheme"),
649				Some("authority"),
650				"",
651				None,
652				Some("fragment"),
653			),
654		),
655		(
656			"scheme:path?query",
657			(Some("scheme"), None, "path", Some("query"), None),
658		),
659		(
660			"scheme:path#fragment",
661			(Some("scheme"), None, "path", None, Some("fragment")),
662		),
663		(
664			"//authority/path?query",
665			(None, Some("authority"), "/path", Some("query"), None),
666		),
667		(
668			"//authority/path#fragment",
669			(None, Some("authority"), "/path", None, Some("fragment")),
670		),
671		(
672			"//authority?query#fragment",
673			(None, Some("authority"), "", Some("query"), Some("fragment")),
674		),
675		(
676			"path?query#fragment",
677			(None, None, "path", Some("query"), Some("fragment")),
678		),
679		// 4 components
680		(
681			"scheme://authority/path?query",
682			(
683				Some("scheme"),
684				Some("authority"),
685				"/path",
686				Some("query"),
687				None,
688			),
689		),
690		(
691			"scheme://authority/path#fragment",
692			(
693				Some("scheme"),
694				Some("authority"),
695				"/path",
696				None,
697				Some("fragment"),
698			),
699		),
700		(
701			"scheme://authority?query#fragment",
702			(
703				Some("scheme"),
704				Some("authority"),
705				"",
706				Some("query"),
707				Some("fragment"),
708			),
709		),
710		(
711			"scheme:path?query#fragment",
712			(
713				Some("scheme"),
714				None,
715				"path",
716				Some("query"),
717				Some("fragment"),
718			),
719		),
720		// 5 components
721		(
722			"scheme://authority/path?query#fragment",
723			(
724				Some("scheme"),
725				Some("authority"),
726				"/path",
727				Some("query"),
728				Some("fragment"),
729			),
730		),
731	];
732
733	#[test]
734	fn parts() {
735		for (input, expected) in PARTS {
736			let input = IriRef::new(input).unwrap();
737			let parts = input.parts();
738
739			assert_eq!(parts.scheme.map(Scheme::as_str), expected.0);
740			assert_eq!(parts.authority.map(Authority::as_str), expected.1);
741			assert_eq!(parts.path.as_str(), expected.2);
742			assert_eq!(parts.query.map(Query::as_str), expected.3);
743			assert_eq!(parts.fragment.map(Fragment::as_str), expected.4)
744		}
745	}
746
747	#[test]
748	fn scheme() {
749		for (input, expected) in PARTS {
750			let input = IriRef::new(input).unwrap();
751			assert_eq!(input.scheme().map(Scheme::as_str), expected.0)
752		}
753	}
754
755	#[test]
756	fn authority() {
757		for (input, expected) in PARTS {
758			let input = IriRef::new(input).unwrap();
759			// eprintln!("{input}: {expected:?}");
760			assert_eq!(input.authority().map(Authority::as_str), expected.1)
761		}
762	}
763
764	#[test]
765	fn set_authority() {
766		let vectors = [
767			("scheme:/path", Some("authority"), "scheme://authority/path"),
768			("scheme:path", Some("authority"), "scheme://authority/path"),
769			("scheme://authority//path", None, "scheme:/.//path"),
770		];
771
772		for (input, authority, expected) in vectors {
773			let mut buffer = IriRefBuf::new(input.to_string()).unwrap();
774			let authority = authority.map(Authority::new).transpose().unwrap();
775			buffer.set_authority(authority);
776			// eprintln!("{input}, {authority:?} => {buffer}, {expected}");
777			assert_eq!(buffer.as_str(), expected)
778		}
779	}
780
781	#[test]
782	fn path() {
783		for (input, expected) in PARTS {
784			let input = IriRef::new(input).unwrap();
785			// eprintln!("{input}: {expected:?}");
786			assert_eq!(input.path().as_str(), expected.2)
787		}
788	}
789
790	#[test]
791	fn query() {
792		for (input, expected) in PARTS {
793			let input = IriRef::new(input).unwrap();
794			// eprintln!("{input}: {expected:?}");
795			assert_eq!(input.query().map(Query::as_str), expected.3)
796		}
797	}
798
799	#[test]
800	fn fragment() {
801		for (input, expected) in PARTS {
802			let input = IriRef::new(input).unwrap();
803			// eprintln!("{input}: {expected:?}");
804			assert_eq!(input.fragment().map(Fragment::as_str), expected.4)
805		}
806	}
807
808	#[test]
809	fn disambiguate_scheme() {
810		let mut iri_ref = IriRefBuf::new("scheme:a:b/c".to_string()).unwrap();
811		iri_ref.set_scheme(None);
812		assert_eq!(iri_ref.as_str(), "./a:b/c")
813	}
814
815	#[test]
816	fn disambiguate_authority() {
817		let mut iri_ref = IriRefBuf::new("//host//path".to_string()).unwrap();
818		iri_ref.set_authority(None);
819		assert_eq!(iri_ref.as_str(), "/.//path")
820	}
821
822	#[test]
823	fn unambiguous_resolution() {
824		let base_iri = Iri::new("http:/a/b").unwrap();
825		let tests = [("../..//", "http:/")];
826
827		for (relative, absolute) in &tests {
828			// println!("{} => {}", relative, absolute);
829			assert_eq!(IriRef::new(relative).unwrap().resolved(base_iri), *absolute);
830		}
831	}
832
833	#[test]
834	fn resolution_normal() {
835		// https://www.w3.org/2004/04/uri-rel-test.html
836		let base_iri = Iri::new("http://a/b/c/d;p?q").unwrap();
837
838		let tests = [
839			("g:h", "g:h"),
840			("g", "http://a/b/c/g"),
841			("./g", "http://a/b/c/g"),
842			("g/", "http://a/b/c/g/"),
843			("/g", "http://a/g"),
844			("//g", "http://g"),
845			("?y", "http://a/b/c/d;p?y"),
846			("g?y", "http://a/b/c/g?y"),
847			("#s", "http://a/b/c/d;p?q#s"),
848			("g#s", "http://a/b/c/g#s"),
849			("g?y#s", "http://a/b/c/g?y#s"),
850			(";x", "http://a/b/c/;x"),
851			("g;x", "http://a/b/c/g;x"),
852			("g;x?y#s", "http://a/b/c/g;x?y#s"),
853			("", "http://a/b/c/d;p?q"),
854			(".", "http://a/b/c/"),
855			("./", "http://a/b/c/"),
856			("..", "http://a/b/"),
857			("../", "http://a/b/"),
858			("../g", "http://a/b/g"),
859			("../..", "http://a/"),
860			("../../", "http://a/"),
861			("../../g", "http://a/g"),
862		];
863
864		for (relative, absolute) in &tests {
865			println!("{} => {}", relative, absolute);
866			assert_eq!(IriRef::new(relative).unwrap().resolved(base_iri), *absolute);
867		}
868	}
869
870	#[test]
871	fn resolution_abnormal() {
872		// https://www.w3.org/2004/04/uri-rel-test.html
873		// NOTE we implement [Errata 4547](https://www.rfc-editor.org/errata/eid4547)
874		let base_iri = Iri::new("http://a/b/c/d;p?q").unwrap();
875
876		let tests = [
877			("../../../g", "http://a/g"),
878			("../../../../g", "http://a/g"),
879			("/./g", "http://a/g"),
880			("/../g", "http://a/g"),
881			("g.", "http://a/b/c/g."),
882			(".g", "http://a/b/c/.g"),
883			("g..", "http://a/b/c/g.."),
884			("..g", "http://a/b/c/..g"),
885			("./../g", "http://a/b/g"),
886			("./g/.", "http://a/b/c/g/"),
887			("g/./h", "http://a/b/c/g/h"),
888			("g/../h", "http://a/b/c/h"),
889			("g;x=1/./y", "http://a/b/c/g;x=1/y"),
890			("g;x=1/../y", "http://a/b/c/y"),
891			("g?y/./x", "http://a/b/c/g?y/./x"),
892			("g?y/../x", "http://a/b/c/g?y/../x"),
893			("g#s/./x", "http://a/b/c/g#s/./x"),
894			("g#s/../x", "http://a/b/c/g#s/../x"),
895			("http:g", "http:g"),
896		];
897
898		for (relative, absolute) in &tests {
899			// println!("{} => {}", relative, absolute);
900			assert_eq!(IriRef::new(relative).unwrap().resolved(base_iri), *absolute);
901		}
902	}
903
904	#[test]
905	fn more_resolutions1() {
906		let base_iri = Iri::new("http://a/bb/ccc/d;p?q").unwrap();
907
908		let tests = [
909			("#s", "http://a/bb/ccc/d;p?q#s"),
910			("", "http://a/bb/ccc/d;p?q"),
911		];
912
913		for (relative, absolute) in &tests {
914			println!("{} => {}", relative, absolute);
915			let buffer: crate::IriBuf = IriRef::new(relative).unwrap().resolved(base_iri);
916			assert_eq!(buffer.as_str(), *absolute);
917		}
918	}
919
920	#[test]
921	fn more_resolutions2() {
922		let base_iri = Iri::new("http://a/bb/ccc/./d;p?q").unwrap();
923
924		let tests = [
925			("..", "http://a/bb/"),
926			("../", "http://a/bb/"),
927			("../g", "http://a/bb/g"),
928			("../..", "http://a/"),
929			("../../", "http://a/"),
930			("../../g", "http://a/g"),
931		];
932
933		for (relative, absolute) in &tests {
934			// println!("{} => {}", relative, absolute);
935			let buffer: crate::IriBuf = IriRef::new(relative).unwrap().resolved(base_iri);
936			assert_eq!(buffer.as_str(), *absolute);
937		}
938	}
939
940	#[test]
941	fn more_resolutions3() {
942		let base_iri = Iri::new("http://ab//de//ghi").unwrap();
943
944		let tests = [
945			("xyz", "http://ab//de//xyz"),
946			("./xyz", "http://ab//de//xyz"),
947			("../xyz", "http://ab//de/xyz"),
948		];
949
950		for (relative, absolute) in &tests {
951			println!("{} => {}", relative, absolute);
952			let buffer: crate::IriBuf = IriRef::new(relative).unwrap().resolved(base_iri);
953			assert_eq!(buffer.as_str(), *absolute);
954		}
955	}
956
957	#[test]
958	fn more_resolutions4() {
959		let base_iri = Iri::new("http://a/bb/ccc/../d;p?q").unwrap();
960
961		let tests = [("../../", "http://a/")];
962
963		for (relative, absolute) in &tests {
964			// println!("{} => {}", relative, absolute);
965			let buffer: crate::IriBuf = IriRef::new(relative).unwrap().resolved(base_iri);
966			assert_eq!(buffer.as_str(), *absolute);
967		}
968	}
969
970	// https://github.com/timothee-haudebourg/iref/issues/14
971	#[test]
972	fn reference_resolution_with_scheme_no_disambiguation() {
973		let base = Iri::new("scheme:a:b/").unwrap();
974		let mut iri = IriRefBuf::new("Foo".to_string()).unwrap();
975		iri.resolve(base);
976
977		assert_eq!(iri.to_string(), "scheme:a:b/Foo")
978	}
979
980	#[test]
981	fn relative_to() {
982		let base =
983			IriRef::new("https://w3c.github.io/json-ld-api/tests/compact/0066-in.jsonld").unwrap();
984		let vectors = [
985			(
986				"https://w3c.github.io/json-ld-api/tests/compact/link",
987				"link",
988			),
989			(
990				"https://w3c.github.io/json-ld-api/tests/compact/0066-in.jsonld#fragment-works",
991				"#fragment-works",
992			),
993			(
994				"https://w3c.github.io/json-ld-api/tests/compact/0066-in.jsonld?query=works",
995				"?query=works",
996			),
997			("https://w3c.github.io/json-ld-api/tests/", "../"),
998			("https://w3c.github.io/json-ld-api/", "../../"),
999			("https://w3c.github.io/json-ld-api/parent", "../../parent"),
1000			(
1001				"https://w3c.github.io/json-ld-api/parent#fragment",
1002				"../../parent#fragment",
1003			),
1004			(
1005				"https://w3c.github.io/parent-parent-eq-root",
1006				"../../../parent-parent-eq-root",
1007			),
1008			(
1009				"http://example.org/scheme-relative",
1010				"http://example.org/scheme-relative",
1011			),
1012			(
1013				"https://w3c.github.io/json-ld-api/tests/compact/0066-in.jsonld",
1014				"0066-in.jsonld",
1015			),
1016		];
1017
1018		for (input, expected) in &vectors {
1019			let input = IriRef::new(input).unwrap();
1020			assert_eq!(input.relative_to(base), *expected)
1021		}
1022	}
1023}