1#![doc = include_str!("../README.md")]
2#![cfg_attr(docsrs, feature(doc_auto_cfg))]
3#![deny(unsafe_code)]
4
5#[cfg(feature = "serde")]
6use serde::{Deserialize, Deserializer, Serialize, Serializer};
7use std::borrow::{Borrow, Cow};
8use std::cmp::Ordering;
9use std::convert::{TryFrom, TryInto};
10use std::error::Error;
11use std::fmt;
12use std::hash::{Hash, Hasher};
13use std::net::{AddrParseError, Ipv6Addr};
14use std::ops::Deref;
15use std::str::{Chars, FromStr};
16
17#[derive(Clone, Copy)]
40pub struct IriRef<T> {
41 iri: T,
42 positions: IriElementsPositions,
43}
44
45impl<T: Deref<Target = str>> IriRef<T> {
46 pub fn parse(iri: T) -> Result<Self, IriParseError> {
59 let positions = IriParser::<_, false>::parse(&iri, None, &mut VoidOutputBuffer::default())?;
60 Ok(Self { iri, positions })
61 }
62
63 pub fn parse_unchecked(iri: T) -> Self {
71 let positions =
72 IriParser::<_, true>::parse(&iri, None, &mut VoidOutputBuffer::default()).unwrap();
73 Self { iri, positions }
74 }
75
76 pub fn resolve(&self, iri: &str) -> Result<IriRef<String>, IriParseError> {
90 let mut target_buffer = String::with_capacity(self.iri.len() + iri.len());
91 let positions = IriParser::<_, false>::parse(iri, Some(self.as_ref()), &mut target_buffer)?;
92 Ok(IriRef {
93 iri: target_buffer,
94 positions,
95 })
96 }
97
98 pub fn resolve_unchecked(&self, iri: &str) -> IriRef<String> {
108 let mut target_buffer = String::with_capacity(self.iri.len() + iri.len());
109 let positions =
110 IriParser::<_, true>::parse(iri, Some(self.as_ref()), &mut target_buffer).unwrap();
111 IriRef {
112 iri: target_buffer,
113 positions,
114 }
115 }
116
117 pub fn resolve_into(&self, iri: &str, target_buffer: &mut String) -> Result<(), IriParseError> {
134 IriParser::<_, false>::parse(iri, Some(self.as_ref()), target_buffer)?;
135 Ok(())
136 }
137
138 pub fn resolve_into_unchecked(&self, iri: &str, target_buffer: &mut String) {
149 IriParser::<_, true>::parse(iri, Some(self.as_ref()), target_buffer).unwrap();
150 }
151
152 #[inline]
154 pub fn as_ref(&self) -> IriRef<&str> {
155 IriRef {
156 iri: &self.iri,
157 positions: self.positions,
158 }
159 }
160
161 #[inline]
171 pub fn as_str(&self) -> &str {
172 &self.iri
173 }
174
175 #[inline]
185 pub fn into_inner(self) -> T {
186 self.iri
187 }
188
189 #[inline]
199 pub fn is_absolute(&self) -> bool {
200 self.positions.scheme_end != 0
201 }
202
203 #[inline]
214 pub fn scheme(&self) -> Option<&str> {
215 if self.positions.scheme_end == 0 {
216 None
217 } else {
218 Some(&self.iri[..self.positions.scheme_end - 1])
219 }
220 }
221
222 #[inline]
237 pub fn authority(&self) -> Option<&str> {
238 if self.positions.scheme_end + 2 > self.positions.authority_end {
239 None
240 } else {
241 Some(&self.iri[self.positions.scheme_end + 2..self.positions.authority_end])
242 }
243 }
244
245 #[inline]
258 pub fn path(&self) -> &str {
259 &self.iri[self.positions.authority_end..self.positions.path_end]
260 }
261
262 #[inline]
272 pub fn query(&self) -> Option<&str> {
273 if self.positions.path_end >= self.positions.query_end {
274 None
275 } else {
276 Some(&self.iri[self.positions.path_end + 1..self.positions.query_end])
277 }
278 }
279
280 #[inline]
290 pub fn fragment(&self) -> Option<&str> {
291 if self.positions.query_end >= self.iri.len() {
292 None
293 } else {
294 Some(&self.iri[self.positions.query_end + 1..])
295 }
296 }
297}
298
299impl<Lft: PartialEq<Rhs>, Rhs> PartialEq<IriRef<Rhs>> for IriRef<Lft> {
300 #[inline]
301 fn eq(&self, other: &IriRef<Rhs>) -> bool {
302 self.iri.eq(&other.iri)
303 }
304}
305
306impl<T: PartialEq<str>> PartialEq<str> for IriRef<T> {
307 #[inline]
308 fn eq(&self, other: &str) -> bool {
309 self.iri.eq(other)
310 }
311}
312
313impl<'a, T: PartialEq<&'a str>> PartialEq<&'a str> for IriRef<T> {
314 #[inline]
315 fn eq(&self, other: &&'a str) -> bool {
316 self.iri.eq(other)
317 }
318}
319
320impl<T: PartialEq<String>> PartialEq<String> for IriRef<T> {
321 #[inline]
322 fn eq(&self, other: &String) -> bool {
323 self.iri.eq(other)
324 }
325}
326
327impl<'a, T: PartialEq<Cow<'a, str>>> PartialEq<Cow<'a, str>> for IriRef<T> {
328 #[inline]
329 fn eq(&self, other: &Cow<'a, str>) -> bool {
330 self.iri.eq(other)
331 }
332}
333
334impl<T: PartialEq<str>> PartialEq<IriRef<T>> for str {
335 #[inline]
336 fn eq(&self, other: &IriRef<T>) -> bool {
337 other.iri.eq(self)
338 }
339}
340
341impl<'a, T: PartialEq<&'a str>> PartialEq<IriRef<T>> for &'a str {
342 #[inline]
343 fn eq(&self, other: &IriRef<T>) -> bool {
344 other.iri.eq(self)
345 }
346}
347
348impl<T: PartialEq<String>> PartialEq<IriRef<T>> for String {
349 #[inline]
350 fn eq(&self, other: &IriRef<T>) -> bool {
351 other.iri.eq(self)
352 }
353}
354
355impl<'a, T: PartialEq<Cow<'a, str>>> PartialEq<IriRef<T>> for Cow<'a, str> {
356 #[inline]
357 fn eq(&self, other: &IriRef<T>) -> bool {
358 other.iri.eq(self)
359 }
360}
361
362impl<T: Eq> Eq for IriRef<T> {}
363
364impl<T: Hash> Hash for IriRef<T> {
365 #[inline]
366 fn hash<H: Hasher>(&self, state: &mut H) {
367 self.iri.hash(state)
368 }
369}
370
371impl<Lft: PartialOrd<Rhs>, Rhs> PartialOrd<IriRef<Rhs>> for IriRef<Lft> {
372 #[inline]
373 fn partial_cmp(&self, other: &IriRef<Rhs>) -> Option<Ordering> {
374 self.iri.partial_cmp(&other.iri)
375 }
376}
377
378impl<T: Ord> Ord for IriRef<T> {
379 #[inline]
380 fn cmp(&self, other: &Self) -> Ordering {
381 self.iri.cmp(&other.iri)
382 }
383}
384
385impl<T: Deref<Target = str>> Deref for IriRef<T> {
386 type Target = str;
387
388 #[inline]
389 fn deref(&self) -> &str {
390 self.iri.deref()
391 }
392}
393
394impl<T: AsRef<str>> AsRef<str> for IriRef<T> {
395 #[inline]
396 fn as_ref(&self) -> &str {
397 self.iri.as_ref()
398 }
399}
400
401impl<T: Borrow<str>> Borrow<str> for IriRef<T> {
402 #[inline]
403 fn borrow(&self) -> &str {
404 self.iri.borrow()
405 }
406}
407
408impl<T: fmt::Debug> fmt::Debug for IriRef<T> {
409 #[inline]
410 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
411 self.iri.fmt(f)
412 }
413}
414
415impl<T: fmt::Display> fmt::Display for IriRef<T> {
416 #[inline]
417 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
418 self.iri.fmt(f)
419 }
420}
421
422impl FromStr for IriRef<String> {
423 type Err = IriParseError;
424
425 #[inline]
426 fn from_str(iri: &str) -> Result<Self, IriParseError> {
427 Self::parse(iri.to_owned())
428 }
429}
430
431impl<'a> From<IriRef<&'a str>> for IriRef<String> {
432 #[inline]
433 fn from(iri: IriRef<&'a str>) -> Self {
434 Self {
435 iri: iri.iri.into(),
436 positions: iri.positions,
437 }
438 }
439}
440
441impl<'a> From<IriRef<Cow<'a, str>>> for IriRef<String> {
442 #[inline]
443 fn from(iri: IriRef<Cow<'a, str>>) -> Self {
444 Self {
445 iri: iri.iri.into(),
446 positions: iri.positions,
447 }
448 }
449}
450
451impl From<IriRef<Box<str>>> for IriRef<String> {
452 #[inline]
453 fn from(iri: IriRef<Box<str>>) -> Self {
454 Self {
455 iri: iri.iri.into(),
456 positions: iri.positions,
457 }
458 }
459}
460
461impl<'a> From<IriRef<&'a str>> for IriRef<Cow<'a, str>> {
462 #[inline]
463 fn from(iri: IriRef<&'a str>) -> Self {
464 Self {
465 iri: iri.iri.into(),
466 positions: iri.positions,
467 }
468 }
469}
470
471impl From<IriRef<String>> for IriRef<Cow<'_, str>> {
472 #[inline]
473 fn from(iri: IriRef<String>) -> Self {
474 Self {
475 iri: iri.iri.into(),
476 positions: iri.positions,
477 }
478 }
479}
480
481impl<'a> From<&'a IriRef<String>> for IriRef<&'a str> {
482 #[inline]
483 fn from(iri: &'a IriRef<String>) -> Self {
484 Self {
485 iri: &iri.iri,
486 positions: iri.positions,
487 }
488 }
489}
490
491impl<'a> From<&'a IriRef<Cow<'a, str>>> for IriRef<&'a str> {
492 #[inline]
493 fn from(iri: &'a IriRef<Cow<'a, str>>) -> Self {
494 Self {
495 iri: &iri.iri,
496 positions: iri.positions,
497 }
498 }
499}
500
501#[cfg(feature = "serde")]
502impl<T: Serialize> Serialize for IriRef<T> {
503 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
504 self.iri.serialize(serializer)
505 }
506}
507
508#[cfg(feature = "serde")]
509impl<'de, T: Deref<Target = str> + Deserialize<'de>> Deserialize<'de> for IriRef<T> {
510 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
511 use serde::de::Error;
512
513 Self::parse(T::deserialize(deserializer)?).map_err(Error::custom)
514 }
515}
516
517#[derive(Clone, Copy)]
541pub struct Iri<T>(IriRef<T>);
542
543impl<T: Deref<Target = str>> Iri<T> {
544 pub fn parse(iri: T) -> Result<Self, IriParseError> {
557 IriRef::parse(iri)?.try_into()
558 }
559
560 pub fn parse_unchecked(iri: T) -> Self {
568 Iri(IriRef::parse_unchecked(iri))
569 }
570
571 pub fn resolve(&self, iri: &str) -> Result<Iri<String>, IriParseError> {
585 Ok(Iri(self.0.resolve(iri)?))
586 }
587
588 pub fn resolve_unchecked(&self, iri: &str) -> Iri<String> {
598 Iri(self.0.resolve_unchecked(iri))
599 }
600
601 pub fn resolve_into(&self, iri: &str, target_buffer: &mut String) -> Result<(), IriParseError> {
618 self.0.resolve_into(iri, target_buffer)
619 }
620
621 pub fn resolve_into_unchecked(&self, iri: &str, target_buffer: &mut String) {
632 self.0.resolve_into_unchecked(iri, target_buffer)
633 }
634
635 pub fn relativize<T2: Deref<Target = str>>(
653 &self,
654 abs: &Iri<T2>,
655 ) -> Result<IriRef<String>, IriRelativizeError> {
656 let base = self;
657 let abs_authority = abs.authority();
658 let base_authority = base.authority();
659 let abs_path = abs.path();
660 let base_path = base.path();
661 let abs_query = abs.query();
662 let base_query = base.query();
663
664 for segment in abs_path.split('/') {
666 if matches!(segment, "." | "..") {
667 return Err(IriRelativizeError {});
668 }
669 }
670
671 if abs.scheme() != base.scheme()
672 || abs_authority.is_none()
673 && (base_authority.is_some()
674 || abs_path.is_empty()
675 && (!base_path.is_empty() || abs_query.is_none() && base_query.is_some()))
676 || abs_path
677 .split_once(':')
679 .map_or(false, |(candidate_scheme, _)| {
680 !candidate_scheme.contains('/')
681 })
682 {
683 return Ok(IriRef {
684 iri: abs.0.to_string(),
685 positions: abs.0.positions,
686 });
687 }
688 if abs_authority != base_authority
689 || abs_path.is_empty() && (!base_path.is_empty() || base_query.is_some())
691 || abs_path.starts_with("//")
693 {
694 return Ok(IriRef {
695 iri: abs.0[abs.0.positions.scheme_end..].to_string(),
696 positions: IriElementsPositions {
697 scheme_end: 0,
698 authority_end: abs.0.positions.authority_end - abs.0.positions.scheme_end,
699 path_end: abs.0.positions.path_end - abs.0.positions.scheme_end,
700 query_end: abs.0.positions.query_end - abs.0.positions.scheme_end,
701 },
702 });
703 }
704 if abs_path != base_path || abs_query.is_none() && base_query.is_some() {
705 let number_of_shared_characters = abs_path
706 .chars()
707 .zip(base_path.chars())
708 .take_while(|(l, r)| l == r)
709 .map(|(l, _)| l.len_utf8())
710 .sum::<usize>();
711 let number_of_shared_characters = abs_path[..number_of_shared_characters]
713 .rfind('/')
714 .map_or(0, |n| n + 1);
715 return if abs_path[number_of_shared_characters..].starts_with('/')
716 || base_path[number_of_shared_characters..].contains('/')
717 || abs_path[number_of_shared_characters..].contains(':')
718 {
719 if !abs_path.starts_with('/') && !base_path.is_empty() {
721 Ok(IriRef {
724 iri: abs.0.to_string(),
725 positions: abs.0.positions,
726 })
727 } else {
728 Ok(IriRef {
729 iri: abs.0[abs.0.positions.authority_end..].to_string(),
730 positions: IriElementsPositions {
731 scheme_end: 0,
732 authority_end: 0,
733 path_end: abs.0.positions.path_end - abs.0.positions.authority_end,
734 query_end: abs.0.positions.query_end - abs.0.positions.authority_end,
735 },
736 })
737 }
738 } else if abs_path[number_of_shared_characters..].is_empty() {
739 let mut iri = String::with_capacity(
741 abs.0.len() - abs.0.positions.authority_end - number_of_shared_characters + 1,
742 );
743 iri.push('.');
744 iri.push_str(&abs.0[abs.0.positions.authority_end + number_of_shared_characters..]);
745 Ok(IriRef {
746 iri,
747 positions: IriElementsPositions {
748 scheme_end: 0,
749 authority_end: 0,
750 path_end: abs.0.positions.path_end
751 - abs.0.positions.authority_end
752 - number_of_shared_characters
753 + 1,
754 query_end: abs.0.positions.query_end
755 - abs.0.positions.authority_end
756 - number_of_shared_characters
757 + 1,
758 },
759 })
760 } else {
761 Ok(IriRef {
763 iri: abs.0[abs.0.positions.authority_end + number_of_shared_characters..]
764 .to_string(),
765 positions: IriElementsPositions {
766 scheme_end: 0,
767 authority_end: 0,
768 path_end: abs.0.positions.path_end
769 - abs.0.positions.authority_end
770 - number_of_shared_characters,
771 query_end: abs.0.positions.query_end
772 - abs.0.positions.authority_end
773 - number_of_shared_characters,
774 },
775 })
776 };
777 }
778 if abs_query != base_query {
779 return Ok(IriRef {
780 iri: abs.0[abs.0.positions.path_end..].to_string(),
781 positions: IriElementsPositions {
782 scheme_end: 0,
783 authority_end: 0,
784 path_end: 0,
785 query_end: abs.0.positions.query_end - abs.0.positions.path_end,
786 },
787 });
788 }
789 Ok(IriRef {
790 iri: abs.0[abs.0.positions.query_end..].to_string(),
791 positions: IriElementsPositions {
792 scheme_end: 0,
793 authority_end: 0,
794 path_end: 0,
795 query_end: 0,
796 },
797 })
798 }
799
800 #[inline]
802 pub fn as_ref(&self) -> Iri<&str> {
803 Iri(self.0.as_ref())
804 }
805
806 #[inline]
816 pub fn as_str(&self) -> &str {
817 self.0.as_str()
818 }
819
820 #[inline]
830 pub fn into_inner(self) -> T {
831 self.0.into_inner()
832 }
833
834 #[inline]
846 pub fn scheme(&self) -> &str {
847 self.0.scheme().expect("The IRI should be absolute")
848 }
849
850 #[inline]
865 pub fn authority(&self) -> Option<&str> {
866 self.0.authority()
867 }
868
869 #[inline]
882 pub fn path(&self) -> &str {
883 self.0.path()
884 }
885
886 #[inline]
896 pub fn query(&self) -> Option<&str> {
897 self.0.query()
898 }
899
900 #[inline]
910 pub fn fragment(&self) -> Option<&str> {
911 self.0.fragment()
912 }
913}
914
915impl<Lft: PartialEq<Rhs>, Rhs> PartialEq<Iri<Rhs>> for Iri<Lft> {
916 #[inline]
917 fn eq(&self, other: &Iri<Rhs>) -> bool {
918 self.0.eq(&other.0)
919 }
920}
921
922impl<Lft: PartialEq<Rhs>, Rhs> PartialEq<IriRef<Rhs>> for Iri<Lft> {
923 #[inline]
924 fn eq(&self, other: &IriRef<Rhs>) -> bool {
925 self.0.eq(other)
926 }
927}
928
929impl<Lft: PartialEq<Rhs>, Rhs> PartialEq<Iri<Rhs>> for IriRef<Lft> {
930 #[inline]
931 fn eq(&self, other: &Iri<Rhs>) -> bool {
932 self.eq(&other.0)
933 }
934}
935
936impl<T: PartialEq<str>> PartialEq<str> for Iri<T> {
937 #[inline]
938 fn eq(&self, other: &str) -> bool {
939 self.0.eq(other)
940 }
941}
942
943impl<'a, T: PartialEq<&'a str>> PartialEq<&'a str> for Iri<T> {
944 #[inline]
945 fn eq(&self, other: &&'a str) -> bool {
946 self.0.eq(other)
947 }
948}
949
950impl<T: PartialEq<String>> PartialEq<String> for Iri<T> {
951 #[inline]
952 fn eq(&self, other: &String) -> bool {
953 self.0.eq(other)
954 }
955}
956
957impl<'a, T: PartialEq<Cow<'a, str>>> PartialEq<Cow<'a, str>> for Iri<T> {
958 #[inline]
959 fn eq(&self, other: &Cow<'a, str>) -> bool {
960 self.0.eq(other)
961 }
962}
963
964impl<T: PartialEq<str>> PartialEq<Iri<T>> for str {
965 #[inline]
966 fn eq(&self, other: &Iri<T>) -> bool {
967 self.eq(&other.0)
968 }
969}
970
971impl<'a, T: PartialEq<&'a str>> PartialEq<Iri<T>> for &'a str {
972 #[inline]
973 fn eq(&self, other: &Iri<T>) -> bool {
974 self.eq(&other.0)
975 }
976}
977
978impl<T: PartialEq<String>> PartialEq<Iri<T>> for String {
979 #[inline]
980 fn eq(&self, other: &Iri<T>) -> bool {
981 self.eq(&other.0)
982 }
983}
984
985impl<'a, T: PartialEq<Cow<'a, str>>> PartialEq<Iri<T>> for Cow<'a, str> {
986 #[inline]
987 fn eq(&self, other: &Iri<T>) -> bool {
988 self.eq(&other.0)
989 }
990}
991
992impl<T: Eq> Eq for Iri<T> {}
993
994impl<T: Hash> Hash for Iri<T> {
995 #[inline]
996 fn hash<H: Hasher>(&self, state: &mut H) {
997 self.0.hash(state)
998 }
999}
1000
1001impl<Lft: PartialOrd<Rhs>, Rhs> PartialOrd<Iri<Rhs>> for Iri<Lft> {
1002 #[inline]
1003 fn partial_cmp(&self, other: &Iri<Rhs>) -> Option<Ordering> {
1004 self.0.partial_cmp(&other.0)
1005 }
1006}
1007
1008impl<Lft: PartialOrd<Rhs>, Rhs> PartialOrd<IriRef<Rhs>> for Iri<Lft> {
1009 #[inline]
1010 fn partial_cmp(&self, other: &IriRef<Rhs>) -> Option<Ordering> {
1011 self.0.partial_cmp(other)
1012 }
1013}
1014
1015impl<Lft: PartialOrd<Rhs>, Rhs> PartialOrd<Iri<Rhs>> for IriRef<Lft> {
1016 #[inline]
1017 fn partial_cmp(&self, other: &Iri<Rhs>) -> Option<Ordering> {
1018 self.partial_cmp(&other.0)
1019 }
1020}
1021
1022impl<T: Ord> Ord for Iri<T> {
1023 #[inline]
1024 fn cmp(&self, other: &Self) -> Ordering {
1025 self.0.cmp(&other.0)
1026 }
1027}
1028
1029impl<T: Deref<Target = str>> Deref for Iri<T> {
1030 type Target = str;
1031
1032 #[inline]
1033 fn deref(&self) -> &str {
1034 self.0.deref()
1035 }
1036}
1037
1038impl<T: AsRef<str>> AsRef<str> for Iri<T> {
1039 #[inline]
1040 fn as_ref(&self) -> &str {
1041 self.0.as_ref()
1042 }
1043}
1044
1045impl<T: Borrow<str>> Borrow<str> for Iri<T> {
1046 #[inline]
1047 fn borrow(&self) -> &str {
1048 self.0.borrow()
1049 }
1050}
1051
1052impl<T: fmt::Debug> fmt::Debug for Iri<T> {
1053 #[inline]
1054 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1055 self.0.fmt(f)
1056 }
1057}
1058
1059impl<T: fmt::Display> fmt::Display for Iri<T> {
1060 #[inline]
1061 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1062 self.0.fmt(f)
1063 }
1064}
1065
1066impl FromStr for Iri<String> {
1067 type Err = IriParseError;
1068
1069 #[inline]
1070 fn from_str(iri: &str) -> Result<Self, IriParseError> {
1071 Self::parse(iri.to_owned())
1072 }
1073}
1074
1075impl<'a> From<Iri<&'a str>> for Iri<String> {
1076 #[inline]
1077 fn from(iri: Iri<&'a str>) -> Self {
1078 Self(iri.0.into())
1079 }
1080}
1081
1082impl<'a> From<Iri<Cow<'a, str>>> for Iri<String> {
1083 #[inline]
1084 fn from(iri: Iri<Cow<'a, str>>) -> Self {
1085 Self(iri.0.into())
1086 }
1087}
1088
1089impl From<Iri<Box<str>>> for Iri<String> {
1090 #[inline]
1091 fn from(iri: Iri<Box<str>>) -> Self {
1092 Self(iri.0.into())
1093 }
1094}
1095
1096impl<'a> From<Iri<&'a str>> for Iri<Cow<'a, str>> {
1097 #[inline]
1098 fn from(iri: Iri<&'a str>) -> Self {
1099 Self(iri.0.into())
1100 }
1101}
1102
1103impl From<Iri<String>> for Iri<Cow<'_, str>> {
1104 #[inline]
1105 fn from(iri: Iri<String>) -> Self {
1106 Self(iri.0.into())
1107 }
1108}
1109
1110impl<'a> From<&'a Iri<String>> for Iri<&'a str> {
1111 #[inline]
1112 fn from(iri: &'a Iri<String>) -> Self {
1113 Self(iri.0.as_ref())
1114 }
1115}
1116
1117impl<'a> From<&'a Iri<Cow<'a, str>>> for Iri<&'a str> {
1118 #[inline]
1119 fn from(iri: &'a Iri<Cow<'a, str>>) -> Self {
1120 Self(iri.0.as_ref())
1121 }
1122}
1123
1124impl<T: Deref<Target = str>> From<Iri<T>> for IriRef<T> {
1125 fn from(iri: Iri<T>) -> Self {
1126 iri.0
1127 }
1128}
1129
1130impl<T: Deref<Target = str>> TryFrom<IriRef<T>> for Iri<T> {
1131 type Error = IriParseError;
1132
1133 fn try_from(iri: IriRef<T>) -> Result<Self, IriParseError> {
1134 if iri.is_absolute() {
1135 Ok(Self(iri))
1136 } else {
1137 Err(IriParseError {
1138 kind: IriParseErrorKind::NoScheme,
1139 })
1140 }
1141 }
1142}
1143
1144#[cfg(feature = "serde")]
1145impl<T: Serialize> Serialize for Iri<T> {
1146 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
1147 self.0.serialize(serializer)
1148 }
1149}
1150
1151#[cfg(feature = "serde")]
1152impl<'de, T: Deref<Target = str> + Deserialize<'de>> Deserialize<'de> for Iri<T> {
1153 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
1154 use serde::de::Error;
1155 IriRef::deserialize(deserializer)?
1156 .try_into()
1157 .map_err(Error::custom)
1158 }
1159}
1160
1161#[derive(Debug)]
1163pub struct IriParseError {
1164 kind: IriParseErrorKind,
1165}
1166
1167impl fmt::Display for IriParseError {
1168 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1169 match &self.kind {
1170 IriParseErrorKind::NoScheme => write!(f, "No scheme found in an absolute IRI"),
1171 IriParseErrorKind::InvalidHostCharacter(c) => {
1172 write!(f, "Invalid character '{c}' in host")
1173 }
1174 IriParseErrorKind::InvalidHostIp(e) => write!(f, "Invalid host IP ({e})"),
1175 IriParseErrorKind::InvalidPortCharacter(c) => write!(f, "Invalid character '{c}'"),
1176 IriParseErrorKind::InvalidIriCodePoint(c) => {
1177 write!(f, "Invalid IRI code point '{c}'")
1178 }
1179 IriParseErrorKind::InvalidPercentEncoding(cs) => write!(
1180 f,
1181 "Invalid IRI percent encoding '{}'",
1182 cs.iter().flatten().cloned().collect::<String>()
1183 ),
1184 IriParseErrorKind::PathStartingWithTwoSlashes => {
1185 write!(f, "An IRI path is not allowed to start with //")
1186 }
1187 }
1188 }
1189}
1190
1191impl Error for IriParseError {
1192 fn source(&self) -> Option<&(dyn Error + 'static)> {
1193 if let IriParseErrorKind::InvalidHostIp(e) = &self.kind {
1194 Some(e)
1195 } else {
1196 None
1197 }
1198 }
1199}
1200
1201#[derive(Debug)]
1202enum IriParseErrorKind {
1203 NoScheme,
1204 InvalidHostCharacter(char),
1205 InvalidHostIp(AddrParseError),
1206 InvalidPortCharacter(char),
1207 InvalidIriCodePoint(char),
1208 InvalidPercentEncoding([Option<char>; 3]),
1209 PathStartingWithTwoSlashes,
1210}
1211
1212#[derive(Debug)]
1217pub struct IriRelativizeError {}
1218
1219impl fmt::Display for IriRelativizeError {
1220 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1221 write!(
1222 f,
1223 "It is not possible to make this IRI relative because it contains `/..` or `/.`"
1224 )
1225 }
1226}
1227
1228impl Error for IriRelativizeError {}
1229
1230#[derive(Debug, Clone, Copy)]
1231struct IriElementsPositions {
1232 scheme_end: usize,
1233 authority_end: usize,
1234 path_end: usize,
1235 query_end: usize,
1236}
1237
1238trait OutputBuffer {
1239 fn push(&mut self, c: char);
1240
1241 fn push_str(&mut self, s: &str);
1242
1243 fn clear(&mut self);
1244
1245 fn truncate(&mut self, new_len: usize);
1246
1247 fn len(&self) -> usize;
1248
1249 fn as_str(&self) -> &str;
1250}
1251
1252#[derive(Default)]
1253struct VoidOutputBuffer {
1254 len: usize,
1255}
1256
1257impl OutputBuffer for VoidOutputBuffer {
1258 #[inline]
1259 fn push(&mut self, c: char) {
1260 self.len += c.len_utf8();
1261 }
1262
1263 #[inline]
1264 fn push_str(&mut self, s: &str) {
1265 self.len += s.len();
1266 }
1267
1268 #[inline]
1269 fn clear(&mut self) {
1270 self.len = 0;
1271 }
1272
1273 #[inline]
1274 fn truncate(&mut self, new_len: usize) {
1275 self.len = new_len;
1276 }
1277
1278 #[inline]
1279 fn len(&self) -> usize {
1280 self.len
1281 }
1282
1283 #[inline]
1284 fn as_str(&self) -> &str {
1285 ""
1286 }
1287}
1288
1289impl OutputBuffer for String {
1290 #[inline]
1291 fn push(&mut self, c: char) {
1292 self.push(c);
1293 }
1294
1295 #[inline]
1296 fn push_str(&mut self, s: &str) {
1297 self.push_str(s);
1298 }
1299
1300 #[inline]
1301 fn clear(&mut self) {
1302 self.clear();
1303 }
1304
1305 #[inline]
1306 fn truncate(&mut self, new_len: usize) {
1307 self.truncate(new_len);
1308 }
1309
1310 #[inline]
1311 fn len(&self) -> usize {
1312 self.len()
1313 }
1314
1315 #[inline]
1316 fn as_str(&self) -> &str {
1317 self.as_str()
1318 }
1319}
1320
1321struct ParserInput<'a> {
1322 value: Chars<'a>,
1323 position: usize,
1324}
1325
1326impl ParserInput<'_> {
1327 #[inline]
1328 fn next(&mut self) -> Option<char> {
1329 if let Some(head) = self.value.next() {
1330 self.position += head.len_utf8();
1331 Some(head)
1332 } else {
1333 None
1334 }
1335 }
1336
1337 #[inline]
1338 fn front(&self) -> Option<char> {
1339 self.value.clone().next()
1340 }
1341
1342 #[inline]
1343 fn starts_with(&self, c: char) -> bool {
1344 self.value.as_str().starts_with(c)
1345 }
1346}
1347
1348struct IriParser<'a, O: OutputBuffer, const UNCHECKED: bool> {
1352 iri: &'a str,
1353 base: Option<IriRef<&'a str>>,
1354 input: ParserInput<'a>,
1355 output: &'a mut O,
1356 output_positions: IriElementsPositions,
1357 input_scheme_end: usize,
1358}
1359
1360impl<'a, O: OutputBuffer, const UNCHECKED: bool> IriParser<'a, O, UNCHECKED> {
1361 fn parse(
1362 iri: &'a str,
1363 base: Option<IriRef<&'a str>>,
1364 output: &'a mut O,
1365 ) -> Result<IriElementsPositions, IriParseError> {
1366 let mut parser = Self {
1367 iri,
1368 base,
1369 input: ParserInput {
1370 value: iri.chars(),
1371 position: 0,
1372 },
1373 output,
1374 output_positions: IriElementsPositions {
1375 scheme_end: 0,
1376 authority_end: 0,
1377 path_end: 0,
1378 query_end: 0,
1379 },
1380 input_scheme_end: 0,
1381 };
1382 parser.parse_scheme_start()?;
1383 Ok(parser.output_positions)
1384 }
1385
1386 fn parse_scheme_start(&mut self) -> Result<(), IriParseError> {
1387 match self.input.front() {
1388 Some(':') => {
1389 if UNCHECKED {
1390 self.parse_scheme()
1391 } else {
1392 self.parse_error(IriParseErrorKind::NoScheme)
1393 }
1394 }
1395 Some(c) if c.is_ascii_alphabetic() => self.parse_scheme(),
1396 _ => self.parse_relative(),
1397 }
1398 }
1399
1400 fn parse_scheme(&mut self) -> Result<(), IriParseError> {
1401 loop {
1402 let c = self.input.next();
1403 match c {
1404 Some(c) if c.is_ascii_alphanumeric() || c == '+' || c == '-' || c == '.' => {
1405 self.output.push(c)
1406 }
1407 Some(':') => {
1408 self.output.push(':');
1409 self.output_positions.scheme_end = self.output.len();
1410 self.input_scheme_end = self.input.position;
1411 return if self.input.starts_with('/') {
1412 self.input.next();
1413 self.output.push('/');
1414 self.parse_path_or_authority()
1415 } else {
1416 self.output_positions.authority_end = self.output.len();
1417 self.parse_path::<false>()
1418 };
1419 }
1420 _ => {
1421 self.input = ParserInput {
1422 value: self.iri.chars(),
1423 position: 0,
1424 }; self.output.clear();
1426 return self.parse_relative();
1427 }
1428 }
1429 }
1430 }
1431
1432 fn parse_path_or_authority(&mut self) -> Result<(), IriParseError> {
1433 if self.input.starts_with('/') {
1434 self.input.next();
1435 self.output.push('/');
1436 self.parse_authority()
1437 } else {
1438 self.output_positions.authority_end = self.output.len() - 1;
1439 self.parse_path::<false>()
1440 }
1441 }
1442
1443 fn parse_relative(&mut self) -> Result<(), IriParseError> {
1444 if let Some(base) = self.base {
1445 match self.input.front() {
1446 None => {
1447 self.output.push_str(&base.iri[..base.positions.query_end]);
1448 self.output_positions.scheme_end = base.positions.scheme_end;
1449 self.output_positions.authority_end = base.positions.authority_end;
1450 self.output_positions.path_end = base.positions.path_end;
1451 self.output_positions.query_end = base.positions.query_end;
1452 Ok(())
1453 }
1454 Some('/') => {
1455 self.input.next();
1456 self.parse_relative_slash(&base)
1457 }
1458 Some('?') => {
1459 self.input.next();
1460 self.output.push_str(&base.iri[..base.positions.path_end]);
1461 self.output.push('?');
1462 self.output_positions.scheme_end = base.positions.scheme_end;
1463 self.output_positions.authority_end = base.positions.authority_end;
1464 self.output_positions.path_end = base.positions.path_end;
1465 self.parse_query()
1466 }
1467 Some('#') => {
1468 self.input.next();
1469 self.output.push_str(&base.iri[..base.positions.query_end]);
1470 self.output_positions.scheme_end = base.positions.scheme_end;
1471 self.output_positions.authority_end = base.positions.authority_end;
1472 self.output_positions.path_end = base.positions.path_end;
1473 self.output_positions.query_end = base.positions.query_end;
1474 self.output.push('#');
1475 self.parse_fragment()
1476 }
1477 _ => {
1478 self.output.push_str(&base.iri[..base.positions.path_end]);
1479 self.output_positions.scheme_end = base.positions.scheme_end;
1480 self.output_positions.authority_end = base.positions.authority_end;
1481 self.output_positions.path_end = base.positions.path_end;
1482 self.remove_last_segment();
1483 self.parse_relative_path::<true>()
1484 }
1485 }
1486 } else {
1487 self.output_positions.scheme_end = 0;
1488 self.input_scheme_end = 0;
1489 if self.input.starts_with('/') {
1490 self.input.next();
1491 self.output.push('/');
1492 self.parse_path_or_authority()
1493 } else {
1494 self.output_positions.authority_end = 0;
1495 self.parse_relative_path::<false>()
1496 }
1497 }
1498 }
1499
1500 fn parse_relative_path<const REMOVE_DOT_SEGMENTS: bool>(
1501 &mut self,
1502 ) -> Result<(), IriParseError> {
1503 while let Some(c) = self.input.front() {
1504 if matches!(c, '/' | '?' | '#') {
1505 break;
1506 }
1507 self.input.next();
1508 self.read_url_codepoint_or_echar(c, |c| is_iunreserved_or_sub_delims(c) || c == '@')?;
1509 }
1510 self.parse_path::<REMOVE_DOT_SEGMENTS>()
1511 }
1512
1513 fn parse_relative_slash(&mut self, base: &IriRef<&'a str>) -> Result<(), IriParseError> {
1514 if self.input.starts_with('/') {
1515 self.input.next();
1516 self.output.push_str(&base.iri[..base.positions.scheme_end]);
1517 self.output_positions.scheme_end = base.positions.scheme_end;
1518 self.output.push('/');
1519 self.output.push('/');
1520 self.parse_authority()
1521 } else {
1522 self.output
1523 .push_str(&base.iri[..base.positions.authority_end]);
1524 self.output.push('/');
1525 self.output_positions.scheme_end = base.positions.scheme_end;
1526 self.output_positions.authority_end = base.positions.authority_end;
1527 self.parse_path::<true>()
1528 }
1529 }
1530
1531 fn parse_authority(&mut self) -> Result<(), IriParseError> {
1532 loop {
1534 let c = self.input.next();
1535 match c {
1536 Some('@') => {
1537 self.output.push('@');
1538 return self.parse_host();
1539 }
1540 None | Some('[') | Some('/') | Some('?') | Some('#') => {
1541 self.input = ParserInput {
1542 value: self.iri[self.input_scheme_end + 2..].chars(),
1543 position: self.input_scheme_end + 2,
1544 };
1545 self.output.truncate(self.output_positions.scheme_end + 2);
1546 return self.parse_host();
1547 }
1548 Some(c) => {
1549 self.read_url_codepoint_or_echar(c, |c| {
1550 is_iunreserved_or_sub_delims(c) || c == ':'
1551 })?;
1552 }
1553 }
1554 }
1555 }
1556
1557 fn parse_host(&mut self) -> Result<(), IriParseError> {
1558 if self.input.starts_with('[') {
1559 let start_position = self.input.position;
1561 while let Some(c) = self.input.next() {
1562 self.output.push(c);
1563 if c == ']' {
1564 let ip = &self.iri[start_position + 1..self.input.position - 1];
1565 if !UNCHECKED {
1566 if ip.starts_with('v') || ip.starts_with('V') {
1567 self.validate_ip_v_future(ip)?;
1568 } else if let Err(error) = Ipv6Addr::from_str(ip) {
1569 return self.parse_error(IriParseErrorKind::InvalidHostIp(error));
1570 }
1571 }
1572
1573 let c = self.input.next();
1574 return match c {
1575 Some(':') => {
1576 self.output.push(':');
1577 self.parse_port()
1578 }
1579 None | Some('/') | Some('?') | Some('#') => {
1580 self.output_positions.authority_end = self.output.len();
1581 self.parse_path_start(c)
1582 }
1583 Some(c) => {
1584 if UNCHECKED {
1585 self.output.push(c);
1586 continue;
1587 } else {
1588 self.parse_error(IriParseErrorKind::InvalidHostCharacter(c))
1589 }
1590 }
1591 };
1592 }
1593 }
1594 if UNCHECKED {
1595 self.output_positions.authority_end = self.output.len();
1597 self.parse_path_start(None)
1598 } else {
1599 self.parse_error(IriParseErrorKind::InvalidHostCharacter('['))
1600 }
1601 } else {
1602 loop {
1604 let c = self.input.next();
1605 match c {
1606 Some(':') => {
1607 self.output.push(':');
1608 return self.parse_port();
1609 }
1610 None | Some('/') | Some('?') | Some('#') => {
1611 self.output_positions.authority_end = self.output.len();
1612 return self.parse_path_start(c);
1613 }
1614 Some(c) => self.read_url_codepoint_or_echar(c, is_iunreserved_or_sub_delims)?,
1615 }
1616 }
1617 }
1618 }
1619
1620 fn parse_port(&mut self) -> Result<(), IriParseError> {
1621 loop {
1622 let c = self.input.next();
1623 match c {
1624 Some('/') | Some('?') | Some('#') | None => {
1625 self.output_positions.authority_end = self.output.len();
1626 return self.parse_path_start(c);
1627 }
1628 Some(c) => {
1629 if UNCHECKED || c.is_ascii_digit() {
1630 self.output.push(c)
1631 } else {
1632 return self.parse_error(IriParseErrorKind::InvalidPortCharacter(c));
1633 }
1634 }
1635 }
1636 }
1637 }
1638
1639 fn parse_path_start(&mut self, c: Option<char>) -> Result<(), IriParseError> {
1640 match c {
1641 None => {
1642 self.output_positions.path_end = self.output.len();
1643 self.output_positions.query_end = self.output.len();
1644 Ok(())
1645 }
1646 Some('?') => {
1647 self.output_positions.path_end = self.output.len();
1648 self.output.push('?');
1649 self.parse_query()
1650 }
1651 Some('#') => {
1652 self.output_positions.path_end = self.output.len();
1653 self.output_positions.query_end = self.output.len();
1654 self.output.push('#');
1655 self.parse_fragment()
1656 }
1657 Some('/') => {
1658 self.output.push('/');
1659 self.parse_path::<false>()
1660 }
1661 Some(c) => {
1662 self.read_url_codepoint_or_echar(c, |c| {
1663 is_iunreserved_or_sub_delims(c) || matches!(c, ':' | '@')
1664 })?;
1665 self.parse_path::<false>()
1666 }
1667 }
1668 }
1669
1670 fn parse_path<const REMOVE_DOT_SEGMENTS: bool>(&mut self) -> Result<(), IriParseError> {
1671 loop {
1672 let c = self.input.next();
1673 match c {
1674 None | Some('/') | Some('?') | Some('#') => {
1675 let output_str = self.output.as_str();
1676 if REMOVE_DOT_SEGMENTS {
1677 let output_path = &output_str[self.output_positions.authority_end..];
1678 if output_path.ends_with("/..") {
1679 self.output.truncate(self.output.len() - 3);
1680 self.remove_last_segment();
1681 } else if output_path.ends_with("/.") || output_path == "." {
1682 self.output.truncate(self.output.len() - 1);
1683 } else if output_path == ".." {
1684 self.output.truncate(self.output.len() - 2);
1685 } else if c == Some('/') {
1686 self.output.push('/');
1687 continue;
1688 }
1689 } else if c == Some('/') {
1690 self.output.push('/');
1691 continue;
1692 }
1693
1694 let output_str = self.output.as_str();
1697 if REMOVE_DOT_SEGMENTS
1698 && !UNCHECKED
1699 && output_str[self.output_positions.authority_end..].starts_with("//")
1700 && self.output_positions.authority_end == self.output_positions.scheme_end
1701 {
1702 return self.parse_error(IriParseErrorKind::PathStartingWithTwoSlashes);
1703 }
1704
1705 if c == Some('?') {
1706 self.output_positions.path_end = self.output.len();
1707 self.output.push('?');
1708 return self.parse_query();
1709 } else if c == Some('#') {
1710 self.output_positions.path_end = self.output.len();
1711 self.output_positions.query_end = self.output.len();
1712 self.output.push('#');
1713 return self.parse_fragment();
1714 } else if c.is_none() {
1715 self.output_positions.path_end = self.output.len();
1716 self.output_positions.query_end = self.output.len();
1717 return Ok(());
1718 }
1719 }
1720 Some(c) => self.read_url_codepoint_or_echar(c, |c| {
1721 is_iunreserved_or_sub_delims(c) || matches!(c, ':' | '@')
1722 })?,
1723 }
1724 }
1725 }
1726
1727 fn parse_query(&mut self) -> Result<(), IriParseError> {
1728 while let Some(c) = self.input.next() {
1729 if c == '#' {
1730 self.output_positions.query_end = self.output.len();
1731 self.output.push('#');
1732 return self.parse_fragment();
1733 } else {
1734 self.read_url_codepoint_or_echar(c, |c| {
1735 is_iunreserved_or_sub_delims(c) || matches!(c, ':' | '@' | '/' | '?' | '\u{E000}'..='\u{F8FF}' | '\u{F0000}'..='\u{FFFFD}' | '\u{100000}'..='\u{10FFFD}')
1736 })?
1737 }
1738 }
1739 self.output_positions.query_end = self.output.len();
1740 Ok(())
1741 }
1742
1743 fn parse_fragment(&mut self) -> Result<(), IriParseError> {
1744 while let Some(c) = self.input.next() {
1745 self.read_url_codepoint_or_echar(c, |c| {
1746 is_iunreserved_or_sub_delims(c) || matches!(c, ':' | '@' | '/' | '?')
1747 })?;
1748 }
1749 Ok(())
1750 }
1751
1752 fn remove_last_segment(&mut self) {
1753 if let Some(last_slash_position) =
1754 self.output.as_str()[self.output_positions.authority_end..].rfind('/')
1755 {
1756 self.output
1757 .truncate(last_slash_position + self.output_positions.authority_end);
1758 self.output.push('/');
1759 } else {
1760 self.output.truncate(self.output_positions.authority_end);
1761 if self.output_positions.authority_end > self.output_positions.scheme_end {
1762 self.output.push('/');
1763 }
1764 }
1765 }
1766
1767 fn read_url_codepoint_or_echar(
1768 &mut self,
1769 c: char,
1770 valid: impl Fn(char) -> bool,
1771 ) -> Result<(), IriParseError> {
1772 if UNCHECKED || valid(c) {
1773 self.output.push(c);
1774 Ok(())
1775 } else if c == '%' {
1776 self.read_echar()
1777 } else {
1778 self.parse_error(IriParseErrorKind::InvalidIriCodePoint(c))
1779 }
1780 }
1781
1782 fn read_echar(&mut self) -> Result<(), IriParseError> {
1783 let c1 = self.input.next();
1784 let c2 = self.input.next();
1785 if c1.map_or(false, |c| c.is_ascii_hexdigit())
1786 && c2.map_or(false, |c| c.is_ascii_hexdigit())
1787 {
1788 self.output.push('%');
1789 self.output.push(c1.unwrap());
1790 self.output.push(c2.unwrap());
1791 Ok(())
1792 } else {
1793 self.parse_error(IriParseErrorKind::InvalidPercentEncoding([
1794 Some('%'),
1795 c1,
1796 c2,
1797 ]))
1798 }
1799 }
1800
1801 fn parse_error<T>(&self, kind: IriParseErrorKind) -> Result<T, IriParseError> {
1802 Err(IriParseError { kind })
1803 }
1804
1805 fn validate_ip_v_future(&self, ip: &str) -> Result<(), IriParseError> {
1807 let mut chars = ip.chars();
1808
1809 let c = chars.next().ok_or(IriParseError {
1810 kind: IriParseErrorKind::InvalidHostCharacter(']'),
1811 })?;
1812 if !matches!(c, 'v' | 'V') {
1813 return self.parse_error(IriParseErrorKind::InvalidHostCharacter(c));
1814 };
1815
1816 let mut with_a_version = false;
1817 for c in &mut chars {
1818 if c == '.' {
1819 break;
1820 } else if c.is_ascii_hexdigit() {
1821 with_a_version = true;
1822 } else {
1823 return self.parse_error(IriParseErrorKind::InvalidHostCharacter(c));
1824 }
1825 }
1826 if !with_a_version {
1827 return self.parse_error(IriParseErrorKind::InvalidHostCharacter(
1828 chars.next().unwrap_or(']'),
1829 ));
1830 }
1831
1832 if chars.as_str().is_empty() {
1833 return self.parse_error(IriParseErrorKind::InvalidHostCharacter(']'));
1834 };
1835 for c in chars {
1836 if !is_unreserved_or_sub_delims(c) && c != ':' {
1837 return self.parse_error(IriParseErrorKind::InvalidHostCharacter(c));
1838 }
1839 }
1840
1841 Ok(())
1842 }
1843}
1844
1845fn is_iunreserved_or_sub_delims(c: char) -> bool {
1846 matches!(c,
1847 'a'..='z'
1848 | 'A'..='Z'
1849 | '0'..='9'
1850 | '!'
1851 | '$'
1852 | '&'
1853 | '\''
1854 | '('
1855 | ')'
1856 | '*'
1857 | '+'
1858 | ','
1859 | '-'
1860 | '.'
1861 | ';'
1862 | '='
1863 | '_'
1864 | '~'
1865 | '\u{A0}'..='\u{D7FF}'
1866 | '\u{F900}'..='\u{FDCF}'
1867 | '\u{FDF0}'..='\u{FFEF}'
1868 | '\u{10000}'..='\u{1FFFD}'
1869 | '\u{20000}'..='\u{2FFFD}'
1870 | '\u{30000}'..='\u{3FFFD}'
1871 | '\u{40000}'..='\u{4FFFD}'
1872 | '\u{50000}'..='\u{5FFFD}'
1873 | '\u{60000}'..='\u{6FFFD}'
1874 | '\u{70000}'..='\u{7FFFD}'
1875 | '\u{80000}'..='\u{8FFFD}'
1876 | '\u{90000}'..='\u{9FFFD}'
1877 | '\u{A0000}'..='\u{AFFFD}'
1878 | '\u{B0000}'..='\u{BFFFD}'
1879 | '\u{C0000}'..='\u{CFFFD}'
1880 | '\u{D0000}'..='\u{DFFFD}'
1881 | '\u{E1000}'..='\u{EFFFD}'
1882 )
1883}
1884
1885fn is_unreserved_or_sub_delims(c: char) -> bool {
1886 matches!(c,
1887 'a'..='z'
1888 | 'A'..='Z'
1889 | '0'..='9'
1890 | '!'
1891 | '$'
1892 | '&'
1893 | '\''
1894 | '('
1895 | ')'
1896 | '*'
1897 | '+'
1898 | ','
1899 | '-'
1900 | '.'
1901 | ';'
1902 | '='
1903 | '_'
1904 | '~'
1905 )
1906}