1#![allow(missing_debug_implementations)]
2
3use crate::{
4 component::{Authority, IAuthority, Scheme},
5 convert::ConvertError,
6 parse::{self, ParseError},
7 pct_enc::{encoder::*, EStr, Encoder},
8};
9use borrow_or_share::{BorrowOrShare, Bos};
10use core::{borrow::Borrow, cmp::Ordering, fmt, hash, num::NonZeroUsize, str};
11
12#[cfg(feature = "alloc")]
13use crate::{
14 build::{
15 state::{NonRefStart, Start},
16 Builder,
17 },
18 normalize::Normalizer,
19 resolve::{self, ResolveError},
20};
21#[cfg(feature = "alloc")]
22use alloc::{borrow::ToOwned, string::String};
23#[cfg(feature = "alloc")]
24use core::str::FromStr;
25
26#[cfg(feature = "net")]
27use crate::net::{Ipv4Addr, Ipv6Addr};
28
29#[cfg(feature = "serde")]
30use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
31
32pub trait Value: Default {}
33
34impl Value for &str {}
35
36#[cfg(feature = "alloc")]
37impl Value for String {}
38
39pub struct Constraints {
40 pub ascii_only: bool,
41 pub scheme_required: bool,
42}
43
44pub trait RiMaybeRef: Sized {
45 type Val;
46 type WithVal<T>: RiMaybeRef<Val = T>;
47
48 type UserinfoE: Encoder;
49 type RegNameE: Encoder;
50 type PathE: Encoder;
51 type QueryE: Encoder;
52 type FragmentE: Encoder;
53
54 const CONSTRAINTS: Constraints;
55
56 fn new(val: Self::Val, meta: Meta) -> Self;
57
58 fn from_pair((val, meta): (Self::Val, Meta)) -> Self {
59 Self::new(val, meta)
60 }
61
62 fn make_ref<'i, 'o>(&'i self) -> RmrRef<'o, 'i>
63 where
64 Self::Val: BorrowOrShare<'i, 'o, str>;
65}
66
67#[cfg(feature = "alloc")]
68pub trait Ri: RiMaybeRef {
69 type Ref<T>: RiMaybeRef<Val = T>;
70}
71
72pub trait Parse {
73 type Val;
74 type Err;
75
76 fn parse<R: RiMaybeRef<Val = Self::Val>>(self) -> Result<R, Self::Err>;
77}
78
79impl<'a> Parse for &'a str {
80 type Val = &'a str;
81 type Err = ParseError;
82
83 fn parse<R: RiMaybeRef<Val = Self::Val>>(self) -> Result<R, Self::Err> {
84 parse::parse(self.as_bytes(), R::CONSTRAINTS).map(|meta| R::new(self, meta))
85 }
86}
87
88#[cfg(feature = "alloc")]
89impl Parse for String {
90 type Val = Self;
91 type Err = (ParseError, Self);
92
93 fn parse<R: RiMaybeRef<Val = Self::Val>>(self) -> Result<R, Self::Err> {
94 match parse::parse(self.as_bytes(), R::CONSTRAINTS) {
95 Ok(meta) => Ok(R::new(self, meta)),
96 Err(e) => Err((e, self)),
97 }
98 }
99}
100
101#[derive(Clone, Copy, Default)]
102pub struct Meta {
103 pub scheme_end: Option<NonZeroUsize>,
105 pub auth_meta: Option<AuthMeta>,
106 pub path_bounds: (usize, usize),
107 pub query_end: Option<NonZeroUsize>,
109}
110
111impl Meta {
112 #[inline]
113 pub fn query_or_path_end(&self) -> usize {
114 self.query_end.map_or(self.path_bounds.1, |i| i.get())
115 }
116}
117
118#[derive(Clone, Copy, Default)]
119pub struct AuthMeta {
120 pub host_bounds: (usize, usize),
121 pub host_meta: HostMeta,
122}
123
124impl AuthMeta {
125 pub const EMPTY: Self = Self {
126 host_bounds: (0, 0),
127 host_meta: HostMeta::RegName,
128 };
129}
130
131#[derive(Clone, Copy, Default)]
132pub enum HostMeta {
133 Ipv4(#[cfg(feature = "net")] Ipv4Addr),
134 Ipv6(#[cfg(feature = "net")] Ipv6Addr),
135 IpvFuture,
136 #[default]
137 RegName,
138}
139
140pub trait PathEncoder: Encoder {}
141
142impl PathEncoder for Path {}
143impl PathEncoder for IPath {}
144
145macro_rules! cond {
146 (if true { $($then:tt)* } else { $($else:tt)* }) => { $($then)* };
147 (if false { $($then:tt)* } else { $($else:tt)* }) => { $($else)* };
148}
149
150macro_rules! ri_maybe_ref {
151 (
152 Type = $Ty:ident,
153 type_name = $ty:literal,
154 variable_name = $var:literal,
155 name = $name:literal,
156 indefinite_article = $art:literal,
157 description = $desc:literal,
158 ascii_only = $ascii_only:literal,
159 scheme_required = $scheme_required:tt,
160 rfc = $rfc:literal,
161 abnf_rule = ($abnf:literal, $abnf_link:literal),
162 $(
163 NonRefType = $NonRefTy:ident,
164 non_ref_name = $nr_name:literal,
165 non_ref_link = $nr_link:literal,
166 abnf_rule_absolute = ($abnf_abs:literal, $abnf_abs_link:literal),
167 )?
168 $(
169 RefType = $RefTy:ident,
170 ref_name = $ref_name:literal,
171 )?
172 AuthorityType = $Authority:ident,
173 UserinfoEncoderType = $UserinfoE:ident,
174 RegNameEncoderType = $RegNameE:ident,
175 PathEncoderType = $PathE:ident,
176 QueryEncoderType = $QueryE:ident,
177 FragmentEncoderType = $FragmentE:ident,
178 ) => {
179 #[doc = $desc]
180 #[doc = concat!("Two variants of `", $ty, "` are available: ")]
186 #[doc = concat!("`", $ty, "<&str>` (borrowed) and `", $ty, "<String>` (owned).")]
187 #[doc = concat!("`", $ty, "<&'a str>`")]
189 #[doc = concat!("use fluent_uri::", $ty, ";")]
194 #[doc = concat!("// Keep a reference to the path after dropping the `", $ty, "`.")]
196 #[doc = concat!("let path = ", $ty, "::parse(\"foo:bar\")?.path();")]
197 #[doc = concat!("`", $ty, "`s")]
204 #[doc = concat!($art, " ", $name, ":")]
211 #[doc = concat!(" ", $ty, ",")]
217 #[doc = concat!("let ", $var, " = ", $ty, "::parse(s)?;")]
223 #[doc = concat!("assert_eq!(", $var, ".scheme()",
225 cond!(if $scheme_required { "" } else { ".unwrap()" }), ", SCHEME_FOO);")]
226 #[doc = concat!("let auth = ", $var, ".authority().unwrap();")]
228 #[doc = concat!("assert_eq!(", $var, ".path(), \"/over/there\");")]
236 #[doc = concat!("assert_eq!(", $var, ".query().unwrap(), \"name=ferret\");")]
237 #[doc = concat!("assert_eq!(", $var, ".fragment().unwrap(), \"nose\");")]
238 #[doc = concat!("`", $ty, "<&str>` and `", $ty, "<String>`:")]
243 #[doc = concat!("use fluent_uri::", $ty, ";")]
246 #[doc = concat!("// Parse into a `", $ty, "<&str>` from a string slice.")]
250 #[doc = concat!("let ", $var, ": ", $ty, "<&str> = ", $ty, "::parse(s)?;")]
251 #[doc = concat!("// Parse into a `", $ty, "<String>` from an owned string.")]
253 #[doc = concat!("let ", $var, "_owned: ", $ty, "<String> = ", $ty, "::parse(s.to_owned()).map_err(|e| e.0)?;")]
254 #[doc = concat!("// Convert a `", $ty, "<&str>` to `", $ty, "<String>`.")]
256 #[doc = concat!("let ", $var, "_owned: ", $ty, "<String> = ", $var, ".to_owned();")]
257 #[doc = concat!("// Borrow a `", $ty, "<String>` as `", $ty, "<&str>`.")]
259 #[doc = concat!("let ", $var, ": ", $ty, "<&str> = ", $var, "_owned.borrow();")]
260 #[derive(Clone, Copy)]
263 pub struct $Ty<T> {
264 pub(crate) val: T,
266 pub(crate) meta: Meta,
269 }
270
271 impl<T> RiMaybeRef for $Ty<T> {
272 type Val = T;
273 type WithVal<U> = $Ty<U>;
274
275 type UserinfoE = $UserinfoE;
276 type RegNameE = $RegNameE;
277 type PathE = $PathE;
278 type QueryE = $QueryE;
279 type FragmentE = $FragmentE;
280
281 const CONSTRAINTS: Constraints = Constraints {
282 ascii_only: $ascii_only,
283 scheme_required: $scheme_required,
284 };
285
286 fn new(val: T, meta: Meta) -> Self {
287 Self { val, meta }
288 }
289
290 fn make_ref<'i, 'o>(&'i self) -> RmrRef<'o, 'i>
291 where
292 Self::Val: BorrowOrShare<'i, 'o, str>,
293 {
294 RmrRef::new(self.as_str(), &self.meta)
295 }
296 }
297
298 $(
299 #[cfg(feature = "alloc")]
300 impl<T: Bos<str>> Ri for $Ty<T> {
301 type Ref<U> = $RefTy<U>;
302 }
303 )?
304
305 impl<T> $Ty<T> {
306 #[doc = concat!("Parses ", $art, " ", $name, " from a string into ", $art, " `", $ty, "`.")]
307 #[doc = concat!("- `Result<", $ty, "<&str>, ParseError>` for `I = &str`;")]
311 #[doc = concat!("- `Result<", $ty, "<String>, (ParseError, String)>` for `I = String`.")]
312 #[doc = concat!("[`", $abnf, "`][abnf] ABNF rule from RFC ", $rfc, ".")]
317 #[doc = concat!("[abnf]: ", $abnf_link)]
319 pub fn parse<I>(input: I) -> Result<Self, I::Err>
320 where
321 I: Parse<Val = T>,
322 {
323 input.parse()
324 }
325 }
326
327 #[cfg(feature = "alloc")]
328 impl $Ty<String> {
329 #[doc = concat!("Creates a new builder for ", $name, ".")]
330 #[inline]
331 pub fn builder() -> Builder<Self, cond!(if $scheme_required { NonRefStart } else { Start })> {
332 Builder::new()
333 }
334
335 #[doc = concat!("Borrows this `", $ty, "<String>` as `", $ty, "<&str>`.")]
336 #[allow(clippy::should_implement_trait)]
337 #[inline]
338 #[must_use]
339 pub fn borrow(&self) -> $Ty<&str> {
340 $Ty {
341 val: &self.val,
342 meta: self.meta,
343 }
344 }
345
346 #[doc = concat!("Consumes this `", $ty, "<String>` and yields the underlying [`String`].")]
347 #[inline]
348 #[must_use]
349 pub fn into_string(self) -> String {
350 self.val
351 }
352 }
353
354 #[cfg(feature = "alloc")]
355 impl $Ty<&str> {
356 #[doc = concat!("Creates a new `", $ty, "<String>` by cloning the contents of this `", $ty, "<&str>`.")]
357 #[inline]
358 #[must_use]
359 pub fn to_owned(&self) -> $Ty<String> {
360 $Ty {
361 val: self.val.to_owned(),
362 meta: self.meta,
363 }
364 }
365 }
366
367 impl<'i, 'o, T: BorrowOrShare<'i, 'o, str>> $Ty<T> {
368 #[doc = concat!("Returns the ", $name, " as a string slice.")]
369 #[must_use]
370 pub fn as_str(&'i self) -> &'o str {
371 self.val.borrow_or_share()
372 }
373
374 cond!(if $scheme_required {
375 #[doc = concat!("use fluent_uri::{component::Scheme, ", $ty, "};")]
386 #[doc = concat!("let ", $var, " = ", $ty, "::parse(\"http://example.com/\")?;")]
390 #[doc = concat!("assert_eq!(", $var, ".scheme(), SCHEME_HTTP);")]
391 #[must_use]
394 pub fn scheme(&'i self) -> &'o Scheme {
395 self.make_ref().scheme()
396 }
397 } else {
398 #[doc = concat!("use fluent_uri::{component::Scheme, ", $ty, "};")]
409 #[doc = concat!("let ", $var, " = ", $ty, "::parse(\"http://example.com/\")?;")]
413 #[doc = concat!("assert_eq!(", $var, ".scheme(), Some(SCHEME_HTTP));")]
414 #[doc = concat!("let ", $var, " = ", $ty, "::parse(\"/path/to/file\")?;")]
416 #[doc = concat!("assert_eq!(", $var, ".scheme(), None);")]
417 #[must_use]
420 pub fn scheme(&'i self) -> Option<&'o Scheme> {
421 self.make_ref().scheme_opt()
422 }
423 });
424
425 #[doc = concat!("use fluent_uri::", $ty, ";")]
433 #[doc = concat!("let ", $var, " = ", $ty, "::parse(\"http://example.com/\")?;")]
435 #[doc = concat!("assert!(", $var, ".authority().is_some());")]
436 #[doc = concat!("let ", $var, " = ", $ty, "::parse(\"mailto:user@example.com\")?;")]
438 #[doc = concat!("assert!(", $var, ".authority().is_none());")]
439 #[must_use]
442 pub fn authority(&'i self) -> Option<$Authority<'o>> {
443 self.make_ref().authority().map(Authority::cast)
444 }
445
446 #[doc = concat!("use fluent_uri::", $ty, ";")]
459 #[doc = concat!("let ", $var, " = ", $ty, "::parse(\"http://example.com/\")?;")]
461 #[doc = concat!("assert_eq!(", $var, ".path(), \"/\");")]
462 #[doc = concat!("let ", $var, " = ", $ty, "::parse(\"mailto:user@example.com\")?;")]
464 #[doc = concat!("assert_eq!(", $var, ".path(), \"user@example.com\");")]
465 #[doc = concat!("let ", $var, " = ", $ty, "::parse(\"http://example.com\")?;")]
467 #[doc = concat!("assert_eq!(", $var, ".path(), \"\");")]
468 #[must_use]
471 pub fn path(&'i self) -> &'o EStr<$PathE> {
472 self.make_ref().path().cast()
473 }
474
475 #[doc = concat!("use fluent_uri::{pct_enc::EStr, ", $ty, "};")]
483 #[doc = concat!("let ", $var, " = ", $ty, "::parse(\"http://example.com/?lang=en\")?;")]
485 #[doc = concat!("assert_eq!(", $var, ".query(), Some(EStr::new_or_panic(\"lang=en\")));")]
486 #[doc = concat!("let ", $var, " = ", $ty, "::parse(\"ftp://192.0.2.1/\")?;")]
488 #[doc = concat!("assert_eq!(", $var, ".query(), None);")]
489 #[must_use]
492 pub fn query(&'i self) -> Option<&'o EStr<$QueryE>> {
493 self.make_ref().query().map(EStr::cast)
494 }
495
496 #[doc = concat!("use fluent_uri::{pct_enc::EStr, ", $ty, "};")]
504 #[doc = concat!("let ", $var, " = ", $ty, "::parse(\"http://example.com/#usage\")?;")]
506 #[doc = concat!("assert_eq!(", $var, ".fragment(), Some(EStr::new_or_panic(\"usage\")));")]
507 #[doc = concat!("let ", $var, " = ", $ty, "::parse(\"ftp://192.0.2.1/\")?;")]
509 #[doc = concat!("assert_eq!(", $var, ".fragment(), None);")]
510 #[must_use]
513 pub fn fragment(&'i self) -> Option<&'o EStr<$FragmentE>> {
514 self.make_ref().fragment().map(EStr::cast)
515 }
516 }
517
518 impl<T: Bos<str>> $Ty<T> {
519 $(
520 #[doc = concat!("Resolves the ", $name, " against the given base ", $nr_name)]
521 #[doc = concat!("and returns the target ", $nr_name, ".")]
522 #[doc = concat!("The base ", $nr_name)]
524 #[doc = concat!("[`", $abnf_abs, "`][abnf] ABNF rule from RFC ", $rfc, ".")]
526 #[doc = concat!("To prepare a base ", $nr_name, ",")]
528 #[doc = concat!("from any ", $nr_name, ".")]
530 #[doc = concat!("[abnf]: ", $abnf_abs_link)]
561 #[doc = concat!("[`strip_fragment`]: ", stringify!($NonRefTy), "::strip_fragment")]
562 #[doc = concat!("[`with_fragment`]: ", stringify!($NonRefTy), "::with_fragment")]
563 #[doc = concat!("[`set_fragment`]: ", stringify!($NonRefTy), "::set_fragment")]
564 #[doc = concat!("use fluent_uri::{", stringify!($NonRefTy), ", ", $ty, "};")]
583 #[doc = concat!("let base = ", stringify!($NonRefTy), "::parse(\"http://example.com/foo/bar\")?;")]
585 #[doc = concat!("let ", $var, " = ", $ty, "::parse(\"baz\")?;")]
587 #[doc = concat!("assert_eq!(", $var, ".resolve_against(&base).unwrap(), \"http://example.com/foo/baz\");")]
588 #[doc = concat!("let ", $var, " = ", $ty, "::parse(\"../baz\")?;")]
590 #[doc = concat!("assert_eq!(", $var, ".resolve_against(&base).unwrap(), \"http://example.com/baz\");")]
591 #[doc = concat!("let ", $var, " = ", $ty, "::parse(\"?baz\")?;")]
593 #[doc = concat!("assert_eq!(", $var, ".resolve_against(&base).unwrap(), \"http://example.com/foo/bar?baz\");")]
594 #[cfg(feature = "alloc")]
597 pub fn resolve_against<U: Bos<str>>(
598 &self,
599 base: &$NonRefTy<U>,
600 ) -> Result<$NonRefTy<String>, ResolveError> {
601 resolve::resolve(base.make_ref(), self.make_ref(), true).map(RiMaybeRef::from_pair)
602 }
603 )?
604
605 #[doc = concat!("Normalizes the ", $name, ".")]
606 #[doc = concat!("use fluent_uri::", $ty, ";")]
638 #[doc = concat!("let ", $var, " = ", $ty, "::parse(\"eXAMPLE://a/./b/../b/%63/%7bfoo%7d\")?;")]
640 #[doc = concat!("assert_eq!(", $var, ".normalize(), \"example://a/b/c/%7Bfoo%7D\");")]
641 #[cfg(feature = "alloc")]
644 #[must_use]
645 pub fn normalize(&self) -> $Ty<String> {
646 Normalizer::new().normalize(self).unwrap()
647 }
648
649 cond!(if $scheme_required {} else {
650 #[doc = concat!("use fluent_uri::", $ty, ";")]
656 #[doc = concat!("assert!(", $ty, "::parse(\"http://example.com/\")?.has_scheme());")]
658 #[doc = concat!("assert!(!", $ty, "::parse(\"/path/to/file\")?.has_scheme());")]
659 #[must_use]
662 pub fn has_scheme(&self) -> bool {
663 self.make_ref().has_scheme()
664 }
665 });
666
667 #[doc = concat!("use fluent_uri::", $ty, ";")]
673 #[doc = concat!("assert!(", $ty, "::parse(\"http://example.com/\")?.has_authority());")]
675 #[doc = concat!("assert!(!", $ty, "::parse(\"mailto:user@example.com\")?.has_authority());")]
676 #[must_use]
679 pub fn has_authority(&self) -> bool {
680 self.make_ref().has_authority()
681 }
682
683 #[doc = concat!("use fluent_uri::", $ty, ";")]
689 #[doc = concat!("assert!(", $ty, "::parse(\"http://example.com/?lang=en\")?.has_query());")]
691 #[doc = concat!("assert!(!", $ty, "::parse(\"ftp://192.0.2.1/\")?.has_query());")]
692 #[must_use]
695 pub fn has_query(&self) -> bool {
696 self.make_ref().has_query()
697 }
698
699 #[doc = concat!("use fluent_uri::", $ty, ";")]
705 #[doc = concat!("assert!(", $ty, "::parse(\"http://example.com/#usage\")?.has_fragment());")]
707 #[doc = concat!("assert!(!", $ty, "::parse(\"ftp://192.0.2.1/\")?.has_fragment());")]
708 #[must_use]
711 pub fn has_fragment(&self) -> bool {
712 self.make_ref().has_fragment()
713 }
714
715 #[doc = concat!("Returns a slice of this ", $name)]
716 #[doc = concat!("use fluent_uri::", $ty, ";")]
722 #[doc = concat!("let ", $var, " = ", $ty, "::parse(\"http://example.com/#fragment\")?;")]
724 #[doc = concat!("assert_eq!(", $var, ".strip_fragment(), \"http://example.com/\");")]
725 #[must_use]
728 pub fn strip_fragment(&self) -> $Ty<&str> {
729 RiMaybeRef::new(self.make_ref().strip_fragment(), self.meta)
731 }
732
733 #[doc = concat!("Creates a new ", $name)]
734 #[doc = concat!("use fluent_uri::{pct_enc::EStr, ", $ty, "};")]
742 #[doc = concat!("let ", $var, " = ", $ty, "::parse(\"http://example.com/\")?;")]
744 #[doc = concat!(" ", $var, ".with_fragment(Some(EStr::new_or_panic(\"fragment\"))),")]
746 #[doc = concat!("let ", $var, " = ", $ty, "::parse(\"http://example.com/#fragment\")?;")]
750 #[doc = concat!("assert_eq!(", $var, ".with_fragment(None), \"http://example.com/\");")]
751 #[cfg(feature = "alloc")]
754 #[must_use]
755 pub fn with_fragment(&self, opt: Option<&EStr<$FragmentE>>) -> $Ty<String> {
756 RiMaybeRef::new(self.make_ref().with_fragment(opt.map(EStr::as_str)), self.meta)
758 }
759 }
760
761 #[cfg(feature = "alloc")]
762 impl $Ty<String> {
763 #[doc = concat!("use fluent_uri::{pct_enc::EStr, ", $ty, "};")]
771 #[doc = concat!("let mut ", $var, " = ", $ty, "::parse(\"http://example.com/\")?.to_owned();")]
773 #[doc = concat!($var, ".set_fragment(Some(EStr::new_or_panic(\"fragment\")));")]
775 #[doc = concat!("assert_eq!(", $var, ", \"http://example.com/#fragment\");")]
776 #[doc = concat!($var, ".set_fragment(None);")]
778 #[doc = concat!("assert_eq!(", $var, ", \"http://example.com/\");")]
779 pub fn set_fragment(&mut self, opt: Option<&EStr<$FragmentE>>) {
782 RmrRef::set_fragment(&mut self.val, &self.meta, opt.map(EStr::as_str))
784 }
785 }
786
787 impl<T: Value> Default for $Ty<T> {
788 #[doc = concat!("Creates an empty ", $name, ".")]
789 fn default() -> Self {
790 Self {
791 val: T::default(),
792 meta: Meta::default(),
793 }
794 }
795 }
796
797 impl<T: Bos<str>, U: Bos<str>> PartialEq<$Ty<U>> for $Ty<T> {
798 fn eq(&self, other: &$Ty<U>) -> bool {
799 self.as_str() == other.as_str()
800 }
801 }
802
803 impl<T: Bos<str>> PartialEq<str> for $Ty<T> {
804 fn eq(&self, other: &str) -> bool {
805 self.as_str() == other
806 }
807 }
808
809 impl<T: Bos<str>> PartialEq<$Ty<T>> for str {
810 fn eq(&self, other: &$Ty<T>) -> bool {
811 self == other.as_str()
812 }
813 }
814
815 impl<T: Bos<str>> PartialEq<&str> for $Ty<T> {
816 fn eq(&self, other: &&str) -> bool {
817 self.as_str() == *other
818 }
819 }
820
821 impl<T: Bos<str>> PartialEq<$Ty<T>> for &str {
822 fn eq(&self, other: &$Ty<T>) -> bool {
823 *self == other.as_str()
824 }
825 }
826
827 impl<T: Bos<str>> Eq for $Ty<T> {}
828
829 impl<T: Bos<str>> hash::Hash for $Ty<T> {
830 fn hash<H: hash::Hasher>(&self, state: &mut H) {
831 self.as_str().hash(state);
832 }
833 }
834
835 impl<T: Bos<str>> PartialOrd for $Ty<T> {
836 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
837 Some(self.cmp(other))
838 }
839 }
840
841 impl<T: Bos<str>> Ord for $Ty<T> {
842 fn cmp(&self, other: &Self) -> Ordering {
843 self.as_str().cmp(other.as_str())
844 }
845 }
846
847 impl<T: Bos<str>> AsRef<str> for $Ty<T> {
848 fn as_ref(&self) -> &str {
849 self.as_str()
850 }
851 }
852
853 impl<T: Bos<str>> Borrow<str> for $Ty<T> {
854 fn borrow(&self) -> &str {
855 self.as_str()
856 }
857 }
858
859 impl<'a> TryFrom<&'a str> for $Ty<&'a str> {
860 type Error = ParseError;
861
862 #[inline]
864 fn try_from(value: &'a str) -> Result<Self, Self::Error> {
865 $Ty::parse(value)
866 }
867 }
868
869 #[cfg(feature = "alloc")]
870 impl TryFrom<String> for $Ty<String> {
871 type Error = (ParseError, String);
872
873 #[inline]
875 fn try_from(value: String) -> Result<Self, Self::Error> {
876 $Ty::parse(value)
877 }
878 }
879
880 impl<'a> From<$Ty<&'a str>> for &'a str {
881 #[doc = concat!("Equivalent to [`as_str`](", $ty, "::as_str).")]
882 #[inline]
883 fn from(value: $Ty<&'a str>) -> &'a str {
884 value.val
885 }
886 }
887
888 #[cfg(feature = "alloc")]
889 impl<'a> From<$Ty<String>> for String {
890 #[doc = concat!("Equivalent to [`into_string`](", $ty, "::into_string).")]
891 #[inline]
892 fn from(value: $Ty<String>) -> String {
893 value.val
894 }
895 }
896
897 #[cfg(feature = "alloc")]
898 impl From<$Ty<&str>> for $Ty<String> {
899 #[inline]
901 fn from(value: $Ty<&str>) -> Self {
902 value.to_owned()
903 }
904 }
905
906 #[cfg(feature = "alloc")]
907 impl FromStr for $Ty<String> {
908 type Err = ParseError;
909
910 #[doc = concat!("Equivalent to `", $ty, "::parse(s).map(|r| r.to_owned())`.")]
911 #[inline]
912 fn from_str(s: &str) -> Result<Self, Self::Err> {
913 $Ty::parse(s).map(|r| r.to_owned())
914 }
915 }
916
917 impl<T: Bos<str>> fmt::Debug for $Ty<T> {
918 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
919 f.debug_struct($ty)
920 .field("scheme", &self.scheme())
921 .field("authority", &self.authority())
922 .field("path", &self.path())
923 .field("query", &self.query())
924 .field("fragment", &self.fragment())
925 .finish()
926 }
927 }
928
929 impl<T: Bos<str>> fmt::Display for $Ty<T> {
930 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
931 fmt::Display::fmt(self.as_str(), f)
932 }
933 }
934
935 #[cfg(feature = "serde")]
936 impl<T: Bos<str>> Serialize for $Ty<T> {
937 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
938 where
939 S: Serializer,
940 {
941 serializer.serialize_str(self.as_str())
942 }
943 }
944
945 #[cfg(feature = "serde")]
946 impl<'de> Deserialize<'de> for $Ty<&'de str> {
947 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
948 where
949 D: Deserializer<'de>,
950 {
951 let s = <&str>::deserialize(deserializer)?;
952 $Ty::parse(s).map_err(|e| {
953 de::Error::custom(format_args!(
954 "failed to parse {s:?} as {}: {e}",
955 $name
956 ))
957 })
958 }
959 }
960
961 #[cfg(feature = "serde")]
962 impl<'de> Deserialize<'de> for $Ty<String> {
963 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
964 where
965 D: Deserializer<'de>,
966 {
967 let s = String::deserialize(deserializer)?;
968 $Ty::parse(s).map_err(|(s, e)| {
969 de::Error::custom(format_args!(
970 "failed to parse {s:?} as {}: {e}",
971 $name
972 ))
973 })
974 }
975 }
976 };
977}
978
979#[derive(Clone, Copy)]
981pub struct RmrRef<'v, 'm> {
982 val: &'v str,
983 meta: &'m Meta,
984}
985
986impl<'v, 'm> RmrRef<'v, 'm> {
987 pub fn new(val: &'v str, meta: &'m Meta) -> Self {
988 Self { val, meta }
989 }
990
991 pub fn as_str(self) -> &'v str {
992 self.val
993 }
994
995 fn slice(self, start: usize, end: usize) -> &'v str {
996 &self.val[start..end]
997 }
998
999 fn eslice<E: Encoder>(self, start: usize, end: usize) -> &'v EStr<E> {
1000 EStr::new_validated(self.slice(start, end))
1001 }
1002
1003 pub fn scheme_opt(self) -> Option<&'v Scheme> {
1004 let end = self.meta.scheme_end?.get();
1005 Some(Scheme::new_validated(self.slice(0, end)))
1006 }
1007
1008 pub fn scheme(self) -> &'v Scheme {
1009 let end = self.meta.scheme_end.map_or(0, |i| i.get());
1010 Scheme::new_validated(self.slice(0, end))
1011 }
1012
1013 pub fn authority(self) -> Option<IAuthority<'v>> {
1014 let mut meta = self.meta.auth_meta?;
1015 let start = match self.meta.scheme_end {
1016 Some(i) => i.get() + 3,
1017 None => 2,
1018 };
1019 let end = self.meta.path_bounds.0;
1020
1021 meta.host_bounds.0 -= start;
1022 meta.host_bounds.1 -= start;
1023
1024 Some(IAuthority::new(self.slice(start, end), meta))
1025 }
1026
1027 pub fn path(self) -> &'v EStr<IPath> {
1028 self.eslice(self.meta.path_bounds.0, self.meta.path_bounds.1)
1029 }
1030
1031 pub fn query(self) -> Option<&'v EStr<IQuery>> {
1032 let end = self.meta.query_end?.get();
1033 Some(self.eslice(self.meta.path_bounds.1 + 1, end))
1034 }
1035
1036 fn fragment_start(self) -> Option<usize> {
1037 Some(self.meta.query_or_path_end())
1038 .filter(|&i| i != self.val.len())
1039 .map(|i| i + 1)
1040 }
1041
1042 pub fn fragment(self) -> Option<&'v EStr<IFragment>> {
1043 self.fragment_start()
1044 .map(|i| self.eslice(i, self.val.len()))
1045 }
1046
1047 #[cfg(feature = "alloc")]
1048 pub fn set_fragment(buf: &mut String, meta: &Meta, opt: Option<&str>) {
1049 buf.truncate(meta.query_or_path_end());
1050 if let Some(s) = opt {
1051 buf.reserve_exact(s.len() + 1);
1052 buf.push('#');
1053 buf.push_str(s);
1054 }
1055 }
1056
1057 pub fn strip_fragment(self) -> &'v str {
1058 &self.val[..self.meta.query_or_path_end()]
1059 }
1060
1061 #[cfg(feature = "alloc")]
1062 pub fn with_fragment(self, opt: Option<&str>) -> String {
1063 let stripped = self.strip_fragment();
1064 if let Some(s) = opt {
1065 [stripped, "#", s].concat()
1066 } else {
1067 stripped.to_owned()
1068 }
1069 }
1070
1071 #[inline]
1072 pub fn has_scheme(self) -> bool {
1073 self.meta.scheme_end.is_some()
1074 }
1075
1076 #[inline]
1077 pub fn has_authority(self) -> bool {
1078 self.meta.auth_meta.is_some()
1079 }
1080
1081 #[inline]
1082 pub fn has_query(self) -> bool {
1083 self.meta.query_end.is_some()
1084 }
1085
1086 #[inline]
1087 pub fn has_fragment(self) -> bool {
1088 self.meta.query_or_path_end() != self.val.len()
1089 }
1090
1091 pub fn ensure_has_scheme(self) -> Result<(), ConvertError> {
1092 if self.has_scheme() {
1093 Ok(())
1094 } else {
1095 Err(ConvertError::NoScheme)
1096 }
1097 }
1098
1099 pub fn ensure_ascii(self) -> Result<(), ConvertError> {
1100 match self.as_str().bytes().position(|x| !x.is_ascii()) {
1101 Some(index) => Err(ConvertError::NotAscii { index }),
1102 None => Ok(()),
1103 }
1104 }
1105}
1106
1107ri_maybe_ref! {
1108 Type = Uri,
1109 type_name = "Uri",
1110 variable_name = "uri",
1111 name = "URI",
1112 indefinite_article = "a",
1113 description = "A URI.",
1114 ascii_only = true,
1115 scheme_required = true,
1116 rfc = 3986,
1117 abnf_rule = ("URI", "https://datatracker.ietf.org/doc/html/rfc3986#section-3"),
1118 RefType = UriRef,
1119 ref_name = "URI reference",
1120 AuthorityType = Authority,
1121 UserinfoEncoderType = Userinfo,
1122 RegNameEncoderType = RegName,
1123 PathEncoderType = Path,
1124 QueryEncoderType = Query,
1125 FragmentEncoderType = Fragment,
1126}
1127
1128ri_maybe_ref! {
1129 Type = UriRef,
1130 type_name = "UriRef",
1131 variable_name = "uri_ref",
1132 name = "URI reference",
1133 indefinite_article = "a",
1134 description = "A URI reference, i.e., either a URI or a relative reference.",
1135 ascii_only = true,
1136 scheme_required = false,
1137 rfc = 3986,
1138 abnf_rule = ("URI-reference", "https://datatracker.ietf.org/doc/html/rfc3986#section-4.1"),
1139 NonRefType = Uri,
1140 non_ref_name = "URI",
1141 non_ref_link = "https://datatracker.ietf.org/doc/html/rfc3986#section-3",
1142 abnf_rule_absolute = ("absolute-URI", "https://datatracker.ietf.org/doc/html/rfc3986#section-4.3"),
1143 AuthorityType = Authority,
1144 UserinfoEncoderType = Userinfo,
1145 RegNameEncoderType = RegName,
1146 PathEncoderType = Path,
1147 QueryEncoderType = Query,
1148 FragmentEncoderType = Fragment,
1149}
1150
1151ri_maybe_ref! {
1152 Type = Iri,
1153 type_name = "Iri",
1154 variable_name = "iri",
1155 name = "IRI",
1156 indefinite_article = "an",
1157 description = "An IRI.",
1158 ascii_only = false,
1159 scheme_required = true,
1160 rfc = 3987,
1161 abnf_rule = ("IRI", "https://datatracker.ietf.org/doc/html/rfc3987#section-2.2"),
1162 RefType = IriRef,
1163 ref_name = "IRI reference",
1164 AuthorityType = IAuthority,
1165 UserinfoEncoderType = IUserinfo,
1166 RegNameEncoderType = IRegName,
1167 PathEncoderType = IPath,
1168 QueryEncoderType = IQuery,
1169 FragmentEncoderType = IFragment,
1170}
1171
1172ri_maybe_ref! {
1173 Type = IriRef,
1174 type_name = "IriRef",
1175 variable_name = "iri_ref",
1176 name = "IRI reference",
1177 indefinite_article = "an",
1178 description = "An IRI reference, i.e., either a IRI or a relative reference.",
1179 ascii_only = false,
1180 scheme_required = false,
1181 rfc = 3987,
1182 abnf_rule = ("IRI-reference", "https://datatracker.ietf.org/doc/html/rfc3987#section-2.2"),
1183 NonRefType = Iri,
1184 non_ref_name = "IRI",
1185 non_ref_link = "https://datatracker.ietf.org/doc/html/rfc3987#section-2.2",
1186 abnf_rule_absolute = ("absolute-IRI", "https://datatracker.ietf.org/doc/html/rfc3987#section-2.2"),
1187 AuthorityType = IAuthority,
1188 UserinfoEncoderType = IUserinfo,
1189 RegNameEncoderType = IRegName,
1190 PathEncoderType = IPath,
1191 QueryEncoderType = IQuery,
1192 FragmentEncoderType = IFragment,
1193}