iref_core/uri/
mod.rs

1use static_regular_grammar::RegularGrammar;
2use std::{
3	borrow::{Borrow, Cow},
4	hash::{self, Hash},
5};
6
7mod authority;
8mod authority_mut;
9mod fragment;
10mod path;
11mod path_mut;
12mod query;
13mod reference;
14mod scheme;
15
16pub use authority::*;
17pub use authority_mut::*;
18pub use fragment::*;
19pub use path::*;
20pub use path_mut::*;
21pub use query::*;
22pub use reference::*;
23pub use scheme::*;
24
25use crate::{
26	common::{bytestr_eq, parse, RiBufImpl, RiImpl, RiRefBufImpl, RiRefImpl},
27	Iri, IriBuf, IriRef, IriRefBuf,
28};
29
30macro_rules! uri_error {
31	($($(#[$meta:meta])* $variant:ident : $ident:ident),*) => {
32		#[derive(Debug, thiserror::Error)]
33		pub enum UriError<T> {
34			$(
35				$(#[$meta])*
36				$variant(#[from] $ident<T>)
37			),*
38		}
39
40		$(
41			impl<'a> From<$ident<String>> for UriError<Cow<'a, str>> {
42				fn from($ident(value): $ident<String>) -> Self {
43					Self::$variant($ident(Cow::Owned(value)))
44				}
45			}
46
47			impl<'a> From<$ident<&'a str>> for UriError<Cow<'a, str>> {
48				fn from($ident(value): $ident<&'a str>) -> Self {
49					Self::$variant($ident(Cow::Borrowed(value)))
50				}
51			}
52
53			impl<'a> From<$ident<Vec<u8>>> for UriError<Cow<'a, [u8]>> {
54				fn from($ident(value): $ident<Vec<u8>>) -> Self {
55					Self::$variant($ident(Cow::Owned(value)))
56				}
57			}
58
59			impl<'a> From<$ident<&'a [u8]>> for UriError<Cow<'a, [u8]>> {
60				fn from($ident(value): $ident<&'a [u8]>) -> Self {
61					Self::$variant($ident(Cow::Borrowed(value)))
62				}
63			}
64		)*
65	};
66}
67
68uri_error! {
69	#[error("invalid URI: {0}")]
70	Uri: InvalidUri,
71
72	#[error("invalid URI reference: {0}")]
73	Reference: InvalidUriRef,
74
75	#[error("invalid URI scheme: {0}")]
76	Scheme: InvalidScheme,
77
78	#[error("invalid URI authority: {0}")]
79	Authority: InvalidAuthority,
80
81	#[error("invalid URI authority user info: {0}")]
82	UserInfo: InvalidUserInfo,
83
84	#[error("invalid URI authority host: {0}")]
85	Host: InvalidHost,
86
87	#[error("invalid URI authority port: {0}")]
88	Port: InvalidPort,
89
90	#[error("invalid URI path: {0}")]
91	Path: InvalidPath,
92
93	#[error("invalid URI path segment: {0}")]
94	PathSegment: InvalidSegment,
95
96	#[error("invalid URI query: {0}")]
97	Query: InvalidQuery,
98
99	#[error("invalid URI fragment: {0}")]
100	Fragment: InvalidFragment
101}
102
103/// Uniform Resource Identifier (URI).
104#[derive(RegularGrammar)]
105#[grammar(
106	file = "src/uri/grammar.abnf",
107	entry_point = "URI",
108	ascii,
109	cache = "automata/uri.aut.cbor"
110)]
111#[grammar(sized(UriBuf, derive(Debug, Display, PartialEq, Eq, PartialOrd, Ord, Hash)))]
112#[cfg_attr(feature = "serde", grammar(serde))]
113#[cfg_attr(feature = "ignore-grammars", grammar(disable))]
114pub struct Uri([u8]);
115
116impl RiRefImpl for Uri {
117	type Authority = Authority;
118	type Path = Path;
119	type Query = Query;
120	type Fragment = Fragment;
121
122	type RiRefBuf = UriRefBuf;
123
124	fn as_bytes(&self) -> &[u8] {
125		&self.0
126	}
127}
128
129impl RiImpl for Uri {}
130
131#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
132pub struct UriParts<'a> {
133	pub scheme: &'a Scheme,
134	pub authority: Option<&'a Authority>,
135	pub path: &'a Path,
136	pub query: Option<&'a Query>,
137	pub fragment: Option<&'a Fragment>,
138}
139
140impl Uri {
141	pub fn parts(&self) -> UriParts {
142		let bytes = self.as_bytes();
143		let ranges = parse::parts(bytes, 0);
144
145		UriParts {
146			scheme: unsafe { Scheme::new_unchecked(&bytes[ranges.scheme]) },
147			authority: ranges
148				.authority
149				.map(|r| unsafe { Authority::new_unchecked(&self.0[r]) }),
150			path: unsafe { Path::new_unchecked(&self.0[ranges.path]) },
151			query: ranges
152				.query
153				.map(|r| unsafe { Query::new_unchecked(&self.0[r]) }),
154			fragment: ranges
155				.fragment
156				.map(|r| unsafe { Fragment::new_unchecked(&self.0[r]) }),
157		}
158	}
159
160	/// Converts this URI into an URI reference.
161	///
162	/// All IRI are valid URI references.
163	pub fn as_uri_ref(&self) -> &UriRef {
164		unsafe { UriRef::new_unchecked(&self.0) }
165	}
166
167	pub fn as_iri(&self) -> &Iri {
168		unsafe { Iri::new_unchecked(std::str::from_utf8_unchecked(&self.0)) }
169	}
170
171	pub fn as_iri_ref(&self) -> &IriRef {
172		unsafe { IriRef::new_unchecked(std::str::from_utf8_unchecked(&self.0)) }
173	}
174
175	/// Returns the scheme of the URI.
176	#[inline]
177	pub fn scheme(&self) -> &Scheme {
178		RiImpl::scheme(self)
179	}
180
181	/// Returns the authority part of the URI, if any.
182	pub fn authority(&self) -> Option<&Authority> {
183		RiRefImpl::authority(self)
184	}
185
186	/// Returns the path of the URI.
187	pub fn path(&self) -> &Path {
188		RiRefImpl::path(self)
189	}
190
191	pub fn query(&self) -> Option<&Query> {
192		RiRefImpl::query(self)
193	}
194
195	pub fn fragment(&self) -> Option<&Fragment> {
196		RiRefImpl::fragment(self)
197	}
198
199	/// Get this IRI relatively to the given IRI reference.
200	///
201	/// # Example
202	/// ```
203	/// # use iref_core::Uri;
204	/// let a = Uri::new(b"https://crates.io/").unwrap();
205	/// let b = Uri::new(b"https://crates.io/crates/iref").unwrap();
206	/// let c = Uri::new(b"https://crates.io/crates/json-ld").unwrap();
207	/// assert_eq!(b.relative_to(a.as_uri_ref()), "crates/iref");
208	/// assert_eq!(a.relative_to(b.as_uri_ref()), "..");
209	/// assert_eq!(b.relative_to(c.as_uri_ref()), "iref");
210	/// assert_eq!(c.relative_to(b.as_uri_ref()), "json-ld");
211	/// ```
212	pub fn relative_to(&self, other: &(impl ?Sized + AsRef<UriRef>)) -> UriRefBuf {
213		self.as_uri_ref().relative_to(other)
214	}
215
216	/// Get the suffix of this URI, if any, with regard to the given prefix URI.
217	///
218	/// Returns `Some((suffix, query, fragment))` if this URI is of the form
219	/// `prefix/suffix?query#fragment` where `prefix` is given as parameter.
220	/// Returns `None` otherwise.
221	/// If the `suffix` scheme or authority is different from this path, it will return `None`.
222	///
223	/// See [`Path::suffix`] for more details.
224	#[inline]
225	pub fn suffix(
226		&self,
227		prefix: &(impl ?Sized + AsRef<Uri>),
228	) -> Option<(PathBuf, Option<&Query>, Option<&Fragment>)> {
229		RiRefImpl::suffix(self, prefix.as_ref())
230	}
231
232	/// The URI without the file name, query and fragment.
233	///
234	/// # Example
235	/// ```
236	/// # use iref_core::Uri;
237	/// let a = Uri::new(b"https://crates.io/crates/iref?query#fragment").unwrap();
238	/// let b = Uri::new(b"https://crates.io/crates/iref/?query#fragment").unwrap();
239	/// assert_eq!(a.base(), "https://crates.io/crates/");
240	/// assert_eq!(b.base(), "https://crates.io/crates/iref/")
241	/// ```
242	#[inline]
243	pub fn base(&self) -> &Self {
244		unsafe { Self::new_unchecked(RiRefImpl::base(self)) }
245	}
246}
247
248impl AsRef<UriRef> for Uri {
249	fn as_ref(&self) -> &UriRef {
250		self.as_uri_ref()
251	}
252}
253
254impl AsRef<Iri> for Uri {
255	fn as_ref(&self) -> &Iri {
256		self.as_iri()
257	}
258}
259
260impl AsRef<IriRef> for Uri {
261	fn as_ref(&self) -> &IriRef {
262		self.as_iri_ref()
263	}
264}
265
266impl Borrow<UriRef> for Uri {
267	fn borrow(&self) -> &UriRef {
268		self.as_uri_ref()
269	}
270}
271
272impl Borrow<Iri> for Uri {
273	fn borrow(&self) -> &Iri {
274		self.as_iri()
275	}
276}
277
278impl Borrow<IriRef> for Uri {
279	fn borrow(&self) -> &IriRef {
280		self.as_iri_ref()
281	}
282}
283
284bytestr_eq!(Uri);
285
286impl PartialEq for Uri {
287	fn eq(&self, other: &Self) -> bool {
288		self.parts() == other.parts()
289	}
290}
291
292impl<'a> PartialEq<&'a Uri> for Uri {
293	fn eq(&self, other: &&'a Self) -> bool {
294		*self == **other
295	}
296}
297
298impl PartialEq<UriBuf> for Uri {
299	fn eq(&self, other: &UriBuf) -> bool {
300		*self == *other.as_uri()
301	}
302}
303
304impl PartialEq<UriRef> for Uri {
305	fn eq(&self, other: &UriRef) -> bool {
306		*self.as_uri_ref() == *other
307	}
308}
309
310impl<'a> PartialEq<&'a UriRef> for Uri {
311	fn eq(&self, other: &&'a UriRef) -> bool {
312		*self.as_uri_ref() == **other
313	}
314}
315
316impl PartialEq<UriRefBuf> for Uri {
317	fn eq(&self, other: &UriRefBuf) -> bool {
318		*self.as_uri_ref() == *other.as_uri_ref()
319	}
320}
321
322impl Eq for Uri {}
323
324impl PartialOrd for Uri {
325	fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
326		Some(self.cmp(other))
327	}
328}
329
330impl<'a> PartialOrd<&'a Uri> for Uri {
331	fn partial_cmp(&self, other: &&'a Self) -> Option<std::cmp::Ordering> {
332		self.partial_cmp(*other)
333	}
334}
335
336impl PartialOrd<UriBuf> for Uri {
337	fn partial_cmp(&self, other: &UriBuf) -> Option<std::cmp::Ordering> {
338		self.partial_cmp(other.as_uri())
339	}
340}
341
342impl PartialOrd<UriRef> for Uri {
343	fn partial_cmp(&self, other: &UriRef) -> Option<std::cmp::Ordering> {
344		self.as_uri_ref().partial_cmp(other)
345	}
346}
347
348impl<'a> PartialOrd<&'a UriRef> for Uri {
349	fn partial_cmp(&self, other: &&'a UriRef) -> Option<std::cmp::Ordering> {
350		self.as_uri_ref().partial_cmp(*other)
351	}
352}
353
354impl PartialOrd<UriRefBuf> for Uri {
355	fn partial_cmp(&self, other: &UriRefBuf) -> Option<std::cmp::Ordering> {
356		self.partial_cmp(other.as_uri_ref())
357	}
358}
359
360impl Ord for Uri {
361	fn cmp(&self, other: &Self) -> std::cmp::Ordering {
362		self.parts().cmp(&other.parts())
363	}
364}
365
366impl Hash for Uri {
367	fn hash<H: hash::Hasher>(&self, state: &mut H) {
368		self.parts().hash(state)
369	}
370}
371
372impl RiRefImpl for UriBuf {
373	type Authority = Authority;
374	type Path = Path;
375	type Query = Query;
376	type Fragment = Fragment;
377
378	type RiRefBuf = UriRefBuf;
379
380	fn as_bytes(&self) -> &[u8] {
381		&self.0
382	}
383}
384
385impl RiImpl for UriBuf {}
386
387impl RiRefBufImpl for UriBuf {
388	type Ri = Uri;
389	type RiBuf = Self;
390
391	unsafe fn new_unchecked(bytes: Vec<u8>) -> Self {
392		Self::new_unchecked(bytes)
393	}
394
395	unsafe fn as_mut_vec(&mut self) -> &mut Vec<u8> {
396		&mut self.0
397	}
398
399	fn into_bytes(self) -> Vec<u8> {
400		self.0
401	}
402}
403
404impl RiBufImpl for UriBuf {}
405
406impl UriBuf {
407	pub fn from_scheme(scheme: SchemeBuf) -> Self {
408		RiBufImpl::from_scheme(scheme)
409	}
410
411	pub fn into_uri_ref(self) -> UriRefBuf {
412		unsafe { UriRefBuf::new_unchecked(self.0) }
413	}
414
415	pub fn into_iri(self) -> IriBuf {
416		unsafe { IriBuf::new_unchecked(String::from_utf8_unchecked(self.0)) }
417	}
418
419	pub fn into_iri_ref(self) -> IriRefBuf {
420		unsafe { IriRefBuf::new_unchecked(String::from_utf8_unchecked(self.0)) }
421	}
422
423	/// Returns a mutable reference to the underlying `Vec<u8>` buffer
424	/// representing the URI.
425	///
426	/// # Safety
427	///
428	/// The caller must ensure that once the mutable reference is dropped, its
429	/// content is still a valid URI.
430	pub unsafe fn as_mut_vec(&mut self) -> &mut Vec<u8> {
431		&mut self.0
432	}
433
434	pub fn path_mut(&mut self) -> PathMut {
435		PathMut::from_impl(RiRefBufImpl::path_mut(self))
436	}
437
438	pub fn authority_mut(&mut self) -> Option<AuthorityMut> {
439		RiRefBufImpl::authority_mut(self).map(AuthorityMut::from_impl)
440	}
441
442	/// Sets the scheme part.
443	///
444	/// # Example
445	///
446	/// ```
447	/// # use iref_core as iref;
448	/// use iref::{UriBuf, uri::Scheme};
449	///
450	/// let mut a = UriBuf::new(b"http://example.org/path".to_vec()).unwrap();
451	/// a.set_scheme(Scheme::new(b"https").unwrap());
452	/// assert_eq!(a, b"https://example.org/path");
453	/// ```
454	pub fn set_scheme(&mut self, new_scheme: &Scheme) {
455		RiBufImpl::set_scheme(self, new_scheme)
456	}
457
458	/// Sets the authority part.
459	///
460	/// If the path is relative, this also turns it into an absolute path,
461	/// since an authority cannot be followed by a relative path.
462	///
463	/// To avoid any ambiguity, if `authority` is `None` and the path starts
464	/// with `//`, it will be changed into `/.//` as to not be interpreted as
465	/// an authority part.
466	///
467	/// # Example
468	///
469	/// ```
470	/// # use iref_core as iref;
471	/// use iref::{UriBuf, uri::Authority};
472	///
473	/// let mut a = UriBuf::new(b"scheme:/path".to_vec()).unwrap();
474	/// a.set_authority(Some(Authority::new(b"example.org").unwrap()));
475	/// assert_eq!(a, b"scheme://example.org/path");
476	///
477	/// // When an authority is added before a relative path,
478	/// // the path becomes absolute.
479	/// let mut b = UriBuf::new(b"scheme:path".to_vec()).unwrap();
480	/// b.set_authority(Some(Authority::new(b"example.org").unwrap()));
481	/// assert_eq!(b, b"scheme://example.org/path");
482	///
483	/// // When an authority is removed and the path starts with `//`,
484	/// // a `/.` prefix is added to the path to avoid any ambiguity.
485	/// let mut c = UriBuf::new(b"scheme://example.org//path".to_vec()).unwrap();
486	/// c.set_authority(None);
487	/// assert_eq!(c, b"scheme:/.//path");
488	/// ```
489	pub fn set_authority(&mut self, authority: Option<&Authority>) {
490		RiRefBufImpl::set_authority(self, authority)
491	}
492
493	/// Sets the path part.
494	///
495	/// If there is an authority and the path is relative, this also turns it
496	/// into an absolute path, since an authority cannot be followed by a
497	/// relative path.
498	///
499	/// To avoid any ambiguity, if there is no authority and the path starts
500	/// with `//`, it will be changed into `/.//` as to not be interpreted as
501	/// an authority part. Similarly if there is no scheme nor authority and the
502	/// beginning of the new path looks like a scheme, it is prefixed with `./`
503	/// to not be confused with a scheme.
504	///
505	/// # Example
506	///
507	/// ```
508	/// # use iref_core as iref;
509	/// use iref::{UriBuf, uri::Path};
510	///
511	/// let mut a = UriBuf::new(b"http://example.org/old/path".to_vec()).unwrap();
512	/// a.set_path(Path::new(b"/foo/bar").unwrap());
513	/// assert_eq!(a, b"http://example.org/foo/bar");
514	///
515	/// // If there is an authority and the new path is relative,
516	/// // it is turned into an absolute path.
517	/// let mut b = UriBuf::new(b"http://example.org/old/path".to_vec()).unwrap();
518	/// b.set_path(Path::new(b"relative/path").unwrap());
519	/// assert_eq!(b, b"http://example.org/relative/path");
520	///
521	/// // If there is no authority and the path starts with `//`,
522	/// // it is prefixed with `/.` to avoid being confused with an authority.
523	/// let mut c = UriBuf::new(b"http:old/path".to_vec()).unwrap();
524	/// c.set_path(Path::new(b"//foo/bar").unwrap());
525	/// assert_eq!(c, b"http:/.//foo/bar");
526	/// ```
527	pub fn set_path(&mut self, path: &Path) {
528		RiRefBufImpl::set_path(self, path)
529	}
530
531	/// Sets the query part.
532	pub fn set_query(&mut self, query: Option<&Query>) {
533		RiRefBufImpl::set_query(self, query)
534	}
535
536	/// Sets the fragment part.
537	pub fn set_fragment(&mut self, fragment: Option<&Fragment>) {
538		RiRefBufImpl::set_fragment(self, fragment)
539	}
540}
541
542impl AsRef<UriRef> for UriBuf {
543	fn as_ref(&self) -> &UriRef {
544		self.as_uri_ref()
545	}
546}
547
548impl AsRef<Iri> for UriBuf {
549	fn as_ref(&self) -> &Iri {
550		self.as_iri()
551	}
552}
553
554impl AsRef<IriRef> for UriBuf {
555	fn as_ref(&self) -> &IriRef {
556		self.as_iri_ref()
557	}
558}
559
560impl Borrow<UriRef> for UriBuf {
561	fn borrow(&self) -> &UriRef {
562		self.as_uri_ref()
563	}
564}
565
566impl Borrow<Iri> for UriBuf {
567	fn borrow(&self) -> &Iri {
568		self.as_iri()
569	}
570}
571
572impl Borrow<IriRef> for UriBuf {
573	fn borrow(&self) -> &IriRef {
574		self.as_iri_ref()
575	}
576}
577
578impl From<UriBuf> for UriRefBuf {
579	fn from(value: UriBuf) -> Self {
580		value.into_uri_ref()
581	}
582}
583
584bytestr_eq!(UriBuf);
585
586impl PartialEq<UriRef> for UriBuf {
587	fn eq(&self, other: &UriRef) -> bool {
588		*self.as_uri_ref() == *other
589	}
590}
591
592impl<'a> PartialEq<&'a UriRef> for UriBuf {
593	fn eq(&self, other: &&'a UriRef) -> bool {
594		*self.as_uri_ref() == **other
595	}
596}
597
598impl PartialEq<UriRefBuf> for UriBuf {
599	fn eq(&self, other: &UriRefBuf) -> bool {
600		*self.as_uri_ref() == *other.as_uri_ref()
601	}
602}
603
604impl PartialOrd<UriRef> for UriBuf {
605	fn partial_cmp(&self, other: &UriRef) -> Option<std::cmp::Ordering> {
606		self.as_uri_ref().partial_cmp(other)
607	}
608}
609
610impl<'a> PartialOrd<&'a UriRef> for UriBuf {
611	fn partial_cmp(&self, other: &&'a UriRef) -> Option<std::cmp::Ordering> {
612		self.as_uri_ref().partial_cmp(*other)
613	}
614}
615
616impl PartialOrd<UriRefBuf> for UriBuf {
617	fn partial_cmp(&self, other: &UriRefBuf) -> Option<std::cmp::Ordering> {
618		self.as_uri_ref().partial_cmp(other.as_uri_ref())
619	}
620}