Skip to main content

iri_string/types/generic/
reference.rs

1//! IRI reference.
2
3#[cfg(feature = "alloc")]
4use alloc::collections::TryReserveError;
5#[cfg(all(feature = "alloc", not(feature = "std")))]
6use alloc::string::String;
7
8use crate::components::AuthorityComponents;
9#[cfg(feature = "alloc")]
10use crate::mask_password::password_range_to_hide;
11use crate::mask_password::PasswordMasked;
12use crate::normalize::Normalized;
13use crate::parser::trusted as trusted_parser;
14#[cfg(feature = "alloc")]
15use crate::raw;
16use crate::resolve::FixedBaseResolver;
17use crate::spec::Spec;
18use crate::types::{RiAbsoluteStr, RiFragmentStr, RiQueryStr, RiRelativeStr, RiStr};
19#[cfg(feature = "alloc")]
20use crate::types::{RiRelativeString, RiString};
21use crate::validate::iri_reference;
22
23define_custom_string_slice! {
24    /// A borrowed string of an absolute IRI possibly with fragment part.
25    ///
26    /// This corresponds to [`IRI-reference` rule] in [RFC 3987]
27    /// (and [`URI-reference` rule] in [RFC 3986]).
28    /// The rule for `IRI-reference` is `IRI / irelative-ref`.
29    /// In other words, this is union of [`RiStr`] and [`RiRelativeStr`].
30    ///
31    /// # Valid values
32    ///
33    /// This type can have an IRI reference (which can be absolute or relative).
34    ///
35    /// ```
36    /// # use iri_string::types::IriReferenceStr;
37    /// assert!(IriReferenceStr::new("https://user:pass@example.com:8080").is_ok());
38    /// assert!(IriReferenceStr::new("https://example.com/").is_ok());
39    /// assert!(IriReferenceStr::new("https://example.com/foo?bar=baz").is_ok());
40    /// assert!(IriReferenceStr::new("https://example.com/foo?bar=baz#qux").is_ok());
41    /// assert!(IriReferenceStr::new("foo:bar").is_ok());
42    /// assert!(IriReferenceStr::new("foo:").is_ok());
43    /// // `foo://.../` below are all allowed. See the crate documentation for detail.
44    /// assert!(IriReferenceStr::new("foo:/").is_ok());
45    /// assert!(IriReferenceStr::new("foo://").is_ok());
46    /// assert!(IriReferenceStr::new("foo:///").is_ok());
47    /// assert!(IriReferenceStr::new("foo:////").is_ok());
48    /// assert!(IriReferenceStr::new("foo://///").is_ok());
49    /// assert!(IriReferenceStr::new("foo/bar").is_ok());
50    /// assert!(IriReferenceStr::new("/foo/bar").is_ok());
51    /// assert!(IriReferenceStr::new("//foo/bar").is_ok());
52    /// assert!(IriReferenceStr::new("#foo").is_ok());
53    /// ```
54    ///
55    /// Some characters and sequences cannot used in an IRI reference.
56    ///
57    /// ```
58    /// # use iri_string::types::IriReferenceStr;
59    /// // `<` and `>` cannot directly appear in an IRI reference.
60    /// assert!(IriReferenceStr::new("<not allowed>").is_err());
61    /// // Broken percent encoding cannot appear in an IRI reference.
62    /// assert!(IriReferenceStr::new("%").is_err());
63    /// assert!(IriReferenceStr::new("%GG").is_err());
64    /// ```
65    ///
66    /// [RFC 3986]: https://www.rfc-editor.org/rfc/rfc3986.html
67    /// [RFC 3987]: https://www.rfc-editor.org/rfc/rfc3987.html
68    /// [`IRI-reference` rule]: https://www.rfc-editor.org/rfc/rfc3987.html#section-2.2
69    /// [`URI-reference` rule]: https://www.rfc-editor.org/rfc/rfc3986.html#section-4.1
70    /// [`RiRelativeStr`]: struct.RiRelativeStr.html
71    /// [`RiStr`]: struct.RiStr.html
72    struct RiReferenceStr {
73        validator = iri_reference,
74        expecting_msg = "IRI reference string",
75    }
76}
77
78#[cfg(feature = "alloc")]
79define_custom_string_owned! {
80    /// An owned string of an absolute IRI possibly with fragment part.
81    ///
82    /// This corresponds to [`IRI-reference` rule] in [RFC 3987]
83    /// (and [`URI-reference` rule] in [RFC 3986]).
84    /// The rule for `IRI-reference` is `IRI / irelative-ref`.
85    /// In other words, this is union of [`RiString`] and [`RiRelativeString`].
86    ///
87    /// For details, see the document for [`RiReferenceStr`].
88    ///
89    /// Enabled by `alloc` or `std` feature.
90    ///
91    /// [RFC 3986]: https://www.rfc-editor.org/rfc/rfc3986.html
92    /// [RFC 3987]: https://www.rfc-editor.org/rfc/rfc3987.html
93    /// [`IRI-reference` rule]: https://www.rfc-editor.org/rfc/rfc3987.html#section-2.2
94    /// [`URI-reference` rule]: https://www.rfc-editor.org/rfc/rfc3986.html#section-4.1
95    /// [`RiReferenceStr`]: struct.RiReferenceString.html
96    /// [`RiRelativeString`]: struct.RiRelativeString.html
97    /// [`RiString`]: struct.RiString.html
98    struct RiReferenceString {
99        validator = iri_reference,
100        slice = RiReferenceStr,
101        expecting_msg = "IRI reference string",
102    }
103}
104
105impl<S: Spec> RiReferenceStr<S> {
106    /// Returns the string as [`&RiStr`][`RiStr`], if it is valid as an IRI.
107    ///
108    /// If it is not an IRI, then [`&RiRelativeStr`][`RiRelativeStr`] is returned as `Err(_)`.
109    ///
110    /// [`RiRelativeStr`]: struct.RiRelativeStr.html
111    /// [`RiStr`]: struct.RiStr.html
112    pub fn to_iri(&self) -> Result<&RiStr<S>, &RiRelativeStr<S>> {
113        // Check with `IRI` rule first, because the syntax rule for `IRI-reference` is
114        // `IRI / irelative-ref`.
115        //
116        // > Some productions are ambiguous. The "first-match-wins" (a.k.a.
117        // > "greedy") algorithm applies. For details, see [RFC3986].
118        // >
119        // > --- <https://www.rfc-editor.org/rfc/rfc3987.html#section-2.2>.
120
121        let s = self.as_str();
122        if trusted_parser::extract_scheme(s).is_some() {
123            // Has a scheme followed by a colon. An IRI.
124            // SAFETY: an IRI reference with scheme is an absolute IRI.
125            // See the RFC 3987 syntax rule `IRI-reference = IRI / irelative-ref`.
126            Ok(unsafe {
127                RiStr::<S>::new_unchecked_justified(
128                    s,
129                    "an IRI reference with a scheme must be a valid non-relative IRI",
130                )
131            })
132        } else {
133            // Has no scheme. A relative IRI reference.
134            // SAFETY: if an IRI reference is not an IRI, then it is a relative
135            // iri reference. See the RFC 3987 syntax rule
136            // `IRI-reference = IRI / irelative-ref`.
137            Err(unsafe {
138                RiRelativeStr::<S>::new_unchecked_justified(
139                    s,
140                    "an IRI reference without a scheme must be a valid relative IRI",
141                )
142            })
143        }
144    }
145
146    /// Returns the string as [`&RiRelativeStr`][`RiRelativeStr`], if it is valid as an IRI.
147    ///
148    /// If it is not an IRI, then [`&RiStr`][`RiStr`] is returned as `Err(_)`.
149    ///
150    /// [`RiRelativeStr`]: struct.RiRelativeStr.html
151    /// [`RiStr`]: struct.RiStr.html
152    pub fn to_relative_iri(&self) -> Result<&RiRelativeStr<S>, &RiStr<S>> {
153        match self.to_iri() {
154            Ok(iri) => Err(iri),
155            Err(relative) => Ok(relative),
156        }
157    }
158
159    /// Returns resolved IRI against the given base IRI.
160    ///
161    /// For IRI reference resolution output examples, see [RFC 3986 section 5.4].
162    ///
163    /// If you are going to resolve multiple references against the common base,
164    /// consider using [`FixedBaseResolver`].
165    ///
166    /// # Strictness
167    ///
168    /// The IRI parsers provided by this crate is strict (e.g. `http:g` is
169    /// always interpreted as a composition of the scheme `http` and the path
170    /// `g`), so backward compatible parsing and resolution are not provided.
171    /// About parser and resolver strictness, see [RFC 3986 section 5.4.2]:
172    ///
173    /// > Some parsers allow the scheme name to be present in a relative
174    /// > reference if it is the same as the base URI scheme. This is considered
175    /// > to be a loophole in prior specifications of partial URI
176    /// > [RFC1630](https://www.rfc-editor.org/rfc/rfc1630.html). Its use should be
177    /// > avoided but is allowed for backward compatibility.
178    /// >
179    /// > --- <https://www.rfc-editor.org/rfc/rfc3986.html#section-5.4.2>
180    ///
181    /// # Failures
182    ///
183    /// This method itself does not fail, but IRI resolution without WHATWG URL
184    /// Standard serialization can fail in some minor cases.
185    ///
186    /// To see examples of such unresolvable IRIs, visit the documentation
187    /// for [`normalize`][`crate::normalize`] module.
188    ///
189    /// [RFC 3986 section 5.4]: https://www.rfc-editor.org/rfc/rfc3986.html#section-5.4
190    /// [RFC 3986 section 5.4.2]: https://www.rfc-editor.org/rfc/rfc3986.html#section-5.4.2
191    pub fn resolve_against<'a>(&'a self, base: &'a RiAbsoluteStr<S>) -> Normalized<'a, RiStr<S>> {
192        FixedBaseResolver::new(base).resolve(self.as_ref())
193    }
194
195    /// Returns the proxy to the IRI with password masking feature.
196    ///
197    /// # Examples
198    ///
199    /// ```
200    /// # use iri_string::validate::Error;
201    /// # #[cfg(feature = "alloc")] {
202    /// use iri_string::format::ToDedicatedString;
203    /// use iri_string::types::IriReferenceStr;
204    ///
205    /// let iri = IriReferenceStr::new("http://user:password@example.com/path?query")?;
206    /// let masked = iri.mask_password();
207    /// assert_eq!(masked.to_dedicated_string(), "http://user:@example.com/path?query");
208    ///
209    /// assert_eq!(
210    ///     masked.replace_password("${password}").to_string(),
211    ///     "http://user:${password}@example.com/path?query"
212    /// );
213    /// # }
214    /// # Ok::<_, Error>(())
215    /// ```
216    #[inline]
217    #[must_use]
218    pub fn mask_password(&self) -> PasswordMasked<'_, Self> {
219        PasswordMasked::new(self)
220    }
221}
222
223/// Components getters.
224impl<S: Spec> RiReferenceStr<S> {
225    /// Returns the scheme.
226    ///
227    /// The following colon is truncated.
228    ///
229    /// # Examples
230    ///
231    /// ```
232    /// # use iri_string::validate::Error;
233    /// use iri_string::types::IriReferenceStr;
234    ///
235    /// let iri = IriReferenceStr::new("http://example.com/pathpath?queryquery#fragfrag")?;
236    /// assert_eq!(iri.scheme_str(), Some("http"));
237    /// # Ok::<_, Error>(())
238    /// ```
239    ///
240    /// ```
241    /// # use iri_string::validate::Error;
242    /// use iri_string::types::IriReferenceStr;
243    ///
244    /// let iri = IriReferenceStr::new("foo/bar:baz")?;
245    /// assert_eq!(iri.scheme_str(), None);
246    /// # Ok::<_, Error>(())
247    /// ```
248    #[inline]
249    #[must_use]
250    pub fn scheme_str(&self) -> Option<&str> {
251        trusted_parser::extract_scheme(self.as_str())
252    }
253
254    /// Returns the authority.
255    ///
256    /// The leading `//` is truncated.
257    ///
258    /// # Examples
259    ///
260    /// ```
261    /// # use iri_string::validate::Error;
262    /// use iri_string::types::IriReferenceStr;
263    ///
264    /// let iri = IriReferenceStr::new("http://example.com/pathpath?queryquery#fragfrag")?;
265    /// assert_eq!(iri.authority_str(), Some("example.com"));
266    /// # Ok::<_, Error>(())
267    /// ```
268    ///
269    /// ```
270    /// # use iri_string::validate::Error;
271    /// use iri_string::types::IriReferenceStr;
272    ///
273    /// let iri = IriReferenceStr::new("urn:uuid:10db315b-fcd1-4428-aca8-15babc9a2da2")?;
274    /// assert_eq!(iri.authority_str(), None);
275    /// # Ok::<_, Error>(())
276    /// ```
277    ///
278    /// ```
279    /// # use iri_string::validate::Error;
280    /// use iri_string::types::IriReferenceStr;
281    ///
282    /// let iri = IriReferenceStr::new("foo/bar:baz")?;
283    /// assert_eq!(iri.authority_str(), None);
284    /// # Ok::<_, Error>(())
285    /// ```
286    #[inline]
287    #[must_use]
288    pub fn authority_str(&self) -> Option<&str> {
289        trusted_parser::extract_authority(self.as_str())
290    }
291
292    /// Returns the path.
293    ///
294    /// # Examples
295    ///
296    /// ```
297    /// # use iri_string::validate::Error;
298    /// use iri_string::types::IriReferenceStr;
299    ///
300    /// let iri = IriReferenceStr::new("http://example.com/pathpath?queryquery#fragfrag")?;
301    /// assert_eq!(iri.path_str(), "/pathpath");
302    /// # Ok::<_, Error>(())
303    /// ```
304    ///
305    /// ```
306    /// # use iri_string::validate::Error;
307    /// use iri_string::types::IriReferenceStr;
308    ///
309    /// let iri = IriReferenceStr::new("urn:uuid:10db315b-fcd1-4428-aca8-15babc9a2da2")?;
310    /// assert_eq!(iri.path_str(), "uuid:10db315b-fcd1-4428-aca8-15babc9a2da2");
311    /// # Ok::<_, Error>(())
312    /// ```
313    ///
314    /// ```
315    /// # use iri_string::validate::Error;
316    /// use iri_string::types::IriReferenceStr;
317    ///
318    /// let iri = IriReferenceStr::new("foo/bar:baz")?;
319    /// assert_eq!(iri.path_str(), "foo/bar:baz");
320    /// # Ok::<_, Error>(())
321    /// ```
322    #[inline]
323    #[must_use]
324    pub fn path_str(&self) -> &str {
325        trusted_parser::extract_path(self.as_str())
326    }
327
328    /// Returns the query.
329    ///
330    /// The leading question mark (`?`) is truncated.
331    ///
332    /// # Examples
333    ///
334    /// ```
335    /// # use iri_string::validate::Error;
336    /// use iri_string::types::{IriQueryStr, IriReferenceStr};
337    ///
338    /// let iri = IriReferenceStr::new("http://example.com/pathpath?queryquery#fragfrag")?;
339    /// let query = IriQueryStr::new("queryquery")?;
340    /// assert_eq!(iri.query(), Some(query));
341    /// # Ok::<_, Error>(())
342    /// ```
343    ///
344    /// ```
345    /// # use iri_string::validate::Error;
346    /// use iri_string::types::IriReferenceStr;
347    ///
348    /// let iri = IriReferenceStr::new("urn:uuid:10db315b-fcd1-4428-aca8-15babc9a2da2")?;
349    /// assert_eq!(iri.query(), None);
350    /// # Ok::<_, Error>(())
351    /// ```
352    ///
353    /// ```
354    /// # use iri_string::validate::Error;
355    /// use iri_string::types::{IriQueryStr, IriReferenceStr};
356    ///
357    /// let iri = IriReferenceStr::new("foo/bar:baz?")?;
358    /// let query = IriQueryStr::new("")?;
359    /// assert_eq!(iri.query(), Some(query));
360    /// # Ok::<_, Error>(())
361    /// ```
362    #[inline]
363    #[must_use]
364    pub fn query(&self) -> Option<&RiQueryStr<S>> {
365        trusted_parser::extract_query(self.as_str()).map(|query| {
366            // SAFETY: `extract_query` returns the query part of an IRI, and the
367            // returned string should have only valid characters since is the
368            // substring of the source IRI.
369            unsafe {
370                RiQueryStr::new_unchecked_justified(
371                    query,
372                    "query in a valid IRI reference must also be valid",
373                )
374            }
375        })
376    }
377
378    /// Returns the query as a raw string slice.
379    ///
380    /// The leading question mark (`?`) is truncated.
381    ///
382    /// # Examples
383    ///
384    /// ```
385    /// # use iri_string::validate::Error;
386    /// use iri_string::types::IriReferenceStr;
387    ///
388    /// let iri = IriReferenceStr::new("http://example.com/pathpath?queryquery#fragfrag")?;
389    /// assert_eq!(iri.query_str(), Some("queryquery"));
390    /// # Ok::<_, Error>(())
391    /// ```
392    ///
393    /// ```
394    /// # use iri_string::validate::Error;
395    /// use iri_string::types::IriReferenceStr;
396    ///
397    /// let iri = IriReferenceStr::new("urn:uuid:10db315b-fcd1-4428-aca8-15babc9a2da2")?;
398    /// assert_eq!(iri.query_str(), None);
399    /// # Ok::<_, Error>(())
400    /// ```
401    ///
402    /// ```
403    /// # use iri_string::validate::Error;
404    /// use iri_string::types::IriReferenceStr;
405    ///
406    /// let iri = IriReferenceStr::new("foo/bar:baz?")?;
407    /// assert_eq!(iri.query_str(), Some(""));
408    /// # Ok::<_, Error>(())
409    /// ```
410    #[inline]
411    #[must_use]
412    pub fn query_str(&self) -> Option<&str> {
413        trusted_parser::extract_query(self.as_str())
414    }
415
416    /// Returns the fragment part if exists.
417    ///
418    /// A leading `#` character is truncated if the fragment part exists.
419    ///
420    /// # Examples
421    ///
422    /// If the IRI has a fragment part, `Some(_)` is returned.
423    ///
424    /// ```
425    /// # use iri_string::{spec::IriSpec, types::{IriFragmentStr, IriReferenceStr}, validate::Error};
426    /// let iri = IriReferenceStr::new("foo://bar/baz?qux=quux#corge")?;
427    /// let fragment = IriFragmentStr::new("corge")?;
428    /// assert_eq!(iri.fragment(), Some(fragment));
429    /// # Ok::<_, Error>(())
430    /// ```
431    ///
432    /// ```
433    /// # use iri_string::{spec::IriSpec, types::{IriFragmentStr, IriReferenceStr}, validate::Error};
434    /// let iri = IriReferenceStr::new("#foo")?;
435    /// let fragment = IriFragmentStr::new("foo")?;
436    /// assert_eq!(iri.fragment(), Some(fragment));
437    /// # Ok::<_, Error>(())
438    /// ```
439    ///
440    /// When the fragment part exists but is empty string, `Some(_)` is returned.
441    ///
442    /// ```
443    /// # use iri_string::{spec::IriSpec, types::{IriFragmentStr, IriReferenceStr}, validate::Error};
444    /// let iri = IriReferenceStr::new("foo://bar/baz?qux=quux#")?;
445    /// let fragment = IriFragmentStr::new("")?;
446    /// assert_eq!(iri.fragment(), Some(fragment));
447    /// # Ok::<_, Error>(())
448    /// ```
449    ///
450    /// ```
451    /// # use iri_string::{spec::IriSpec, types::{IriFragmentStr, IriReferenceStr}, validate::Error};
452    /// let iri = IriReferenceStr::new("#")?;
453    /// let fragment = IriFragmentStr::new("")?;
454    /// assert_eq!(iri.fragment(), Some(fragment));
455    /// # Ok::<_, Error>(())
456    /// ```
457    ///
458    /// If the IRI has no fragment, `None` is returned.
459    ///
460    /// ```
461    /// # use iri_string::{spec::IriSpec, types::IriReferenceStr, validate::Error};
462    /// let iri = IriReferenceStr::new("foo://bar/baz?qux=quux")?;
463    /// assert_eq!(iri.fragment(), None);
464    /// # Ok::<_, Error>(())
465    /// ```
466    ///
467    /// ```
468    /// # use iri_string::{spec::IriSpec, types::IriReferenceStr, validate::Error};
469    /// let iri = IriReferenceStr::new("")?;
470    /// assert_eq!(iri.fragment(), None);
471    /// # Ok::<_, Error>(())
472    /// ```
473    #[must_use]
474    pub fn fragment(&self) -> Option<&RiFragmentStr<S>> {
475        trusted_parser::extract_fragment(self.as_str()).map(|fragment| {
476            // SAFETY: `extract_fragment` returns the fragment part of an IRI,
477            // and the returned string should have only valid characters since
478            // is the substring of the source IRI.
479            unsafe {
480                RiFragmentStr::new_unchecked_justified(
481                    fragment,
482                    "fragment in a valid IRI reference must also be valid",
483                )
484            }
485        })
486    }
487
488    /// Returns the fragment part as a raw string slice if exists.
489    ///
490    /// A leading `#` character is truncated if the fragment part exists.
491    ///
492    /// # Examples
493    ///
494    /// If the IRI has a fragment part, `Some(_)` is returned.
495    ///
496    /// ```
497    /// # use iri_string::{spec::IriSpec, types::IriReferenceStr, validate::Error};
498    /// let iri = IriReferenceStr::new("foo://bar/baz?qux=quux#corge")?;
499    /// assert_eq!(iri.fragment_str(), Some("corge"));
500    /// # Ok::<_, Error>(())
501    /// ```
502    ///
503    /// ```
504    /// # use iri_string::{spec::IriSpec, types::IriReferenceStr, validate::Error};
505    /// let iri = IriReferenceStr::new("#foo")?;
506    /// assert_eq!(iri.fragment_str(), Some("foo"));
507    /// # Ok::<_, Error>(())
508    /// ```
509    ///
510    /// When the fragment part exists but is empty string, `Some(_)` is returned.
511    ///
512    /// ```
513    /// # use iri_string::{spec::IriSpec, types::IriReferenceStr, validate::Error};
514    /// let iri = IriReferenceStr::new("foo://bar/baz?qux=quux#")?;
515    /// assert_eq!(iri.fragment_str(), Some(""));
516    /// # Ok::<_, Error>(())
517    /// ```
518    ///
519    /// ```
520    /// # use iri_string::{spec::IriSpec, types::IriReferenceStr, validate::Error};
521    /// let iri = IriReferenceStr::new("#")?;
522    /// assert_eq!(iri.fragment_str(), Some(""));
523    /// # Ok::<_, Error>(())
524    /// ```
525    ///
526    /// If the IRI has no fragment, `None` is returned.
527    ///
528    /// ```
529    /// # use iri_string::{spec::IriSpec, types::IriReferenceStr, validate::Error};
530    /// let iri = IriReferenceStr::new("foo://bar/baz?qux=quux")?;
531    /// assert_eq!(iri.fragment(), None);
532    /// # Ok::<_, Error>(())
533    /// ```
534    ///
535    /// ```
536    /// # use iri_string::{spec::IriSpec, types::IriReferenceStr, validate::Error};
537    /// let iri = IriReferenceStr::new("")?;
538    /// assert_eq!(iri.fragment(), None);
539    /// # Ok::<_, Error>(())
540    /// ```
541    #[must_use]
542    pub fn fragment_str(&self) -> Option<&str> {
543        trusted_parser::extract_fragment(self.as_str())
544    }
545
546    /// Returns the authority components.
547    ///
548    /// # Examples
549    ///
550    /// ```
551    /// # use iri_string::validate::Error;
552    /// use iri_string::types::IriReferenceStr;
553    ///
554    /// let iri = IriReferenceStr::new("http://user:pass@example.com:8080/pathpath?queryquery")?;
555    /// let authority = iri.authority_components()
556    ///     .expect("authority is available");
557    /// assert_eq!(authority.userinfo(), Some("user:pass"));
558    /// assert_eq!(authority.host(), "example.com");
559    /// assert_eq!(authority.port(), Some("8080"));
560    /// # Ok::<_, Error>(())
561    /// ```
562    ///
563    /// ```
564    /// # use iri_string::validate::Error;
565    /// use iri_string::types::IriReferenceStr;
566    ///
567    /// let iri = IriReferenceStr::new("foo//bar:baz")?;
568    /// assert_eq!(iri.authority_str(), None);
569    /// # Ok::<_, Error>(())
570    /// ```
571    #[inline]
572    #[must_use]
573    pub fn authority_components(&self) -> Option<AuthorityComponents<'_>> {
574        AuthorityComponents::from_iri(self)
575    }
576}
577
578#[cfg(feature = "alloc")]
579impl<S: Spec> RiReferenceString<S> {
580    /// Returns the string as [`RiString`], if it is valid as an IRI.
581    ///
582    /// If it is not an IRI, then [`RiRelativeString`] is returned as `Err(_)`.
583    ///
584    /// [`RiRelativeString`]: struct.RiRelativeString.html
585    /// [`RiString`]: struct.RiString.html
586    pub fn into_iri(self) -> Result<RiString<S>, RiRelativeString<S>> {
587        let s: String = self.into();
588        // Check with `IRI` rule first, because of the syntax.
589        //
590        // > Some productions are ambiguous. The "first-match-wins" (a.k.a.
591        // > "greedy") algorithm applies. For details, see [RFC3986].
592        // >
593        // > --- <https://www.rfc-editor.org/rfc/rfc3987.html#section-2.2>.
594        if trusted_parser::extract_scheme(&s).is_some() {
595            // SAFETY: an IRI reference with a scheme is a non-relative IRI.
596            Ok(unsafe {
597                RiString::new_unchecked_justified(
598                    s,
599                    "IRI reference with a scheme must be a non-relative IRI reference",
600                )
601            })
602        } else {
603            // SAFETY: if an IRI reference is not an IRI, then it is a relative IRI.
604            // See the RFC 3987 syntax rule `IRI-reference = IRI / irelative-ref`.
605            Err(unsafe {
606                RiRelativeString::new_unchecked_justified(
607                    s,
608                    "non-absolute IRI reference must be a relative IRI reference",
609                )
610            })
611        }
612    }
613
614    /// Returns the string as [`RiRelativeString`], if it is valid as an IRI.
615    ///
616    /// If it is not an IRI, then [`RiString`] is returned as `Err(_)`.
617    ///
618    /// [`RiRelativeString`]: struct.RiRelativeString.html
619    /// [`RiString`]: struct.RiString.html
620    pub fn into_relative_iri(self) -> Result<RiRelativeString<S>, RiString<S>> {
621        match self.into_iri() {
622            Ok(iri) => Err(iri),
623            Err(relative) => Ok(relative),
624        }
625    }
626
627    /// Sets the fragment part to the given string.
628    ///
629    /// Removes fragment part (and following `#` character) if `None` is given.
630    pub fn set_fragment(&mut self, fragment: Option<&RiFragmentStr<S>>) {
631        raw::set_fragment(&mut self.inner, fragment.map(AsRef::as_ref));
632        debug_assert_eq!(Self::validate(&self.inner), Ok(()));
633    }
634
635    /// Removes the password completely (including separator colon) from `self` even if it is empty.
636    ///
637    /// # Examples
638    ///
639    /// ```
640    /// # use iri_string::validate::Error;
641    /// # #[cfg(feature = "alloc")] {
642    /// use iri_string::types::IriReferenceString;
643    ///
644    /// let mut iri = IriReferenceString::try_from("http://user:password@example.com/path?query")?;
645    /// iri.remove_password_inline();
646    /// assert_eq!(iri, "http://user@example.com/path?query");
647    /// # }
648    /// # Ok::<_, Error>(())
649    /// ```
650    ///
651    /// Even if the password is empty, the password and separator will be removed.
652    ///
653    /// ```
654    /// # use iri_string::validate::Error;
655    /// # #[cfg(feature = "alloc")] {
656    /// use iri_string::types::IriReferenceString;
657    ///
658    /// let mut iri = IriReferenceString::try_from("http://user:@example.com/path?query")?;
659    /// iri.remove_password_inline();
660    /// assert_eq!(iri, "http://user@example.com/path?query");
661    /// # }
662    /// # Ok::<_, Error>(())
663    /// ```
664    pub fn remove_password_inline(&mut self) {
665        let pw_range = match password_range_to_hide(self.as_slice()) {
666            Some(v) => v,
667            None => return,
668        };
669        let separator_colon = pw_range.start - 1;
670        // SAFETY: the IRI must be valid after the password component and
671        // the leading separator colon is removed.
672        unsafe {
673            let buf = self.as_inner_mut();
674            buf.drain(separator_colon..pw_range.end);
675            debug_assert_eq!(
676                Self::validate(buf),
677                Ok(()),
678                "the IRI must be valid after the password component is removed"
679            );
680        }
681    }
682
683    /// Replaces the non-empty password in `self` to the empty password.
684    ///
685    /// This leaves the separator colon if the password part was available.
686    ///
687    /// # Examples
688    ///
689    /// ```
690    /// # use iri_string::validate::Error;
691    /// # #[cfg(feature = "alloc")] {
692    /// use iri_string::types::IriReferenceString;
693    ///
694    /// let mut iri = IriReferenceString::try_from("http://user:password@example.com/path?query")?;
695    /// iri.remove_nonempty_password_inline();
696    /// assert_eq!(iri, "http://user:@example.com/path?query");
697    /// # }
698    /// # Ok::<_, Error>(())
699    /// ```
700    ///
701    /// If the password is empty, it is left as is.
702    ///
703    /// ```
704    /// # use iri_string::validate::Error;
705    /// # #[cfg(feature = "alloc")] {
706    /// use iri_string::types::IriReferenceString;
707    ///
708    /// let mut iri = IriReferenceString::try_from("http://user:@example.com/path?query")?;
709    /// iri.remove_nonempty_password_inline();
710    /// assert_eq!(iri, "http://user:@example.com/path?query");
711    /// # }
712    /// # Ok::<_, Error>(())
713    /// ```
714    pub fn remove_nonempty_password_inline(&mut self) {
715        let pw_range = match password_range_to_hide(self.as_slice()) {
716            Some(v) if !v.is_empty() => v,
717            _ => return,
718        };
719        debug_assert_eq!(
720            self.as_str().as_bytes().get(pw_range.start - 1).copied(),
721            Some(b':'),
722            "the password component must be prefixed with a separator colon"
723        );
724        // SAFETY: the IRI must be valid after the password component is
725        // replaced with the empty password.
726        unsafe {
727            let buf = self.as_inner_mut();
728            buf.drain(pw_range);
729            debug_assert_eq!(
730                Self::validate(buf),
731                Ok(()),
732                "the IRI must be valid after the password component \
733                 is replaced with the empty password"
734            );
735        }
736    }
737
738    /// Replaces the host in-place and returns the new host, if authority is not empty.
739    ///
740    /// If the IRI has no authority, returns `None` without doing nothing. Note
741    /// that an empty host is distinguished from the absence of an authority.
742    ///
743    /// If the new host is invalid (i.e., [`validate::validate_host`][`crate::validate::host`]
744    /// returns `Err(_)`), also returns `None` without doing anything.
745    fn try_replace_host_impl(
746        &mut self,
747        new_host: &'_ str,
748        replace_only_reg_name: bool,
749    ) -> Result<Option<&str>, TryReserveError> {
750        use crate::types::generic::replace_domain_impl;
751
752        let result: Result<Option<core::ops::Range<usize>>, TryReserveError>;
753        {
754            // SAFETY: Replacing the (already existing) host part with another
755            // valid host does not change the class of an IRI.
756            let strbuf = unsafe { self.as_inner_mut() };
757            result = replace_domain_impl::<S>(strbuf, new_host, replace_only_reg_name);
758            debug_assert_eq!(
759                RiReferenceStr::<S>::validate(strbuf),
760                Ok(()),
761                "replacing a host with another valid host must keep an IRI valid: raw={strbuf:?}",
762            );
763        }
764        result.map(|opt| opt.map(|range| &self.as_str()[range]))
765    }
766
767    /// Replaces the host in-place and returns the new host, if authority is not empty.
768    ///
769    /// If the IRI has no authority, returns `None` without doing nothing. Note
770    /// that an empty host is distinguished from the absence of an authority.
771    ///
772    /// If the new host is invalid (i.e., [`validate::validate_host`][`crate::validate::host`]
773    /// returns `Err(_)`), also returns `None` without doing anything.
774    ///
775    /// If you need to replace only when the host is `reg-name` (for example
776    /// when you attempt to apply IDNA encoding), use
777    /// [`try_replace_host_reg_name`][`Self::try_replace_host_reg_name`] method
778    /// instead.
779    ///
780    /// # Examples
781    ///
782    /// ```
783    /// # use iri_string::types::UriReferenceString;
784    /// let mut iri =
785    ///     UriReferenceString::try_from("https://user:pass@example.com:443/").unwrap();
786    /// let new_host_opt = iri.replace_host("www.example.com");
787    /// assert_eq!(new_host_opt, Some("www.example.com"));
788    /// assert_eq!(iri.authority_components().unwrap().host(), "www.example.com");
789    /// assert_eq!(iri, "https://user:pass@www.example.com:443/");
790    /// ```
791    pub fn replace_host(&mut self, new_host: &'_ str) -> Option<&str> {
792        self.try_replace_host(new_host)
793            .expect("failed to allocate memory when replacing the host part of an IRI")
794    }
795
796    /// Replaces the host in-place and returns the new host, if authority is not empty.
797    ///
798    /// This returns `TryReserveError` on memory allocation failure, instead of
799    /// panicking. Otherwise, this method behaves same as
800    /// [`replace_host`][`Self::replace_host`] method.
801    pub fn try_replace_host(&mut self, new_host: &'_ str) -> Result<Option<&str>, TryReserveError> {
802        self.try_replace_host_impl(new_host, false)
803    }
804
805    /// Replaces the domain name (`reg-name`) in-place and returns the new host,
806    /// if authority is not empty.
807    ///
808    /// If the IRI has no authority or the host is not a reg-name (i.e., is
809    /// neither an IP-address nor empty), returns `None` without doing nothing.
810    /// Note that an empty host is distinguished from the absence of an authority.
811    ///
812    /// If the new host is invalid (i.e., [`validate::validate_host`][`crate::validate::host`]
813    /// returns `Err(_)`), also returns `None` without doing anything.
814    ///
815    /// # Examples
816    ///
817    /// ```
818    /// # use iri_string::types::UriReferenceString;
819    /// let mut iri =
820    ///     UriReferenceString::try_from("https://user:pass@example.com:443/").unwrap();
821    /// let new_host_opt = iri.replace_host("www.example.com");
822    /// assert_eq!(new_host_opt, Some("www.example.com"));
823    /// assert_eq!(iri.authority_components().unwrap().host(), "www.example.com");
824    /// assert_eq!(iri, "https://user:pass@www.example.com:443/");
825    /// ```
826    ///
827    /// ```
828    /// # use iri_string::types::UriReferenceString;
829    /// let mut iri =
830    ///     UriReferenceString::try_from("https://192.168.0.1/").unwrap();
831    /// let new_host_opt = iri.replace_host_reg_name("localhost");
832    /// assert_eq!(new_host_opt, None, "IPv4 address is not a reg-name");
833    /// assert_eq!(iri, "https://192.168.0.1/", "won't be changed");
834    /// ```
835    ///
836    /// To apply IDNA conversion, get the domain by [`AuthorityComponents::reg_name`]
837    /// method, convert the domain, and then set it by this
838    /// `replace_host_reg_name` method.
839    ///
840    /// ```
841    /// # extern crate alloc;
842    /// # use alloc::string::String;
843    /// # use iri_string::types::IriReferenceString;
844    /// /// Converts the given into IDNA encoding.
845    /// fn conv_idna(domain: &str) -> String {
846    ///     /* ... */
847    /// #   if domain == "\u{03B1}.example.com" {
848    /// #       "xn--mxa.example.com".into()
849    /// #   } else {
850    /// #       unimplemented!()
851    /// #   }
852    /// }
853    ///
854    /// // U+03B1: GREEK SMALL LETTER ALPHA
855    /// let mut iri =
856    ///     IriReferenceString::try_from("https://\u{03B1}.example.com/").unwrap();
857    ///
858    /// let old_domain = iri
859    ///     .authority_components()
860    ///     .expect("authority is not empty")
861    ///     .reg_name()
862    ///     .expect("the host is reg-name");
863    /// assert_eq!(old_domain, "\u{03B1}.example.com");
864    ///
865    /// // Get the new host by your own.
866    /// let new_domain: String = conv_idna(old_domain);
867    /// assert_eq!(new_domain, "xn--mxa.example.com");
868    ///
869    /// let new_host_opt = iri.replace_host(&new_domain);
870    /// assert_eq!(new_host_opt, Some("xn--mxa.example.com"));
871    /// assert_eq!(iri.authority_components().unwrap().host(), "xn--mxa.example.com");
872    /// assert_eq!(iri, "https://xn--mxa.example.com/");
873    /// ```
874    pub fn replace_host_reg_name(&mut self, new_host: &'_ str) -> Option<&str> {
875        self.try_replace_host_reg_name(new_host)
876            .expect("failed to allocate memory when replacing the host part of an IRI")
877    }
878
879    /// Replaces the domain name (`reg-name`) in-place and returns the new host,
880    /// if authority is not empty.
881    ///
882    /// This returns `TryReserveError` on memory allocation failure, instead of
883    /// panicking. Otherwise, this method behaves same as
884    /// [`replace_host_reg_name`][`Self::replace_host_reg_name`] method.
885    pub fn try_replace_host_reg_name(
886        &mut self,
887        new_host: &'_ str,
888    ) -> Result<Option<&str>, TryReserveError> {
889        self.try_replace_host_impl(new_host, true)
890    }
891}