Skip to main content

iref/uri/
reference.rs

1use core::{
2	cmp::Ordering,
3	hash::{Hash, Hasher},
4};
5
6use crate::{Port, Uri};
7
8#[cfg(feature = "std")]
9use crate::{InvalidUri, PathContext, UriBuf};
10
11use super::{Authority, Fragment, Host, Path, Query, Scheme, UserInfo};
12
13#[cfg(feature = "std")]
14use super::{
15	AuthorityMut, InvalidAuthority, InvalidFragment, InvalidPath, InvalidQuery, PathBuf, PathMut,
16	Segment,
17};
18
19/// URI reference.
20#[derive(static_automata::Validate, str_newtype::StrNewType)]
21#[automaton(super::grammar::UriRef)]
22#[newtype(name = "URI reference", ord([u8], &[u8], str, &str))]
23#[cfg_attr(
24	feature = "std",
25	newtype(ord(Vec<u8>, String), owned(UriRefBuf, derive(Default, PartialEq, Eq, PartialOrd, Ord, Hash)))
26)]
27#[cfg_attr(feature = "serde", newtype(serde))]
28pub struct UriRef(str);
29
30/// Individual components of a URI reference.
31///
32/// Contains references to each component of a URI reference as defined
33/// in RFC 3986. Unlike [`UriParts`](super::UriParts), the scheme is optional
34/// since URI references may be relative.
35#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
36pub struct UriRefParts<'a> {
37	/// Scheme component, if present (e.g., `https`, `http`, `file`).
38	pub scheme: Option<&'a Scheme>,
39
40	/// Authority component, if present.
41	///
42	/// Contains the host and optionally userinfo and port
43	/// (e.g., `user@example.org:8080`).
44	pub authority: Option<&'a Authority>,
45
46	/// Path component.
47	///
48	/// May be empty, but is always present.
49	pub path: &'a Path,
50
51	/// Query component, if present.
52	pub query: Option<&'a Query>,
53
54	/// Fragment component, if present.
55	pub fragment: Option<&'a Fragment>,
56}
57
58impl Default for &UriRef {
59	fn default() -> Self {
60		<UriRef>::EMPTY
61	}
62}
63
64impl UriRef {
65	/// Empty URI reference.
66	pub const EMPTY: &'static Self = unsafe { Self::new_unchecked("") };
67
68	/// Returns all the parts of this URI reference.
69	///
70	/// This method parses the URI reference and returns a [`UriRefParts`]
71	/// struct containing references to each component: scheme, authority, path,
72	/// query, and fragment. Unlike [`Uri::parts`], the scheme is optional.
73	///
74	/// # Example
75	///
76	/// ```rust
77	/// use iref::UriRef;
78	///
79	/// let uri_ref = UriRef::new("//example.org/path?query#fragment").unwrap();
80	/// let parts = uri_ref.parts();
81	///
82	/// assert!(parts.scheme.is_none());
83	/// assert_eq!(parts.authority.unwrap(), "example.org");
84	/// assert_eq!(parts.path, "/path");
85	/// assert_eq!(parts.query.unwrap(), "query");
86	/// assert_eq!(parts.fragment.unwrap(), "fragment");
87	/// ```
88	pub fn parts(&self) -> UriRefParts<'_> {
89		let bytes = self.as_bytes();
90		let ranges = crate::common::parse::reference_parts(bytes, 0);
91
92		UriRefParts {
93			scheme: ranges
94				.scheme
95				.map(|r| unsafe { Scheme::new_unchecked_from_bytes(&bytes[r]) }),
96			authority: ranges
97				.authority
98				.map(|r| unsafe { Authority::new_unchecked(&self.0[r]) }),
99			path: unsafe { Path::new_unchecked(&self.0[ranges.path]) },
100			query: ranges
101				.query
102				.map(|r| unsafe { Query::new_unchecked(&self.0[r]) }),
103			fragment: ranges
104				.fragment
105				.map(|r| unsafe { Fragment::new_unchecked(&self.0[r]) }),
106		}
107	}
108
109	/// Returns the scheme of the URI reference, if any.
110	///
111	/// # Example
112	///
113	/// ```rust
114	/// use iref::UriRef;
115	///
116	/// let absolute = UriRef::new("https://example.org/path").unwrap();
117	/// assert_eq!(absolute.scheme().unwrap(), "https");
118	///
119	/// let relative = UriRef::new("/path").unwrap();
120	/// assert!(relative.scheme().is_none());
121	/// ```
122	#[inline]
123	pub fn scheme(&self) -> Option<&Scheme> {
124		let bytes = self.as_bytes();
125		crate::common::parse::find_scheme(bytes, 0)
126			.map(|range| unsafe { Scheme::new_unchecked_from_bytes(&bytes[range]) })
127	}
128
129	/// Adds the given scheme to the reference, returning a URI.
130	///
131	/// # Example
132	///
133	/// ```rust
134	/// use iref::{UriRef, Scheme};
135	///
136	/// let uri_ref = UriRef::new("//example.org/path").unwrap();
137	/// let uri = uri_ref.with_scheme(Scheme::new(b"https").unwrap());
138	///
139	/// assert_eq!(uri, "https://example.org/path");
140	/// ```
141	#[cfg(feature = "std")]
142	pub fn with_scheme(&self, scheme: &Scheme) -> UriBuf {
143		self.to_owned().into_with_scheme(scheme)
144	}
145
146	/// Returns a copy of this URI reference with the given authority.
147	///
148	/// # Example
149	///
150	/// ```rust
151	/// use iref::{UriRef, uri::Authority};
152	///
153	/// let uri_ref = UriRef::new("https://example.org/path").unwrap();
154	/// let new = uri_ref.with_authority(Some(Authority::new("other.com").unwrap()));
155	/// assert_eq!(new, "https://other.com/path");
156	/// ```
157	#[cfg(feature = "std")]
158	pub fn with_authority(&self, authority: Option<&Authority>) -> UriRefBuf {
159		let mut buf = self.to_owned();
160		buf.set_authority(authority);
161		buf
162	}
163
164	/// Returns a copy of this URI reference with the given path.
165	///
166	/// # Example
167	///
168	/// ```rust
169	/// use iref::{UriRef, uri::Path};
170	///
171	/// let uri_ref = UriRef::new("https://example.org/old").unwrap();
172	/// let new = uri_ref.with_path(Path::new("/new").unwrap());
173	/// assert_eq!(new, "https://example.org/new");
174	/// ```
175	#[cfg(feature = "std")]
176	pub fn with_path(&self, path: &Path) -> UriRefBuf {
177		let mut buf = self.to_owned();
178		buf.set_path(path);
179		buf
180	}
181
182	/// Returns a copy of this URI reference with the given query.
183	///
184	/// # Example
185	///
186	/// ```rust
187	/// use iref::{UriRef, uri::Query};
188	///
189	/// let uri_ref = UriRef::new("https://example.org/path").unwrap();
190	/// let new = uri_ref.with_query(Some(Query::new("key=value").unwrap()));
191	/// assert_eq!(new, "https://example.org/path?key=value");
192	/// ```
193	#[cfg(feature = "std")]
194	pub fn with_query(&self, query: Option<&Query>) -> UriRefBuf {
195		let mut buf = self.to_owned();
196		buf.set_query(query);
197		buf
198	}
199
200	/// Returns a copy of this URI reference with the given fragment.
201	///
202	/// # Example
203	///
204	/// ```rust
205	/// use iref::{UriRef, uri::Fragment};
206	///
207	/// let uri_ref = UriRef::new("https://example.org/path").unwrap();
208	/// let new = uri_ref.with_fragment(Some(Fragment::new("section").unwrap()));
209	/// assert_eq!(new, "https://example.org/path#section");
210	/// ```
211	#[cfg(feature = "std")]
212	pub fn with_fragment(&self, fragment: Option<&Fragment>) -> UriRefBuf {
213		let mut buf = self.to_owned();
214		buf.set_fragment(fragment);
215		buf
216	}
217
218	/// Returns the authority part of the URI reference, if any.
219	///
220	/// # Example
221	///
222	/// ```rust
223	/// use iref::UriRef;
224	///
225	/// let uri_ref = UriRef::new("https://user@example.org:8080/path").unwrap();
226	/// assert_eq!(uri_ref.authority().unwrap(), "user@example.org:8080");
227	///
228	/// let no_authority = UriRef::new("/path").unwrap();
229	/// assert!(no_authority.authority().is_none());
230	/// ```
231	pub fn authority(&self) -> Option<&Authority> {
232		let bytes = self.as_bytes();
233		crate::common::parse::find_authority(bytes, 0)
234			.ok()
235			.map(|range| unsafe { Authority::new_unchecked_from_bytes(&bytes[range]) })
236	}
237
238	/// Returns the host of the URI reference, if an authority is present.
239	///
240	/// # Example
241	///
242	/// ```rust
243	/// use iref::UriRef;
244	///
245	/// let uri_ref = UriRef::new("https://example.org:8080/path").unwrap();
246	/// assert_eq!(uri_ref.host().unwrap(), "example.org");
247	///
248	/// let no_authority = UriRef::new("/path").unwrap();
249	/// assert!(no_authority.host().is_none());
250	/// ```
251	pub fn host(&self) -> Option<&Host> {
252		self.authority().map(Authority::host)
253	}
254
255	/// Returns the user info of the URI reference, if present.
256	///
257	/// # Example
258	///
259	/// ```rust
260	/// use iref::UriRef;
261	///
262	/// let uri_ref = UriRef::new("https://user:pass@example.org/path").unwrap();
263	/// assert_eq!(uri_ref.user_info().unwrap(), "user:pass");
264	///
265	/// let no_userinfo = UriRef::new("https://example.org/path").unwrap();
266	/// assert!(no_userinfo.user_info().is_none());
267	/// ```
268	pub fn user_info(&self) -> Option<&UserInfo> {
269		self.authority().and_then(Authority::user_info)
270	}
271
272	/// Returns the port of the URI reference, if present.
273	///
274	/// # Example
275	///
276	/// ```rust
277	/// use iref::UriRef;
278	///
279	/// let uri_ref = UriRef::new("https://example.org:8080/path").unwrap();
280	/// assert_eq!(uri_ref.port().unwrap(), "8080");
281	///
282	/// let no_port = UriRef::new("https://example.org/path").unwrap();
283	/// assert!(no_port.port().is_none());
284	/// ```
285	pub fn port(&self) -> Option<&Port> {
286		self.authority().and_then(Authority::port)
287	}
288
289	/// Returns the path of the URI reference.
290	///
291	/// The path is always present, though it may be empty.
292	///
293	/// # Example
294	///
295	/// ```rust
296	/// use iref::UriRef;
297	///
298	/// let uri_ref = UriRef::new("https://example.org/foo/bar?query").unwrap();
299	/// assert_eq!(uri_ref.path(), "/foo/bar");
300	///
301	/// let empty_path = UriRef::new("https://example.org").unwrap();
302	/// assert_eq!(empty_path.path(), "");
303	/// ```
304	pub fn path(&self) -> &Path {
305		let bytes = self.as_bytes();
306		let range = crate::common::parse::find_path(bytes, 0);
307		unsafe { Path::new_unchecked_from_bytes(&bytes[range]) }
308	}
309
310	/// Returns the query component of the URI reference, if any.
311	pub fn query(&self) -> Option<&Query> {
312		let bytes = self.as_bytes();
313		crate::common::parse::find_query(bytes, 0)
314			.ok()
315			.map(|range| unsafe { Query::new_unchecked_from_bytes(&bytes[range]) })
316	}
317
318	/// Returns the fragment component of the URI reference, if any.
319	pub fn fragment(&self) -> Option<&Fragment> {
320		let bytes = self.as_bytes();
321		crate::common::parse::find_fragment(bytes, 0)
322			.ok()
323			.map(|range| unsafe { Fragment::new_unchecked_from_bytes(&bytes[range]) })
324	}
325
326	/// Returns this URI reference without the fragment component.
327	///
328	/// If the URI reference has a fragment (e.g. `#section`), it is removed
329	/// along with the `#` delimiter. If there is no fragment, the original
330	/// URI reference is returned unchanged.
331	///
332	/// # Example
333	///
334	/// ```rust
335	/// use iref::UriRef;
336	///
337	/// let uri_ref = UriRef::new("https://example.org/path?query#frag").unwrap();
338	/// assert_eq!(uri_ref.without_fragment(), "https://example.org/path?query");
339	///
340	/// let no_frag = UriRef::new("https://example.org/path").unwrap();
341	/// assert_eq!(no_frag.without_fragment(), "https://example.org/path");
342	/// ```
343	pub fn without_fragment(&self) -> &Self {
344		let bytes = self.as_bytes();
345		match crate::common::parse::find_fragment(bytes, 0) {
346			Ok(range) => unsafe { Self::new_unchecked_from_bytes(&bytes[..range.start - 1]) },
347			Err(_) => self,
348		}
349	}
350
351	/// Returns this URI reference without the query and fragment components.
352	///
353	/// If the URI reference has a query (e.g. `?key=value`) or fragment
354	/// (e.g. `#section`), they are removed along with their delimiters.
355	///
356	/// # Example
357	///
358	/// ```rust
359	/// use iref::UriRef;
360	///
361	/// let uri_ref = UriRef::new("https://example.org/path?query#frag").unwrap();
362	/// assert_eq!(
363	///     uri_ref.without_query_and_fragment(),
364	///     "https://example.org/path"
365	/// );
366	///
367	/// let no_query = UriRef::new("https://example.org/path#frag").unwrap();
368	/// assert_eq!(
369	///     no_query.without_query_and_fragment(),
370	///     "https://example.org/path"
371	/// );
372	/// ```
373	pub fn without_query_and_fragment(&self) -> &Self {
374		let bytes = self.as_bytes();
375		let end = match crate::common::parse::find_query(bytes, 0) {
376			Ok(range) => range.start - 1,
377			Err(i) => i,
378		};
379		unsafe { Self::new_unchecked_from_bytes(&bytes[..end]) }
380	}
381
382	/// Returns this URI reference relative to the given base.
383	///
384	/// Computes a relative URI reference that, when resolved against `other`,
385	/// would produce `self`. This is the inverse operation of resolution.
386	///
387	/// # Example
388	///
389	/// ```rust
390	/// use iref::UriRef;
391	///
392	/// let base = UriRef::new("https://example.org/foo/bar").unwrap();
393	/// let target = UriRef::new("https://example.org/foo/baz").unwrap();
394	///
395	/// assert_eq!(target.relative_to(base), "baz");
396	///
397	/// let other = UriRef::new("https://example.org/other").unwrap();
398	/// assert_eq!(other.relative_to(base), "../other");
399	/// ```
400	#[cfg(feature = "std")]
401	#[inline]
402	pub fn relative_to(&self, other: &Self) -> UriRefBuf {
403		let mut result = <UriRefBuf>::default();
404
405		// If self has a scheme/authority that differs from the base, the
406		// result must include it — return self as-is.
407		// If self lacks a scheme/authority, it inherits the base's through
408		// resolution, so we can continue.
409		match (self.scheme(), other.scheme()) {
410			(Some(a), Some(b)) if a == b => (),
411			(None, _) => (),
412			_ => return unsafe { <UriRefBuf>::new_unchecked(self.as_bytes().to_vec()) },
413		}
414
415		match (self.authority(), other.authority()) {
416			(Some(a), Some(b)) if a == b => (),
417			(None, None) => (),
418			_ => return unsafe { <UriRefBuf>::new_unchecked(self.as_bytes().to_vec()) },
419		}
420
421		let self_path = self.path();
422		let base_path = other.path();
423
424		let mut self_segments = self_path.normalized_segments().peekable();
425		let mut base_segments = base_path.parent_or_empty().normalized_segments().peekable();
426
427		if self_path.is_absolute() == base_path.is_absolute() {
428			loop {
429				match (self_segments.peek(), base_segments.peek()) {
430					(Some(a), Some(b)) if a == b => {
431						base_segments.next();
432						self_segments.next();
433					}
434					_ => break,
435				}
436			}
437		}
438
439		for _ in base_segments {
440			result.path_mut().lazy_push(Segment::PARENT);
441		}
442
443		if self_path.is_absolute() && base_path == Path::EMPTY_RELATIVE {
444			result.set_path(Path::EMPTY_ABSOLUTE);
445		}
446
447		for segment in self_segments {
448			result.path_mut().lazy_push(segment);
449		}
450
451		if (self.query().is_some() || self.fragment().is_some())
452			&& Some(result.path().as_bytes()) == other.path().last().map(|s| s.as_bytes())
453		{
454			result.path_mut().clear();
455		}
456
457		result.set_query(self.query());
458		result.set_fragment(self.fragment());
459
460		result
461	}
462
463	/// Returns the suffix of this URI relative to the given prefix.
464	///
465	/// Returns `Some((suffix, query, fragment))` if this URI is of the form
466	/// `prefix/suffix?query#fragment` where `prefix` is given as parameter.
467	/// Returns `None` otherwise.
468	/// If the scheme or authority differs from the prefix, returns `None`.
469	///
470	/// See [`Path::suffix`] for more details.
471	///
472	/// # Example
473	///
474	/// ```rust
475	/// use iref::UriRef;
476	///
477	/// let uri = UriRef::new("https://example.org/foo/bar/baz?query").unwrap();
478	/// let prefix = UriRef::new("https://example.org/foo").unwrap();
479	///
480	/// let (suffix, query, fragment) = uri.suffix(prefix).unwrap();
481	/// assert_eq!(suffix, "bar/baz");
482	/// assert_eq!(query.unwrap(), "query");
483	/// assert!(fragment.is_none());
484	/// ```
485	#[cfg(feature = "std")]
486	#[inline]
487	pub fn suffix(
488		&self,
489		prefix: impl AsRef<Self>,
490	) -> Option<(PathBuf, Option<&Query>, Option<&Fragment>)> {
491		let prefix = prefix.as_ref();
492		if self.scheme() == prefix.scheme() && self.authority() == prefix.authority() {
493			self.path()
494				.suffix(prefix.path())
495				.map(|suffix_path| (suffix_path, self.query(), self.fragment()))
496		} else {
497			None
498		}
499	}
500
501	/// The IRI reference without the file name, query and fragment.
502	///
503	/// # Example
504	/// ```
505	/// # use iref::IriRef;
506	/// let a = IriRef::new("https://crates.io/crates/iref?query#fragment").unwrap();
507	/// let b = IriRef::new("https://crates.io/crates/iref/?query#fragment").unwrap();
508	/// assert_eq!(a.base(), "https://crates.io/crates/");
509	/// assert_eq!(b.base(), "https://crates.io/crates/iref/")
510	/// ```
511	#[inline]
512	pub fn base(&self) -> &Self {
513		let bytes = self.as_bytes();
514		let path_range = crate::common::parse::find_path(bytes, 0);
515		let path_start = path_range.start;
516		let path = unsafe { Path::new_unchecked_from_bytes(&bytes[path_range]) };
517
518		let directory_path = path.directory();
519		let end = path_start + directory_path.len();
520		unsafe { Self::new_unchecked_from_bytes(&bytes[..end]) }
521	}
522
523	/// Resolves the URI reference against the given base URI.
524	///
525	/// See the [`UriRefBuf::resolve`] method for more information about the
526	/// resolution process.
527	#[cfg(feature = "std")]
528	#[inline]
529	pub fn resolved(&self, base_iri: impl AsRef<Uri>) -> UriBuf {
530		let iri_ref = self.to_owned();
531		iri_ref.into_resolved(base_iri)
532	}
533
534	/// Resolves the URI reference against the given base URI.
535	///
536	/// Same as [`Self::resolved`] but accepts a `&str` instead of an
537	/// URI. Returns an error if the input is not a valid URI.
538	#[cfg(feature = "std")]
539	pub fn try_resolved<'r>(
540		&self,
541		base_iri: &'r str,
542	) -> Result<UriBuf, <&'r Uri as TryFrom<&'r str>>::Error> {
543		Uri::new(base_iri).map(|u| self.resolved(u))
544	}
545
546	/// Returns this URI reference as a URI, if it has a scheme.
547	///
548	/// A URI reference with a scheme is a valid URI.
549	/// Returns `None` if this is a relative reference (no scheme).
550	///
551	/// # Example
552	///
553	/// ```rust
554	/// use iref::UriRef;
555	///
556	/// let absolute = UriRef::new("https://example.org/path").unwrap();
557	/// assert!(absolute.as_uri().is_some());
558	///
559	/// let relative = UriRef::new("/path").unwrap();
560	/// assert!(relative.as_uri().is_none());
561	/// ```
562	#[inline]
563	pub fn as_uri(&self) -> Option<&Uri> {
564		if self.scheme().is_some() {
565			Some(unsafe { Uri::new_unchecked(&self.0) })
566		} else {
567			None
568		}
569	}
570}
571
572/// Parses an [`UriRef`] at compile time.
573#[macro_export]
574macro_rules! uri_ref {
575	($value:literal) => {
576		match $crate::uri::UriRef::from_str($value) {
577			Ok(value) => value,
578			Err(_) => panic!("invalid URI reference"),
579		}
580	};
581}
582
583impl PartialEq for UriRef {
584	fn eq(&self, other: &Self) -> bool {
585		self.parts() == other.parts()
586	}
587}
588
589impl<'a> PartialEq<&'a UriRef> for UriRef {
590	fn eq(&self, other: &&'a Self) -> bool {
591		*self == **other
592	}
593}
594
595impl PartialEq<Uri> for UriRef {
596	fn eq(&self, other: &Uri) -> bool {
597		*self == *other.as_uri_ref()
598	}
599}
600
601impl<'a> PartialEq<&'a Uri> for UriRef {
602	fn eq(&self, other: &&'a Uri) -> bool {
603		*self == *other.as_uri_ref()
604	}
605}
606
607#[cfg(feature = "std")]
608impl PartialEq<UriBuf> for UriRef {
609	fn eq(&self, other: &UriBuf) -> bool {
610		*self == *other.as_uri_ref()
611	}
612}
613
614impl Eq for UriRef {}
615
616impl PartialOrd for UriRef {
617	fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
618		Some(self.cmp(other))
619	}
620}
621
622impl<'a> PartialOrd<&'a UriRef> for UriRef {
623	fn partial_cmp(&self, other: &&'a Self) -> Option<Ordering> {
624		self.partial_cmp(*other)
625	}
626}
627
628impl PartialOrd<Uri> for UriRef {
629	fn partial_cmp(&self, other: &Uri) -> Option<Ordering> {
630		self.partial_cmp(other.as_uri_ref())
631	}
632}
633
634impl<'a> PartialOrd<&'a Uri> for UriRef {
635	fn partial_cmp(&self, other: &&'a Uri) -> Option<Ordering> {
636		self.partial_cmp(other.as_uri_ref())
637	}
638}
639
640#[cfg(feature = "std")]
641impl PartialOrd<UriBuf> for UriRef {
642	fn partial_cmp(&self, other: &UriBuf) -> Option<Ordering> {
643		self.partial_cmp(other.as_uri_ref())
644	}
645}
646
647impl Ord for UriRef {
648	fn cmp(&self, other: &Self) -> Ordering {
649		self.parts().cmp(&other.parts())
650	}
651}
652
653impl Hash for UriRef {
654	fn hash<H: Hasher>(&self, state: &mut H) {
655		self.parts().hash(state)
656	}
657}
658
659impl<'a> From<&'a Uri> for &'a UriRef {
660	fn from(value: &'a Uri) -> Self {
661		value.as_uri_ref()
662	}
663}
664
665#[cfg(feature = "std")]
666impl UriRefBuf {
667	#[inline]
668	unsafe fn replace(&mut self, range: core::ops::Range<usize>, content: &[u8]) {
669		crate::utils::replace(unsafe { self.as_mut_vec() }, range, content)
670	}
671
672	#[inline]
673	unsafe fn allocate(&mut self, range: core::ops::Range<usize>, len: usize) {
674		crate::utils::allocate_range(unsafe { self.as_mut_vec() }, range, len)
675	}
676
677	/// Returns a mutable reference to the authority part, if present.
678	///
679	/// The returned [`AuthorityMut`] allows in-place modification of the
680	/// authority component (host, port, userinfo).
681	///
682	/// # Example
683	///
684	/// ```rust
685	/// use iref::UriRefBuf;
686	///
687	/// let mut uri_ref = UriRefBuf::new("//example.org:8080/path".to_string()).unwrap();
688	///
689	/// if let Some(mut authority) = uri_ref.authority_mut() {
690	///     authority.set_host("other.com".try_into().unwrap());
691	/// }
692	///
693	/// assert_eq!(uri_ref, "//other.com:8080/path");
694	/// ```
695	#[inline]
696	pub fn authority_mut(&mut self) -> Option<AuthorityMut<'_>> {
697		crate::common::parse::find_authority(self.as_bytes(), 0)
698			.ok()
699			.map(|range| unsafe { AuthorityMut::new_unchecked(self.as_mut_vec(), range) })
700	}
701
702	/// Sets the authority part.
703	///
704	/// If the path is relative, this also turns it into an absolute path,
705	/// since an authority cannot be followed by a relative path.
706	///
707	/// To avoid any ambiguity, if `authority` is `None` and the path starts
708	/// with `//`, it will be changed into `/.//` as to not be interpreted as
709	/// an authority part.
710	///
711	/// # Example
712	///
713	/// ```
714	/// use iref::{UriRefBuf, uri::Authority};
715	///
716	/// let mut a = UriRefBuf::new("scheme:/path".to_string()).unwrap();
717	/// a.set_authority(Some(Authority::new("example.org").unwrap()));
718	/// assert_eq!(a, "scheme://example.org/path");
719	///
720	/// // When an authority is added before a relative path,
721	/// // the path becomes absolute.
722	/// let mut b = UriRefBuf::new("scheme:path".to_string()).unwrap();
723	/// b.set_authority(Some(Authority::new("example.org").unwrap()));
724	/// assert_eq!(b, "scheme://example.org/path");
725	///
726	/// // When an authority is removed and the path starts with `//`,
727	/// // a `/.` prefix is added to the path to avoid any ambiguity.
728	/// let mut c = UriRefBuf::new("scheme://example.org//path".to_string()).unwrap();
729	/// c.set_authority(None);
730	/// assert_eq!(c, "scheme:/.//path");
731	/// ```
732	#[inline]
733	pub fn set_authority(&mut self, authority: Option<&Authority>) {
734		let bytes = self.as_bytes();
735		match authority {
736			Some(new_authority) => match crate::common::parse::find_authority(bytes, 0) {
737				Ok(range) => unsafe { self.replace(range, new_authority.as_bytes()) },
738				Err(start) => {
739					if !bytes[start..].starts_with(b"/") {
740						// VALIDITY: When an authority is present, the path must
741						//           be absolute.
742						unsafe {
743							self.allocate(start..start, new_authority.len() + 3);
744							let bytes = self.as_mut_vec();
745							let delim_end = start + 2;
746							bytes[start..delim_end].copy_from_slice(b"//");
747							bytes[delim_end..(delim_end + new_authority.len())]
748								.copy_from_slice(new_authority.as_bytes());
749							bytes[delim_end + new_authority.len()] = b'/';
750						}
751					} else {
752						unsafe {
753							self.allocate(start..start, new_authority.len() + 2);
754							let bytes = self.as_mut_vec();
755							let delim_end = start + 2;
756							bytes[start..delim_end].copy_from_slice(b"//");
757							bytes[delim_end..(delim_end + new_authority.len())]
758								.copy_from_slice(new_authority.as_bytes())
759						}
760					}
761				}
762			},
763			None => {
764				if let Ok(range) = crate::common::parse::find_authority(bytes, 0) {
765					let value: &[u8] = if bytes[range.end..].starts_with(b"//") {
766						// AMBIGUITY: The URI `http://example.com//foo` would
767						//            become `http://foo`, but `//foo` is not
768						//            the authority.
769						// SOLUTION:  We change `//foo` to `/.//foo`.
770						b"/."
771					} else {
772						b""
773					};
774
775					unsafe {
776						self.replace((range.start - 2)..range.end, value);
777					}
778				}
779			}
780		}
781	}
782
783	/// Tries to set the authority.
784	///
785	/// Same as [`Self::set_authority`] but accepts a `&str` instead of
786	/// an [`&Authority`](Authority).
787	///
788	/// # Example
789	///
790	/// ```rust
791	/// use iref::{UriRefBuf, uri::Authority};
792	///
793	/// let mut uri_ref = UriRefBuf::new("/path".to_string()).unwrap();
794	///
795	/// // Set authority
796	/// uri_ref.try_set_authority(Some("example.org")).unwrap();
797	/// assert_eq!(uri_ref, "//example.org/path");
798	///
799	/// // Remove authority
800	/// uri_ref.try_set_authority(None).unwrap();
801	/// assert_eq!(uri_ref, "/path");
802	/// ```
803	pub fn try_set_authority<'s>(
804		&mut self,
805		authority: Option<&'s str>,
806	) -> Result<(), InvalidAuthority<&'s str>> {
807		self.set_authority(authority.map(TryInto::try_into).transpose()?);
808		Ok(())
809	}
810
811	/// Returns a mutable reference to the path part.
812	///
813	/// The returned [`PathMut`] allows in-place modification of the path,
814	/// including appending segments, normalizing, and replacing the entire path.
815	///
816	/// # Example
817	///
818	/// ```rust
819	/// use iref::{UriRefBuf, uri::Segment};
820	///
821	/// let mut uri_ref = UriRefBuf::new("/foo/../bar?query".to_string()).unwrap();
822	/// uri_ref.path_mut()
823	///     .normalize()
824	///     .try_push("baz")
825	///     .unwrap();
826	/// assert_eq!(uri_ref, "/bar/baz?query");
827	/// ```
828	#[inline]
829	pub fn path_mut(&mut self) -> PathMut<'_> {
830		let range = crate::common::parse::find_path(self.as_bytes(), 0);
831		unsafe { PathMut::new_unchecked(self.as_mut_vec(), range) }
832	}
833
834	/// Sets the path part.
835	///
836	/// If there is an authority and the path is relative, this also turns it
837	/// into an absolute path, since an authority cannot be followed by a
838	/// relative path.
839	///
840	/// To avoid any ambiguity, if there is no authority and the path starts
841	/// with `//`, it will be changed into `/.//` as to not be interpreted as
842	/// an authority part. Similarly if there is no scheme nor authority and the
843	/// beginning of the new path looks like a scheme, it is prefixed with `./`
844	/// to not be confused with a scheme.
845	///
846	/// # Example
847	///
848	/// ```
849	/// use iref::{UriRefBuf, uri::Path};
850	///
851	/// let mut a = UriRefBuf::new("http://example.org/old/path".to_string()).unwrap();
852	/// a.set_path(Path::new("/foo/bar").unwrap());
853	/// assert_eq!(a, "http://example.org/foo/bar");
854	///
855	/// // If there is an authority and the new path is relative,
856	/// // it is turned into an absolute path.
857	/// let mut b = UriRefBuf::new("http://example.org/old/path".to_string()).unwrap();
858	/// b.set_path(Path::new("relative/path").unwrap());
859	/// assert_eq!(b, "http://example.org/relative/path");
860	///
861	/// // If there is no authority and the path starts with `//`,
862	/// // it is prefixed with `/.` to avoid being confused with an authority.
863	/// let mut c = UriRefBuf::new("http:old/path".to_string()).unwrap();
864	/// c.set_path(Path::new("//foo/bar").unwrap());
865	/// assert_eq!(c, "http:/.//foo/bar");
866	///
867	/// // If there is no authority nor scheme, and the path beginning looks
868	/// // like a scheme, it is prefixed with `./` to avoid being confused with
869	/// // a scheme.
870	/// let mut d = UriRefBuf::new("old/path".to_string()).unwrap();
871	/// d.set_path(Path::new("foo:bar").unwrap());
872	/// assert_eq!(d, "./foo:bar");
873	/// ```
874	#[inline]
875	pub fn set_path(&mut self, path: &Path) {
876		self.path_mut().replace(path);
877	}
878
879	/// Tries to set the path.
880	///
881	/// Same as [`Self::set_path`] but accepts a `&str` instead of
882	/// an [`&Path`](Path).
883	pub fn try_set_path<'s>(&mut self, path: &'s str) -> Result<(), InvalidPath<&'s str>> {
884		self.set_path(path.try_into()?);
885		Ok(())
886	}
887
888	/// Sets and normalizes the path.
889	pub fn set_and_normalize_path(&mut self, path: &Path) {
890		self.set_path(path);
891		self.path_mut().normalize();
892	}
893
894	/// Sets the query part.
895	///
896	/// If `query` is `Some`, the query component is set to the given value.
897	/// If `query` is `None`, the query component is removed entirely.
898	///
899	/// # Example
900	///
901	/// ```rust
902	/// use iref::{UriRefBuf, uri::Query};
903	///
904	/// let mut uri_ref = UriRefBuf::new("/path".to_string()).unwrap();
905	///
906	/// uri_ref.set_query(Some(Query::new("key=value").unwrap()));
907	/// assert_eq!(uri_ref, "/path?key=value");
908	///
909	/// uri_ref.set_query(None);
910	/// assert_eq!(uri_ref, "/path");
911	/// ```
912	#[inline]
913	pub fn set_query(&mut self, query: Option<&Query>) {
914		match query {
915			Some(new_query) => match crate::common::parse::find_query(self.as_bytes(), 0) {
916				Ok(range) => unsafe { self.replace(range, new_query.as_bytes()) },
917				Err(start) => unsafe {
918					self.allocate(start..start, new_query.len() + 1);
919					let bytes = self.as_mut_vec();
920					let delim_end = start + 1;
921					bytes[start] = b'?';
922					bytes[delim_end..(delim_end + new_query.len())]
923						.copy_from_slice(new_query.as_bytes())
924				},
925			},
926			None => {
927				if let Ok(range) = crate::common::parse::find_query(self.as_bytes(), 0) {
928					unsafe {
929						self.replace((range.start - 1)..range.end, b"");
930					}
931				}
932			}
933		}
934	}
935
936	/// Tries to set the query part.
937	///
938	/// Same as [`Self::set_query`] but accepts a `&str` instead of
939	/// an [`&Query`](Query).
940	pub fn try_set_query<'s>(
941		&mut self,
942		query: Option<&'s str>,
943	) -> Result<(), InvalidQuery<&'s str>> {
944		self.set_query(query.map(TryInto::try_into).transpose()?);
945		Ok(())
946	}
947
948	/// Sets the fragment part.
949	///
950	/// If `fragment` is `Some`, the fragment component is set to the given value.
951	/// If `fragment` is `None`, the fragment component is removed entirely.
952	///
953	/// # Example
954	///
955	/// ```rust
956	/// use iref::{UriRefBuf, uri::Fragment};
957	///
958	/// let mut uri_ref = UriRefBuf::new("/path".to_string()).unwrap();
959	///
960	/// uri_ref.set_fragment(Some(Fragment::new("section").unwrap()));
961	/// assert_eq!(uri_ref, "/path#section");
962	///
963	/// uri_ref.set_fragment(None);
964	/// assert_eq!(uri_ref, "/path");
965	/// ```
966	#[inline]
967	pub fn set_fragment(&mut self, fragment: Option<&Fragment>) {
968		match fragment {
969			Some(new_fragment) => match crate::common::parse::find_fragment(self.as_bytes(), 0) {
970				Ok(range) => unsafe { self.replace(range, new_fragment.as_bytes()) },
971				Err(start) => unsafe {
972					self.allocate(start..start, new_fragment.len() + 1);
973					let bytes = self.as_mut_vec();
974					let delim_end = start + 1;
975					bytes[start] = b'#';
976					bytes[delim_end..(delim_end + new_fragment.len())]
977						.copy_from_slice(new_fragment.as_bytes())
978				},
979			},
980			None => {
981				if let Ok(range) = crate::common::parse::find_fragment(self.as_bytes(), 0) {
982					unsafe {
983						self.replace((range.start - 1)..range.end, b"");
984					}
985				}
986			}
987		}
988	}
989
990	/// Tries to set the fragment part.
991	///
992	/// Same as [`Self::set_fragment`] but accepts a `&str` instead of
993	/// an [`&Fragment`](Fragment).
994	pub fn try_set_fragment<'s>(
995		&mut self,
996		fragment: Option<&'s str>,
997	) -> Result<(), InvalidFragment<&'s str>> {
998		self.set_fragment(fragment.map(TryInto::try_into).transpose()?);
999		Ok(())
1000	}
1001
1002	/// Sets the scheme part.
1003	///
1004	/// If there is no authority and the start of the path looks like a scheme
1005	/// (e.g. `foo:`) then the path is prefixed with `./` to avoid being
1006	/// confused with a scheme.
1007	///
1008	/// # Example
1009	///
1010	/// ```
1011	/// use iref::{UriRefBuf, uri::Scheme};
1012	///
1013	/// let mut a = UriRefBuf::new("foo/bar".to_string()).unwrap();
1014	/// a.set_scheme(Some(Scheme::new(b"http").unwrap()));
1015	/// assert_eq!(a, "http:foo/bar");
1016	///
1017	/// let mut b = UriRefBuf::new("scheme://example.org/foo/bar".to_string()).unwrap();
1018	/// b.set_scheme(None);
1019	/// assert_eq!(b, "//example.org/foo/bar");
1020	///
1021	/// let mut c = UriRefBuf::new("scheme:foo:bar".to_string()).unwrap();
1022	/// c.set_scheme(None);
1023	/// assert_eq!(c, "./foo:bar");
1024	/// ```
1025	#[inline]
1026	pub fn set_scheme(&mut self, scheme: Option<&Scheme>) {
1027		match scheme {
1028			Some(new_scheme) => match crate::common::parse::find_scheme(self.as_bytes(), 0) {
1029				Some(scheme_range) => unsafe {
1030					self.replace(scheme_range, new_scheme.as_bytes());
1031				},
1032				None => unsafe {
1033					self.allocate(0..0, new_scheme.len() + 1);
1034					let bytes = self.as_mut_vec();
1035					bytes[0..new_scheme.len()].copy_from_slice(new_scheme.as_bytes());
1036					bytes[new_scheme.len()] = b':'
1037				},
1038			},
1039			None => {
1040				if let Some(scheme_range) = crate::common::parse::find_scheme(self.as_bytes(), 0) {
1041					let value: &[u8] =
1042						if self.authority().is_none() && self.path().looks_like_scheme() {
1043							// AMBIGUITY: The URI `http:foo:bar` would become
1044							//            `foo:bar`, but `foo` is not the scheme.
1045							// SOLUTION:  We change `foo:bar` to `./foo:bar`.
1046							b"./"
1047						} else {
1048							b""
1049						};
1050
1051					unsafe { self.replace(scheme_range.start..(scheme_range.end + 1), value) }
1052				}
1053			}
1054		}
1055	}
1056
1057	/// Tries to set the scheme part.
1058	///
1059	/// Same [`Self::set_scheme`] but accepts an `&str` instead of a
1060	/// [`&Scheme`](Scheme). Returns an error if the input string is not
1061	/// a valid scheme.
1062	pub fn try_set_scheme<'s>(
1063		&mut self,
1064		scheme: Option<&'s str>,
1065	) -> Result<(), super::InvalidScheme<&'s str>> {
1066		self.set_scheme(scheme.map(TryInto::try_into).transpose()?);
1067		Ok(())
1068	}
1069
1070	/// Adds the given scheme to the reference, turning it into an URI.
1071	pub fn into_with_scheme(mut self, scheme: &Scheme) -> UriBuf {
1072		self.set_scheme(Some(scheme));
1073		unsafe { UriBuf::new_unchecked(self.0) }
1074	}
1075
1076	/// Tries to add the given scheme to the reference, turning it into
1077	/// an URI.
1078	///
1079	/// Same [`Self::into_with_scheme`] but accepts an `&str` instead of
1080	/// a [`&Scheme`](Scheme). Returns an error if the input string is
1081	/// not a valid scheme.
1082	pub fn try_into_with_scheme(
1083		mut self,
1084		scheme: &str,
1085	) -> Result<UriBuf, (Self, super::InvalidScheme<&str>)> {
1086		match self.try_set_scheme(Some(scheme)) {
1087			Ok(_) => Ok(unsafe { UriBuf::new_unchecked(self.0) }),
1088			Err(e) => Err((self, e)),
1089		}
1090	}
1091
1092	/// Resolves this URI reference against the given base URI in place.
1093	///
1094	/// This transforms a relative URI reference into an absolute URI by
1095	/// resolving it against the provided base URI, following RFC 3986.
1096	/// This is similar to [`UriBuf::join`], but with the subject and object
1097	/// swapped.
1098	///
1099	/// # Example
1100	///
1101	/// ```rust
1102	/// use iref::{UriRefBuf, Uri};
1103	///
1104	/// let base = Uri::new("https://example.org/foo/bar").unwrap();
1105	/// let mut uri_ref = UriRefBuf::new("../baz?query".to_string()).unwrap();
1106	/// uri_ref.resolve(base);
1107	///
1108	/// assert_eq!(uri_ref, "https://example.org/baz?query");
1109	/// ```
1110	///
1111	/// ## Abnormal use of dot segments
1112	///
1113	/// See <https://www.rfc-editor.org/errata/eid4547>
1114	pub fn resolve(&mut self, base_iri: impl AsRef<Uri>) {
1115		let base_iri = base_iri.as_ref();
1116		let parts = crate::common::parse::reference_parts(self.as_bytes(), 0);
1117
1118		if parts.scheme.is_some() {
1119			self.path_mut().normalize();
1120		} else {
1121			self.set_scheme(Some(base_iri.scheme()));
1122			if parts.authority.is_some() {
1123				self.path_mut().normalize();
1124			} else if self.path().is_relative() && self.path().is_empty() {
1125				self.set_authority(base_iri.authority());
1126				self.set_path(base_iri.path());
1127				if self.query().is_none() {
1128					self.set_query(base_iri.query());
1129				}
1130			} else if self.path().is_absolute() {
1131				self.set_authority(base_iri.authority());
1132				self.path_mut().normalize();
1133			} else {
1134				self.set_authority(base_iri.authority());
1135
1136				let base_path = base_iri.path();
1137				let mut path = if base_path.is_empty() && base_iri.authority().is_some() {
1138					Path::EMPTY_ABSOLUTE.to_owned()
1139				} else {
1140					base_path.parent_or_empty().to_owned()
1141				};
1142
1143				let mut path_mut = PathMut::from_path_with_context(
1144					&mut path,
1145					PathContext {
1146						// Set the context manually to prevent disambiguation logic.
1147						has_scheme: false,
1148						has_authority: false,
1149					},
1150				);
1151
1152				for s in self.path().segments() {
1153					path_mut.lazy_push(s);
1154				}
1155
1156				path_mut.normalize();
1157
1158				self.set_path(&path);
1159			}
1160		}
1161	}
1162
1163	/// Tries to resolve this URI reference against the given base URI string.
1164	///
1165	/// Same as [`Self::resolve`] but accepts a `&str` instead of a [`&Uri`](Uri).
1166	/// Returns an error if the base string is not a valid URI.
1167	pub fn try_resolve<'r>(
1168		&mut self,
1169		base_iri: &'r str,
1170	) -> Result<(), <&'r Uri as TryFrom<&'r str>>::Error> {
1171		self.resolve(Uri::new(base_iri)?);
1172		Ok(())
1173	}
1174
1175	/// Resolves this URI reference against the given base URI, consuming self.
1176	///
1177	/// Returns the resolved URI as an owned [`UriBuf`].
1178	///
1179	/// # Example
1180	///
1181	/// ```rust
1182	/// use iref::{UriRefBuf, Uri};
1183	///
1184	/// let base = Uri::new("https://example.org/foo/bar").unwrap();
1185	/// let uri_ref = UriRefBuf::new("../baz".to_string()).unwrap();
1186	/// let resolved = uri_ref.into_resolved(base);
1187	///
1188	/// assert_eq!(resolved, "https://example.org/baz");
1189	/// ```
1190	pub fn into_resolved(mut self, base_iri: impl AsRef<Uri>) -> UriBuf {
1191		self.resolve(base_iri);
1192		unsafe { <UriBuf>::new_unchecked(self.into_bytes()) }
1193	}
1194
1195	/// Tries to resolve this URI reference, consuming self.
1196	///
1197	/// Same as [`Self::into_resolved`] but accepts a `&str` instead of a [`&Uri`](Uri).
1198	/// Returns an error if the base string is not a valid URI.
1199	pub fn try_into_resolved(
1200		mut self,
1201		base_iri: &str,
1202	) -> Result<UriBuf, (Self, <&Uri as TryFrom<&str>>::Error)> {
1203		match self.try_resolve(base_iri) {
1204			Ok(_) => Ok(unsafe { <UriBuf>::new_unchecked(self.into_bytes()) }),
1205			Err(e) => Err((self, e)),
1206		}
1207	}
1208
1209	/// Returns a mutable reference to the underlying `Vec<u8>` buffer
1210	/// representing the URI reference.
1211	///
1212	/// # Safety
1213	///
1214	/// The caller must ensure that once the mutable reference is dropped, its
1215	/// content is still a valid URI reference.
1216	pub unsafe fn as_mut_vec(&mut self) -> &mut Vec<u8> {
1217		unsafe { self.0.as_mut_vec() }
1218	}
1219
1220	/// Tries to convert this URI reference into a URI.
1221	///
1222	/// Succeeds if this URI reference has a scheme (making it a valid URI).
1223	/// Returns an error containing `self` if it's a relative reference.
1224	///
1225	/// # Example
1226	///
1227	/// ```rust
1228	/// use iref::UriRefBuf;
1229	///
1230	/// let absolute = UriRefBuf::new("https://example.org/path".to_string()).unwrap();
1231	/// assert!(absolute.try_into_uri().is_ok());
1232	///
1233	/// let relative = UriRefBuf::new("/path".to_string()).unwrap();
1234	/// assert!(relative.try_into_uri().is_err());
1235	/// ```
1236	pub fn try_into_uri(self) -> Result<UriBuf, InvalidUri<Self>> {
1237		if self.scheme().is_some() {
1238			unsafe { Ok(UriBuf::new_unchecked(self.0)) }
1239		} else {
1240			Err(InvalidUri(self))
1241		}
1242	}
1243}
1244
1245#[cfg(feature = "std")]
1246impl TryFrom<UriRefBuf> for UriBuf {
1247	type Error = InvalidUri<UriRefBuf>;
1248
1249	fn try_from(value: UriRefBuf) -> Result<Self, Self::Error> {
1250		value.try_into_uri()
1251	}
1252}
1253
1254#[cfg(feature = "std")]
1255impl PartialEq<Uri> for UriRefBuf {
1256	fn eq(&self, other: &Uri) -> bool {
1257		*self.as_uri_ref() == *other.as_uri_ref()
1258	}
1259}
1260
1261#[cfg(feature = "std")]
1262impl<'a> PartialEq<&'a Uri> for UriRefBuf {
1263	fn eq(&self, other: &&'a Uri) -> bool {
1264		*self.as_uri_ref() == *other.as_uri_ref()
1265	}
1266}
1267
1268#[cfg(feature = "std")]
1269impl PartialEq<UriBuf> for UriRefBuf {
1270	fn eq(&self, other: &UriBuf) -> bool {
1271		*self.as_uri_ref() == *other.as_uri_ref()
1272	}
1273}
1274
1275#[cfg(feature = "std")]
1276impl PartialOrd<Uri> for UriRefBuf {
1277	fn partial_cmp(&self, other: &Uri) -> Option<Ordering> {
1278		self.as_uri_ref().partial_cmp(other.as_uri_ref())
1279	}
1280}
1281
1282#[cfg(feature = "std")]
1283impl<'a> PartialOrd<&'a Uri> for UriRefBuf {
1284	fn partial_cmp(&self, other: &&'a Uri) -> Option<Ordering> {
1285		self.as_uri_ref().partial_cmp(other.as_uri_ref())
1286	}
1287}
1288
1289#[cfg(feature = "std")]
1290impl PartialOrd<UriBuf> for UriRefBuf {
1291	fn partial_cmp(&self, other: &UriBuf) -> Option<Ordering> {
1292		self.as_uri_ref().partial_cmp(other.as_uri_ref())
1293	}
1294}
1295
1296#[cfg(test)]
1297mod tests {
1298	use super::*;
1299
1300	const PARTS: [(
1301		&[u8],
1302		(
1303			Option<&[u8]>,
1304			Option<&[u8]>,
1305			&[u8],
1306			Option<&[u8]>,
1307			Option<&[u8]>,
1308		),
1309	); 38] = [
1310		// 0 components.
1311		(b"", (None, None, b"", None, None)),
1312		(b"a/:", (None, None, b"a/:", None, None)),
1313		// 1 component.
1314		(b"scheme:", (Some(b"scheme"), None, b"", None, None)),
1315		(b"//authority", (None, Some(b"authority"), b"", None, None)),
1316		(b"path", (None, None, b"path", None, None)),
1317		(b"/path", (None, None, b"/path", None, None)),
1318		(b"/", (None, None, b"/", None, None)),
1319		(b"foo//bar", (None, None, b"foo//bar", None, None)),
1320		(b"?query", (None, None, b"", Some(b"query"), None)),
1321		(b"#fragment", (None, None, b"", None, Some(b"fragment"))),
1322		(
1323			b"scheme:?query",
1324			(Some(b"scheme"), None, b"", Some(b"query"), None),
1325		),
1326		(b"//[::]", (None, Some(b"[::]"), b"", None, None)),
1327		// 2 components.
1328		(
1329			b"scheme://authority",
1330			(Some(b"scheme"), Some(b"authority"), b"", None, None),
1331		),
1332		(b"scheme:path", (Some(b"scheme"), None, b"path", None, None)),
1333		(
1334			b"scheme:/path",
1335			(Some(b"scheme"), None, b"/path", None, None),
1336		),
1337		(
1338			b"scheme:?query",
1339			(Some(b"scheme"), None, b"", Some(b"query"), None),
1340		),
1341		(
1342			b"scheme:#fragment",
1343			(Some(b"scheme"), None, b"", None, Some(b"fragment")),
1344		),
1345		(
1346			b"//authority/path",
1347			(None, Some(b"authority"), b"/path", None, None),
1348		),
1349		(
1350			b"//authority?query",
1351			(None, Some(b"authority"), b"", Some(b"query"), None),
1352		),
1353		(
1354			b"//authority#fragment",
1355			(None, Some(b"authority"), b"", None, Some(b"fragment")),
1356		),
1357		(b"path?query", (None, None, b"path", Some(b"query"), None)),
1358		(b"/path?query", (None, None, b"/path", Some(b"query"), None)),
1359		(
1360			b"path#fragment",
1361			(None, None, b"path", None, Some(b"fragment")),
1362		),
1363		(
1364			b"?query#fragment",
1365			(None, None, b"", Some(b"query"), Some(b"fragment")),
1366		),
1367		// 3 components
1368		(
1369			b"scheme://authority/path",
1370			(Some(b"scheme"), Some(b"authority"), b"/path", None, None),
1371		),
1372		(
1373			b"scheme://authority?query",
1374			(
1375				Some(b"scheme"),
1376				Some(b"authority"),
1377				b"",
1378				Some(b"query"),
1379				None,
1380			),
1381		),
1382		(
1383			b"scheme://authority#fragment",
1384			(
1385				Some(b"scheme"),
1386				Some(b"authority"),
1387				b"",
1388				None,
1389				Some(b"fragment"),
1390			),
1391		),
1392		(
1393			b"scheme:path?query",
1394			(Some(b"scheme"), None, b"path", Some(b"query"), None),
1395		),
1396		(
1397			b"scheme:path#fragment",
1398			(Some(b"scheme"), None, b"path", None, Some(b"fragment")),
1399		),
1400		(
1401			b"//authority/path?query",
1402			(None, Some(b"authority"), b"/path", Some(b"query"), None),
1403		),
1404		(
1405			b"//authority/path#fragment",
1406			(None, Some(b"authority"), b"/path", None, Some(b"fragment")),
1407		),
1408		(
1409			b"//authority?query#fragment",
1410			(
1411				None,
1412				Some(b"authority"),
1413				b"",
1414				Some(b"query"),
1415				Some(b"fragment"),
1416			),
1417		),
1418		(
1419			b"path?query#fragment",
1420			(None, None, b"path", Some(b"query"), Some(b"fragment")),
1421		),
1422		// 4 components
1423		(
1424			b"scheme://authority/path?query",
1425			(
1426				Some(b"scheme"),
1427				Some(b"authority"),
1428				b"/path",
1429				Some(b"query"),
1430				None,
1431			),
1432		),
1433		(
1434			b"scheme://authority/path#fragment",
1435			(
1436				Some(b"scheme"),
1437				Some(b"authority"),
1438				b"/path",
1439				None,
1440				Some(b"fragment"),
1441			),
1442		),
1443		(
1444			b"scheme://authority?query#fragment",
1445			(
1446				Some(b"scheme"),
1447				Some(b"authority"),
1448				b"",
1449				Some(b"query"),
1450				Some(b"fragment"),
1451			),
1452		),
1453		(
1454			b"scheme:path?query#fragment",
1455			(
1456				Some(b"scheme"),
1457				None,
1458				b"path",
1459				Some(b"query"),
1460				Some(b"fragment"),
1461			),
1462		),
1463		// 5 components
1464		(
1465			b"scheme://authority/path?query#fragment",
1466			(
1467				Some(b"scheme"),
1468				Some(b"authority"),
1469				b"/path",
1470				Some(b"query"),
1471				Some(b"fragment"),
1472			),
1473		),
1474	];
1475
1476	#[test]
1477	fn parts() {
1478		for (input, expected) in PARTS {
1479			let input = UriRef::new(input).unwrap();
1480			let parts = input.parts();
1481
1482			assert_eq!(parts.scheme.map(Scheme::as_bytes), expected.0);
1483			assert_eq!(parts.authority.map(Authority::as_bytes), expected.1);
1484			assert_eq!(parts.path.as_bytes(), expected.2);
1485			assert_eq!(parts.query.map(Query::as_bytes), expected.3);
1486			assert_eq!(parts.fragment.map(Fragment::as_bytes), expected.4)
1487		}
1488	}
1489
1490	#[test]
1491	fn scheme() {
1492		for (input, expected) in PARTS {
1493			let input = UriRef::new(input).unwrap();
1494			assert_eq!(input.scheme().map(Scheme::as_bytes), expected.0)
1495		}
1496	}
1497
1498	#[test]
1499	fn authority() {
1500		for (input, expected) in PARTS {
1501			let input = UriRef::new(input).unwrap();
1502			// eprintln!("{input}: {expected:?}");
1503			assert_eq!(input.authority().map(Authority::as_bytes), expected.1)
1504		}
1505	}
1506
1507	#[test]
1508	fn authority_port() {
1509		let vectors: &[(&str, Option<&str>)] = &[("//[::]", None)];
1510
1511		for (input, expected) in vectors {
1512			let uri = UriRef::new(input).unwrap();
1513			assert_eq!(
1514				uri.authority().and_then(|a| a.port()).map(|p| p.as_str()),
1515				*expected,
1516				"input: {input}"
1517			);
1518		}
1519	}
1520
1521	#[test]
1522	fn set_authority() {
1523		let vectors: [(&[u8], Option<&[u8]>, &[u8]); 3] = [
1524			(
1525				b"scheme:/path",
1526				Some(b"authority"),
1527				b"scheme://authority/path",
1528			),
1529			(
1530				b"scheme:path",
1531				Some(b"authority"),
1532				b"scheme://authority/path",
1533			),
1534			(b"scheme://authority//path", None, b"scheme:/.//path"),
1535		];
1536
1537		for (input, authority, expected) in vectors {
1538			let mut buffer = UriRefBuf::new(input.to_vec()).unwrap();
1539			let authority = authority.map(Authority::new).transpose().unwrap();
1540			buffer.set_authority(authority);
1541			// eprintln!("{input:?}, {authority:?} => {buffer}, {expected:?}");
1542			assert_eq!(buffer.as_bytes(), expected)
1543		}
1544	}
1545
1546	#[test]
1547	fn path() {
1548		for (input, expected) in PARTS {
1549			let input = UriRef::new(input).unwrap();
1550			// eprintln!("{input}: {expected:?}");
1551			assert_eq!(input.path().as_bytes(), expected.2)
1552		}
1553	}
1554
1555	#[test]
1556	fn query() {
1557		for (input, expected) in PARTS {
1558			let input = UriRef::new(input).unwrap();
1559			// eprintln!("{input}: {expected:?}");
1560			assert_eq!(input.query().map(Query::as_bytes), expected.3)
1561		}
1562	}
1563
1564	#[test]
1565	fn fragment() {
1566		for (input, expected) in PARTS {
1567			let input = UriRef::new(input).unwrap();
1568			// eprintln!("{input}: {expected:?}");
1569			assert_eq!(input.fragment().map(Fragment::as_bytes), expected.4)
1570		}
1571	}
1572
1573	#[test]
1574	fn disambiguate_scheme() {
1575		let mut uri_ref = UriRefBuf::new("scheme:a:b/c".to_string()).unwrap();
1576		uri_ref.set_scheme(None);
1577		assert_eq!(uri_ref.as_str(), "./a:b/c")
1578	}
1579
1580	#[test]
1581	fn disambiguate_authority() {
1582		let mut uri_ref = UriRefBuf::new("//host//path".to_string()).unwrap();
1583		uri_ref.set_authority(None);
1584		assert_eq!(uri_ref.as_str(), "/.//path")
1585	}
1586
1587	fn test_resolution<'a>(base: &str, vectors: impl IntoIterator<Item = (&'a str, &'a str)>) {
1588		let base_uri = Uri::new(base).unwrap();
1589		for (reference, expected) in vectors {
1590			let uri_ref = UriRef::new(reference).unwrap();
1591			let expected_uri = Uri::new(expected).unwrap();
1592
1593			assert_eq!(
1594				uri_ref.resolved(base_uri),
1595				expected_uri,
1596				"({uri_ref}).resolved({base_uri})",
1597			);
1598			assert_eq!(
1599				base_uri.joined(uri_ref),
1600				expected_uri,
1601				"({base_uri}).joined({uri_ref})",
1602			);
1603		}
1604	}
1605
1606	/// RFC 3986 Section 5.4 — Normal examples.
1607	/// https://www.w3.org/2004/04/uri-rel-test.html
1608	#[test]
1609	fn resolution_normal() {
1610		test_resolution(
1611			"http://a/b/c/d;p?q",
1612			[
1613				("g:h", "g:h"),
1614				("g", "http://a/b/c/g"),
1615				("./g", "http://a/b/c/g"),
1616				("g/", "http://a/b/c/g/"),
1617				("/g", "http://a/g"),
1618				("//g", "http://g"),
1619				("?y", "http://a/b/c/d;p?y"),
1620				("g?y", "http://a/b/c/g?y"),
1621				("#s", "http://a/b/c/d;p?q#s"),
1622				("g#s", "http://a/b/c/g#s"),
1623				("g?y#s", "http://a/b/c/g?y#s"),
1624				(";x", "http://a/b/c/;x"),
1625				("g;x", "http://a/b/c/g;x"),
1626				("g;x?y#s", "http://a/b/c/g;x?y#s"),
1627				("", "http://a/b/c/d;p?q"),
1628				(".", "http://a/b/c/"),
1629				("./", "http://a/b/c/"),
1630				("..", "http://a/b/"),
1631				("../", "http://a/b/"),
1632				("../g", "http://a/b/g"),
1633				("../..", "http://a/"),
1634				("../../", "http://a/"),
1635				("../../g", "http://a/g"),
1636			],
1637		);
1638	}
1639
1640	/// RFC 3986 Section 5.4 — Abnormal examples.
1641	/// NOTE we implement [Errata 4547](https://www.rfc-editor.org/errata/eid4547).
1642	#[test]
1643	fn resolution_abnormal() {
1644		test_resolution(
1645			"http://a/b/c/d;p?q",
1646			[
1647				("../../../g", "http://a/g"),
1648				("../../../../g", "http://a/g"),
1649				("/./g", "http://a/g"),
1650				("/../g", "http://a/g"),
1651				("g.", "http://a/b/c/g."),
1652				(".g", "http://a/b/c/.g"),
1653				("g..", "http://a/b/c/g.."),
1654				("..g", "http://a/b/c/..g"),
1655				("./../g", "http://a/b/g"),
1656				("./g/.", "http://a/b/c/g/"),
1657				("g/./h", "http://a/b/c/g/h"),
1658				("g/../h", "http://a/b/c/h"),
1659				("g;x=1/./y", "http://a/b/c/g;x=1/y"),
1660				("g;x=1/../y", "http://a/b/c/y"),
1661				("g?y/./x", "http://a/b/c/g?y/./x"),
1662				("g?y/../x", "http://a/b/c/g?y/../x"),
1663				("g#s/./x", "http://a/b/c/g#s/./x"),
1664				("g#s/../x", "http://a/b/c/g#s/../x"),
1665				("http:g", "http:g"),
1666			],
1667		);
1668	}
1669
1670	#[test]
1671	fn resolution_ambiguous_double_slash() {
1672		test_resolution("http:/a/b", [("../..//", "http:/.//")]);
1673	}
1674
1675	#[test]
1676	fn resolution_longer_segments() {
1677		test_resolution(
1678			"http://a/bb/ccc/d;p?q",
1679			[
1680				("#s", "http://a/bb/ccc/d;p?q#s"),
1681				("", "http://a/bb/ccc/d;p?q"),
1682			],
1683		);
1684	}
1685
1686	#[test]
1687	fn resolution_dot_segments_in_base() {
1688		test_resolution(
1689			"http://a/bb/ccc/./d;p?q",
1690			[
1691				("..", "http://a/bb/"),
1692				("../", "http://a/bb/"),
1693				("../g", "http://a/bb/g"),
1694				("../..", "http://a/"),
1695				("../../", "http://a/"),
1696				("../../g", "http://a/g"),
1697			],
1698		);
1699	}
1700
1701	#[test]
1702	fn resolution_double_slashes_in_base() {
1703		test_resolution(
1704			"http://ab//de//ghi",
1705			[
1706				("xyz", "http://ab//de//xyz"),
1707				("./xyz", "http://ab//de//xyz"),
1708				("../xyz", "http://ab//de/xyz"),
1709			],
1710		);
1711	}
1712
1713	#[test]
1714	fn resolution_parent_segments_in_base() {
1715		test_resolution("http://a/bb/ccc/../d;p?q", [("../../", "http://a/")]);
1716	}
1717
1718	/// https://github.com/timothee-haudebourg/iref/issues/14
1719	#[test]
1720	fn resolution_scheme_no_authority() {
1721		test_resolution("scheme:a:b/", [("Foo", "scheme:a:b/Foo")]);
1722	}
1723
1724	/// RFC 3986 Section 5.2.3: base URI with authority and empty path.
1725	#[test]
1726	fn resolution_empty_base_path() {
1727		test_resolution(
1728			"http://a",
1729			[
1730				(".", "http://a/"),
1731				("./", "http://a/"),
1732				("..", "http://a/"),
1733				("../", "http://a/"),
1734				("../g", "http://a/g"),
1735				("g", "http://a/g"),
1736				("g/", "http://a/g/"),
1737				("g/h", "http://a/g/h"),
1738				("?q", "http://a?q"),
1739				("#f", "http://a#f"),
1740			],
1741		);
1742	}
1743
1744	fn test_relative_to<'a>(base: &str, vectors: impl IntoIterator<Item = (&'a str, &'a str)>) {
1745		let base = UriRef::new(base).unwrap();
1746		for (input, expected) in vectors {
1747			let input = UriRef::new(input).unwrap();
1748			let relative = input.relative_to(base);
1749			assert_eq!(
1750				relative, expected,
1751				"({input}).relative_to({base}) != {expected}",
1752			);
1753
1754			// Round-trip: resolving the relative reference against the base
1755			// should give back the original input. Only possible when both
1756			// the base and input are full URIs (have a scheme).
1757			if let (Ok(base_uri), Ok(input_uri)) =
1758				(Uri::new(base.as_str()), Uri::new(input.as_str()))
1759			{
1760				let resolved = relative.resolved(base_uri);
1761				assert_eq!(
1762					resolved, *input_uri,
1763					"({relative}).resolved({base}) != {input}",
1764				);
1765			}
1766		}
1767	}
1768
1769	#[test]
1770	fn relative_to() {
1771		test_relative_to(
1772			"https://w3c.github.io/json-ld-api/tests/compact/0066-in.jsonld",
1773			[
1774				(
1775					"https://w3c.github.io/json-ld-api/tests/compact/link",
1776					"link",
1777				),
1778				(
1779					"https://w3c.github.io/json-ld-api/tests/compact/0066-in.jsonld#fragment-works",
1780					"#fragment-works",
1781				),
1782				(
1783					"https://w3c.github.io/json-ld-api/tests/compact/0066-in.jsonld?query=works",
1784					"?query=works",
1785				),
1786				("https://w3c.github.io/json-ld-api/tests/", "../"),
1787				("https://w3c.github.io/json-ld-api/", "../../"),
1788				("https://w3c.github.io/json-ld-api/parent", "../../parent"),
1789				(
1790					"https://w3c.github.io/json-ld-api/parent#fragment",
1791					"../../parent#fragment",
1792				),
1793				(
1794					"https://w3c.github.io/parent-parent-eq-root",
1795					"../../../parent-parent-eq-root",
1796				),
1797				(
1798					"http://example.org/scheme-relative",
1799					"http://example.org/scheme-relative",
1800				),
1801				(
1802					"https://w3c.github.io/json-ld-api/tests/compact/0066-in.jsonld",
1803					"0066-in.jsonld",
1804				),
1805			],
1806		);
1807	}
1808
1809	#[test]
1810	fn relative_to_empty_base_path() {
1811		test_relative_to(
1812			"http://a",
1813			[
1814				("http://a/", "/"),
1815				("http://a/g", "/g"),
1816				("http://a/g/h", "/g/h"),
1817				("http://a?q", "?q"),
1818				("http://a#f", "#f"),
1819				("http://a?q#f", "?q#f"),
1820			],
1821		);
1822	}
1823
1824	#[test]
1825	fn relative_to_root_base_path() {
1826		test_relative_to(
1827			"http://a/",
1828			[
1829				("http://a/", ""),
1830				("http://a/g", "g"),
1831				("http://a/g/h", "g/h"),
1832				("http://a/?q", "?q"),
1833				("http://a/#f", "#f"),
1834			],
1835		);
1836	}
1837
1838	#[test]
1839	fn relative_to_different_authority() {
1840		test_relative_to(
1841			"http://a/path",
1842			[
1843				("http://b/path", "http://b/path"),
1844				("http://b/other", "http://b/other"),
1845			],
1846		);
1847	}
1848
1849	#[test]
1850	fn relative_to_mismatched_scheme() {
1851		test_relative_to(
1852			"http://a/path",
1853			[
1854				("https://a/path", "https://a/path"),
1855				("ftp://a/path", "ftp://a/path"),
1856			],
1857		);
1858	}
1859
1860	#[test]
1861	fn relative_to_missing_scheme() {
1862		// Self has no scheme → inherits base's, so relative_to proceeds.
1863		test_relative_to(
1864			"http://a/path",
1865			[("//a/path", "path"), ("//a/other", "other")],
1866		);
1867		// Self has scheme, base doesn't → return self as-is.
1868		test_relative_to("//a/path", [("http://a/path", "http://a/path")]);
1869	}
1870
1871	#[test]
1872	fn relative_to_missing_authority() {
1873		// Authority mismatch (present vs absent) → return self as-is.
1874		test_relative_to("http://a/path", [("http:/path", "http:/path")]);
1875		test_relative_to("http:/path", [("http://a/path", "http://a/path")]);
1876	}
1877
1878	#[test]
1879	fn relative_to_same_uri() {
1880		test_relative_to(
1881			"http://a/b/c",
1882			[
1883				("http://a/b/c", "c"),
1884				("http://a/b/c?q", "?q"),
1885				("http://a/b/c#f", "#f"),
1886			],
1887		);
1888	}
1889
1890	#[test]
1891	fn invalid() {
1892		let vectors: [&[u8]; _] = [
1893			b"http://host name",      // space in host
1894			b"http://host\0name",     // null byte
1895			b"http://[::1",           // unclosed bracket
1896			b"http://ho st/path",     // space in authority
1897			b"htt p://host",          // space in scheme
1898			b"http://host/pa th",     // space in path
1899			b"http://host?qu ery",    // space in query
1900			b"http://host#fra gment", // space in fragment
1901			b"\xff://host",           // invalid byte in scheme
1902		];
1903
1904		for input in vectors {
1905			assert!(UriRef::new(input).is_err(), "should reject: {input:?}");
1906		}
1907	}
1908
1909	#[test]
1910	fn resolve_relative_to_round_trip() {
1911		let bases = ["http://a/b/c/d", "http://a/b/c/", "http://a/", "http://a"];
1912
1913		let targets = [
1914			"http://a/",
1915			"http://a/g",
1916			"http://a/b/g",
1917			"http://a/b/c/g",
1918			"http://a/b/c/g/",
1919			"http://a/b/c/?q",
1920			"http://a/b/c/#f",
1921			"http://a/b/c/g?q#f",
1922		];
1923
1924		for base in bases {
1925			let base_uri = Uri::new(base).unwrap();
1926			for target in targets {
1927				let target_uri = UriRef::new(target).unwrap();
1928				let rel = target_uri.relative_to(base_uri.as_uri_ref());
1929				let resolved = rel.resolved(base_uri);
1930				assert_eq!(
1931					resolved.as_str(),
1932					target,
1933					"round-trip failed: ({target}).relative_to({base}).resolved({base}) = {resolved}",
1934				);
1935			}
1936		}
1937	}
1938
1939	#[test]
1940	fn valid_relative() {
1941		let vectors = [
1942			"",            // empty is valid
1943			"/path",       // absolute path
1944			"../..",       // relative reference
1945			"?query",      // query only
1946			"#fragment",   // fragment only
1947			"//authority", // authority without scheme
1948		];
1949
1950		for input in vectors {
1951			assert!(UriRef::new(input).is_ok(), "should accept: {input:?}");
1952		}
1953	}
1954}