iri_string/types/generic/
reference.rs

1//! IRI reference.
2
3#[cfg(all(feature = "alloc", not(feature = "std")))]
4use alloc::string::String;
5
6use crate::components::AuthorityComponents;
7#[cfg(feature = "alloc")]
8use crate::mask_password::password_range_to_hide;
9use crate::mask_password::PasswordMasked;
10use crate::normalize::Normalized;
11use crate::parser::trusted as trusted_parser;
12#[cfg(feature = "alloc")]
13use crate::raw;
14use crate::resolve::FixedBaseResolver;
15use crate::spec::Spec;
16use crate::types::{RiAbsoluteStr, RiFragmentStr, RiQueryStr, RiRelativeStr, RiStr};
17#[cfg(feature = "alloc")]
18use crate::types::{RiRelativeString, RiString};
19#[cfg(feature = "alloc")]
20use crate::validate::iri;
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://tools.ietf.org/html/rfc3986
67    /// [RFC 3987]: https://tools.ietf.org/html/rfc3987
68    /// [`IRI-reference` rule]: https://tools.ietf.org/html/rfc3987#section-2.2
69    /// [`URI-reference` rule]: https://tools.ietf.org/html/rfc3986#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://tools.ietf.org/html/rfc3986
92    /// [RFC 3987]: https://tools.ietf.org/html/rfc3987
93    /// [`IRI-reference` rule]: https://tools.ietf.org/html/rfc3987#section-2.2
94    /// [`URI-reference` rule]: https://tools.ietf.org/html/rfc3986#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://tools.ietf.org/html/rfc3987#section-2.2>.
120
121        <&RiStr<S>>::try_from(self.as_str()).map_err(|_| {
122            // SAFETY: if an IRI reference is not an IRI, then it is a relative IRI.
123            // See the RFC 3987 syntax rule `IRI-reference = IRI / irelative-ref`.
124            unsafe { RiRelativeStr::new_maybe_unchecked(self.as_str()) }
125        })
126    }
127
128    /// Returns the string as [`&RiRelativeStr`][`RiRelativeStr`], if it is valid as an IRI.
129    ///
130    /// If it is not an IRI, then [`&RiStr`][`RiStr`] is returned as `Err(_)`.
131    ///
132    /// [`RiRelativeStr`]: struct.RiRelativeStr.html
133    /// [`RiStr`]: struct.RiStr.html
134    pub fn to_relative_iri(&self) -> Result<&RiRelativeStr<S>, &RiStr<S>> {
135        match self.to_iri() {
136            Ok(iri) => Err(iri),
137            Err(relative) => Ok(relative),
138        }
139    }
140
141    /// Returns resolved IRI against the given base IRI.
142    ///
143    /// For IRI reference resolution output examples, see [RFC 3986 section 5.4].
144    ///
145    /// If you are going to resolve multiple references against the common base,
146    /// consider using [`FixedBaseResolver`].
147    ///
148    /// # Strictness
149    ///
150    /// The IRI parsers provided by this crate is strict (e.g. `http:g` is
151    /// always interpreted as a composition of the scheme `http` and the path
152    /// `g`), so backward compatible parsing and resolution are not provided.
153    /// About parser and resolver strictness, see [RFC 3986 section 5.4.2]:
154    ///
155    /// > Some parsers allow the scheme name to be present in a relative
156    /// > reference if it is the same as the base URI scheme. This is considered
157    /// > to be a loophole in prior specifications of partial URI
158    /// > [RFC1630](https://tools.ietf.org/html/rfc1630). Its use should be
159    /// > avoided but is allowed for backward compatibility.
160    /// >
161    /// > --- <https://tools.ietf.org/html/rfc3986#section-5.4.2>
162    ///
163    /// # Failures
164    ///
165    /// This method itself does not fail, but IRI resolution without WHATWG URL
166    /// Standard serialization can fail in some minor cases.
167    ///
168    /// To see examples of such unresolvable IRIs, visit the documentation
169    /// for [`normalize`][`crate::normalize`] module.
170    ///
171    /// [RFC 3986 section 5.4]: https://tools.ietf.org/html/rfc3986#section-5.4
172    /// [RFC 3986 section 5.4.2]: https://tools.ietf.org/html/rfc3986#section-5.4.2
173    pub fn resolve_against<'a>(&'a self, base: &'a RiAbsoluteStr<S>) -> Normalized<'a, RiStr<S>> {
174        FixedBaseResolver::new(base).resolve(self.as_ref())
175    }
176
177    /// Returns the proxy to the IRI with password masking feature.
178    ///
179    /// # Examples
180    ///
181    /// ```
182    /// # use iri_string::validate::Error;
183    /// # #[cfg(feature = "alloc")] {
184    /// use iri_string::format::ToDedicatedString;
185    /// use iri_string::types::IriReferenceStr;
186    ///
187    /// let iri = IriReferenceStr::new("http://user:password@example.com/path?query")?;
188    /// let masked = iri.mask_password();
189    /// assert_eq!(masked.to_dedicated_string(), "http://user:@example.com/path?query");
190    ///
191    /// assert_eq!(
192    ///     masked.replace_password("${password}").to_string(),
193    ///     "http://user:${password}@example.com/path?query"
194    /// );
195    /// # }
196    /// # Ok::<_, Error>(())
197    /// ```
198    #[inline]
199    #[must_use]
200    pub fn mask_password(&self) -> PasswordMasked<'_, Self> {
201        PasswordMasked::new(self)
202    }
203}
204
205/// Components getters.
206impl<S: Spec> RiReferenceStr<S> {
207    /// Returns the scheme.
208    ///
209    /// The following colon is truncated.
210    ///
211    /// # Examples
212    ///
213    /// ```
214    /// # use iri_string::validate::Error;
215    /// use iri_string::types::IriReferenceStr;
216    ///
217    /// let iri = IriReferenceStr::new("http://example.com/pathpath?queryquery#fragfrag")?;
218    /// assert_eq!(iri.scheme_str(), Some("http"));
219    /// # Ok::<_, Error>(())
220    /// ```
221    ///
222    /// ```
223    /// # use iri_string::validate::Error;
224    /// use iri_string::types::IriReferenceStr;
225    ///
226    /// let iri = IriReferenceStr::new("foo/bar:baz")?;
227    /// assert_eq!(iri.scheme_str(), None);
228    /// # Ok::<_, Error>(())
229    /// ```
230    #[inline]
231    #[must_use]
232    pub fn scheme_str(&self) -> Option<&str> {
233        trusted_parser::extract_scheme(self.as_str())
234    }
235
236    /// Returns the authority.
237    ///
238    /// The leading `//` is truncated.
239    ///
240    /// # Examples
241    ///
242    /// ```
243    /// # use iri_string::validate::Error;
244    /// use iri_string::types::IriReferenceStr;
245    ///
246    /// let iri = IriReferenceStr::new("http://example.com/pathpath?queryquery#fragfrag")?;
247    /// assert_eq!(iri.authority_str(), Some("example.com"));
248    /// # Ok::<_, Error>(())
249    /// ```
250    ///
251    /// ```
252    /// # use iri_string::validate::Error;
253    /// use iri_string::types::IriReferenceStr;
254    ///
255    /// let iri = IriReferenceStr::new("urn:uuid:10db315b-fcd1-4428-aca8-15babc9a2da2")?;
256    /// assert_eq!(iri.authority_str(), None);
257    /// # Ok::<_, Error>(())
258    /// ```
259    ///
260    /// ```
261    /// # use iri_string::validate::Error;
262    /// use iri_string::types::IriReferenceStr;
263    ///
264    /// let iri = IriReferenceStr::new("foo/bar:baz")?;
265    /// assert_eq!(iri.authority_str(), None);
266    /// # Ok::<_, Error>(())
267    /// ```
268    #[inline]
269    #[must_use]
270    pub fn authority_str(&self) -> Option<&str> {
271        trusted_parser::extract_authority(self.as_str())
272    }
273
274    /// Returns the path.
275    ///
276    /// # Examples
277    ///
278    /// ```
279    /// # use iri_string::validate::Error;
280    /// use iri_string::types::IriReferenceStr;
281    ///
282    /// let iri = IriReferenceStr::new("http://example.com/pathpath?queryquery#fragfrag")?;
283    /// assert_eq!(iri.path_str(), "/pathpath");
284    /// # Ok::<_, Error>(())
285    /// ```
286    ///
287    /// ```
288    /// # use iri_string::validate::Error;
289    /// use iri_string::types::IriReferenceStr;
290    ///
291    /// let iri = IriReferenceStr::new("urn:uuid:10db315b-fcd1-4428-aca8-15babc9a2da2")?;
292    /// assert_eq!(iri.path_str(), "uuid:10db315b-fcd1-4428-aca8-15babc9a2da2");
293    /// # Ok::<_, Error>(())
294    /// ```
295    ///
296    /// ```
297    /// # use iri_string::validate::Error;
298    /// use iri_string::types::IriReferenceStr;
299    ///
300    /// let iri = IriReferenceStr::new("foo/bar:baz")?;
301    /// assert_eq!(iri.path_str(), "foo/bar:baz");
302    /// # Ok::<_, Error>(())
303    /// ```
304    #[inline]
305    #[must_use]
306    pub fn path_str(&self) -> &str {
307        trusted_parser::extract_path(self.as_str())
308    }
309
310    /// Returns the query.
311    ///
312    /// The leading question mark (`?`) is truncated.
313    ///
314    /// # Examples
315    ///
316    /// ```
317    /// # use iri_string::validate::Error;
318    /// use iri_string::types::{IriQueryStr, IriReferenceStr};
319    ///
320    /// let iri = IriReferenceStr::new("http://example.com/pathpath?queryquery#fragfrag")?;
321    /// let query = IriQueryStr::new("queryquery")?;
322    /// assert_eq!(iri.query(), Some(query));
323    /// # Ok::<_, Error>(())
324    /// ```
325    ///
326    /// ```
327    /// # use iri_string::validate::Error;
328    /// use iri_string::types::IriReferenceStr;
329    ///
330    /// let iri = IriReferenceStr::new("urn:uuid:10db315b-fcd1-4428-aca8-15babc9a2da2")?;
331    /// assert_eq!(iri.query(), None);
332    /// # Ok::<_, Error>(())
333    /// ```
334    ///
335    /// ```
336    /// # use iri_string::validate::Error;
337    /// use iri_string::types::{IriQueryStr, IriReferenceStr};
338    ///
339    /// let iri = IriReferenceStr::new("foo/bar:baz?")?;
340    /// let query = IriQueryStr::new("")?;
341    /// assert_eq!(iri.query(), Some(query));
342    /// # Ok::<_, Error>(())
343    /// ```
344    #[inline]
345    #[must_use]
346    pub fn query(&self) -> Option<&RiQueryStr<S>> {
347        trusted_parser::extract_query(self.as_str()).map(|query| {
348            // SAFETY: `extract_query` returns the query part of an IRI, and the
349            // returned string should have only valid characters since is the
350            // substring of the source IRI.
351            unsafe { RiQueryStr::new_maybe_unchecked(query) }
352        })
353    }
354
355    /// Returns the query as a raw string slice.
356    ///
357    /// The leading question mark (`?`) is truncated.
358    ///
359    /// # Examples
360    ///
361    /// ```
362    /// # use iri_string::validate::Error;
363    /// use iri_string::types::IriReferenceStr;
364    ///
365    /// let iri = IriReferenceStr::new("http://example.com/pathpath?queryquery#fragfrag")?;
366    /// assert_eq!(iri.query_str(), Some("queryquery"));
367    /// # Ok::<_, Error>(())
368    /// ```
369    ///
370    /// ```
371    /// # use iri_string::validate::Error;
372    /// use iri_string::types::IriReferenceStr;
373    ///
374    /// let iri = IriReferenceStr::new("urn:uuid:10db315b-fcd1-4428-aca8-15babc9a2da2")?;
375    /// assert_eq!(iri.query_str(), None);
376    /// # Ok::<_, Error>(())
377    /// ```
378    ///
379    /// ```
380    /// # use iri_string::validate::Error;
381    /// use iri_string::types::IriReferenceStr;
382    ///
383    /// let iri = IriReferenceStr::new("foo/bar:baz?")?;
384    /// assert_eq!(iri.query_str(), Some(""));
385    /// # Ok::<_, Error>(())
386    /// ```
387    #[inline]
388    #[must_use]
389    pub fn query_str(&self) -> Option<&str> {
390        trusted_parser::extract_query(self.as_str())
391    }
392
393    /// Returns the fragment part if exists.
394    ///
395    /// A leading `#` character is truncated if the fragment part exists.
396    ///
397    /// # Examples
398    ///
399    /// If the IRI has a fragment part, `Some(_)` is returned.
400    ///
401    /// ```
402    /// # use iri_string::{spec::IriSpec, types::{IriFragmentStr, IriReferenceStr}, validate::Error};
403    /// let iri = IriReferenceStr::new("foo://bar/baz?qux=quux#corge")?;
404    /// let fragment = IriFragmentStr::new("corge")?;
405    /// assert_eq!(iri.fragment(), Some(fragment));
406    /// # Ok::<_, Error>(())
407    /// ```
408    ///
409    /// ```
410    /// # use iri_string::{spec::IriSpec, types::{IriFragmentStr, IriReferenceStr}, validate::Error};
411    /// let iri = IriReferenceStr::new("#foo")?;
412    /// let fragment = IriFragmentStr::new("foo")?;
413    /// assert_eq!(iri.fragment(), Some(fragment));
414    /// # Ok::<_, Error>(())
415    /// ```
416    ///
417    /// When the fragment part exists but is empty string, `Some(_)` is returned.
418    ///
419    /// ```
420    /// # use iri_string::{spec::IriSpec, types::{IriFragmentStr, IriReferenceStr}, validate::Error};
421    /// let iri = IriReferenceStr::new("foo://bar/baz?qux=quux#")?;
422    /// let fragment = IriFragmentStr::new("")?;
423    /// assert_eq!(iri.fragment(), Some(fragment));
424    /// # Ok::<_, Error>(())
425    /// ```
426    ///
427    /// ```
428    /// # use iri_string::{spec::IriSpec, types::{IriFragmentStr, IriReferenceStr}, validate::Error};
429    /// let iri = IriReferenceStr::new("#")?;
430    /// let fragment = IriFragmentStr::new("")?;
431    /// assert_eq!(iri.fragment(), Some(fragment));
432    /// # Ok::<_, Error>(())
433    /// ```
434    ///
435    /// If the IRI has no fragment, `None` is returned.
436    ///
437    /// ```
438    /// # use iri_string::{spec::IriSpec, types::IriReferenceStr, validate::Error};
439    /// let iri = IriReferenceStr::new("foo://bar/baz?qux=quux")?;
440    /// assert_eq!(iri.fragment(), None);
441    /// # Ok::<_, Error>(())
442    /// ```
443    ///
444    /// ```
445    /// # use iri_string::{spec::IriSpec, types::IriReferenceStr, validate::Error};
446    /// let iri = IriReferenceStr::new("")?;
447    /// assert_eq!(iri.fragment(), None);
448    /// # Ok::<_, Error>(())
449    /// ```
450    #[must_use]
451    pub fn fragment(&self) -> Option<&RiFragmentStr<S>> {
452        trusted_parser::extract_fragment(self.as_str()).map(|fragment| {
453            // SAFETY: `extract_fragment` returns the fragment part of an IRI,
454            // and the returned string should have only valid characters since
455            // is the substring of the source IRI.
456            unsafe { RiFragmentStr::new_maybe_unchecked(fragment) }
457        })
458    }
459
460    /// Returns the fragment part as a raw string slice if exists.
461    ///
462    /// A leading `#` character is truncated if the fragment part exists.
463    ///
464    /// # Examples
465    ///
466    /// If the IRI has a fragment part, `Some(_)` is returned.
467    ///
468    /// ```
469    /// # use iri_string::{spec::IriSpec, types::IriReferenceStr, validate::Error};
470    /// let iri = IriReferenceStr::new("foo://bar/baz?qux=quux#corge")?;
471    /// assert_eq!(iri.fragment_str(), Some("corge"));
472    /// # Ok::<_, Error>(())
473    /// ```
474    ///
475    /// ```
476    /// # use iri_string::{spec::IriSpec, types::IriReferenceStr, validate::Error};
477    /// let iri = IriReferenceStr::new("#foo")?;
478    /// assert_eq!(iri.fragment_str(), Some("foo"));
479    /// # Ok::<_, Error>(())
480    /// ```
481    ///
482    /// When the fragment part exists but is empty string, `Some(_)` is returned.
483    ///
484    /// ```
485    /// # use iri_string::{spec::IriSpec, types::IriReferenceStr, validate::Error};
486    /// let iri = IriReferenceStr::new("foo://bar/baz?qux=quux#")?;
487    /// assert_eq!(iri.fragment_str(), Some(""));
488    /// # Ok::<_, Error>(())
489    /// ```
490    ///
491    /// ```
492    /// # use iri_string::{spec::IriSpec, types::IriReferenceStr, validate::Error};
493    /// let iri = IriReferenceStr::new("#")?;
494    /// assert_eq!(iri.fragment_str(), Some(""));
495    /// # Ok::<_, Error>(())
496    /// ```
497    ///
498    /// If the IRI has no fragment, `None` is returned.
499    ///
500    /// ```
501    /// # use iri_string::{spec::IriSpec, types::IriReferenceStr, validate::Error};
502    /// let iri = IriReferenceStr::new("foo://bar/baz?qux=quux")?;
503    /// assert_eq!(iri.fragment(), None);
504    /// # Ok::<_, Error>(())
505    /// ```
506    ///
507    /// ```
508    /// # use iri_string::{spec::IriSpec, types::IriReferenceStr, validate::Error};
509    /// let iri = IriReferenceStr::new("")?;
510    /// assert_eq!(iri.fragment(), None);
511    /// # Ok::<_, Error>(())
512    /// ```
513    #[must_use]
514    pub fn fragment_str(&self) -> Option<&str> {
515        trusted_parser::extract_fragment(self.as_str())
516    }
517
518    /// Returns the authority components.
519    ///
520    /// # Examples
521    ///
522    /// ```
523    /// # use iri_string::validate::Error;
524    /// use iri_string::types::IriReferenceStr;
525    ///
526    /// let iri = IriReferenceStr::new("http://user:pass@example.com:8080/pathpath?queryquery")?;
527    /// let authority = iri.authority_components()
528    ///     .expect("authority is available");
529    /// assert_eq!(authority.userinfo(), Some("user:pass"));
530    /// assert_eq!(authority.host(), "example.com");
531    /// assert_eq!(authority.port(), Some("8080"));
532    /// # Ok::<_, Error>(())
533    /// ```
534    ///
535    /// ```
536    /// # use iri_string::validate::Error;
537    /// use iri_string::types::IriReferenceStr;
538    ///
539    /// let iri = IriReferenceStr::new("foo//bar:baz")?;
540    /// assert_eq!(iri.authority_str(), None);
541    /// # Ok::<_, Error>(())
542    /// ```
543    #[inline]
544    #[must_use]
545    pub fn authority_components(&self) -> Option<AuthorityComponents<'_>> {
546        AuthorityComponents::from_iri(self)
547    }
548}
549
550#[cfg(feature = "alloc")]
551impl<S: Spec> RiReferenceString<S> {
552    /// Returns the string as [`RiString`], if it is valid as an IRI.
553    ///
554    /// If it is not an IRI, then [`RiRelativeString`] is returned as `Err(_)`.
555    ///
556    /// [`RiRelativeString`]: struct.RiRelativeString.html
557    /// [`RiString`]: struct.RiString.html
558    pub fn into_iri(self) -> Result<RiString<S>, RiRelativeString<S>> {
559        let s: String = self.into();
560        // Check with `IRI` rule first, because of the syntax.
561        //
562        // > Some productions are ambiguous. The "first-match-wins" (a.k.a.
563        // > "greedy") algorithm applies. For details, see [RFC3986].
564        // >
565        // > --- <https://tools.ietf.org/html/rfc3987#section-2.2>.
566        if iri::<S>(&s).is_ok() {
567            // SAFETY: just checked `s` is valid as an IRI.
568            Ok(unsafe { RiString::new_always_unchecked(s) })
569        } else {
570            // SAFETY: if an IRI reference is not an IRI, then it is a relative IRI.
571            // See the RFC 3987 syntax rule `IRI-reference = IRI / irelative-ref`.
572            Err(unsafe { RiRelativeString::new_maybe_unchecked(s) })
573        }
574    }
575
576    /// Returns the string as [`RiRelativeString`], if it is valid as an IRI.
577    ///
578    /// If it is not an IRI, then [`RiString`] is returned as `Err(_)`.
579    ///
580    /// [`RiRelativeString`]: struct.RiRelativeString.html
581    /// [`RiString`]: struct.RiString.html
582    pub fn into_relative_iri(self) -> Result<RiRelativeString<S>, RiString<S>> {
583        match self.into_iri() {
584            Ok(iri) => Err(iri),
585            Err(relative) => Ok(relative),
586        }
587    }
588
589    /// Sets the fragment part to the given string.
590    ///
591    /// Removes fragment part (and following `#` character) if `None` is given.
592    pub fn set_fragment(&mut self, fragment: Option<&RiFragmentStr<S>>) {
593        raw::set_fragment(&mut self.inner, fragment.map(AsRef::as_ref));
594        debug_assert!(iri_reference::<S>(&self.inner).is_ok());
595    }
596
597    /// Removes the password completely (including separator colon) from `self` even if it is empty.
598    ///
599    /// # Examples
600    ///
601    /// ```
602    /// # use iri_string::validate::Error;
603    /// # #[cfg(feature = "alloc")] {
604    /// use iri_string::types::IriReferenceString;
605    ///
606    /// let mut iri = IriReferenceString::try_from("http://user:password@example.com/path?query")?;
607    /// iri.remove_password_inline();
608    /// assert_eq!(iri, "http://user@example.com/path?query");
609    /// # }
610    /// # Ok::<_, Error>(())
611    /// ```
612    ///
613    /// Even if the password is empty, the password and separator will be removed.
614    ///
615    /// ```
616    /// # use iri_string::validate::Error;
617    /// # #[cfg(feature = "alloc")] {
618    /// use iri_string::types::IriReferenceString;
619    ///
620    /// let mut iri = IriReferenceString::try_from("http://user:@example.com/path?query")?;
621    /// iri.remove_password_inline();
622    /// assert_eq!(iri, "http://user@example.com/path?query");
623    /// # }
624    /// # Ok::<_, Error>(())
625    /// ```
626    pub fn remove_password_inline(&mut self) {
627        let pw_range = match password_range_to_hide(self.as_slice()) {
628            Some(v) => v,
629            None => return,
630        };
631        let separator_colon = pw_range.start - 1;
632        // SAFETY: the IRI must be valid after the password component and
633        // the leading separator colon is removed.
634        unsafe {
635            let buf = self.as_inner_mut();
636            buf.drain(separator_colon..pw_range.end);
637            debug_assert!(
638                RiReferenceStr::<S>::new(buf).is_ok(),
639                "[validity] the IRI must be valid after the password component is removed"
640            );
641        }
642    }
643
644    /// Replaces the non-empty password in `self` to the empty password.
645    ///
646    /// This leaves the separator colon if the password part was available.
647    ///
648    /// # Examples
649    ///
650    /// ```
651    /// # use iri_string::validate::Error;
652    /// # #[cfg(feature = "alloc")] {
653    /// use iri_string::types::IriReferenceString;
654    ///
655    /// let mut iri = IriReferenceString::try_from("http://user:password@example.com/path?query")?;
656    /// iri.remove_nonempty_password_inline();
657    /// assert_eq!(iri, "http://user:@example.com/path?query");
658    /// # }
659    /// # Ok::<_, Error>(())
660    /// ```
661    ///
662    /// If the password is empty, it is left as is.
663    ///
664    /// ```
665    /// # use iri_string::validate::Error;
666    /// # #[cfg(feature = "alloc")] {
667    /// use iri_string::types::IriReferenceString;
668    ///
669    /// let mut iri = IriReferenceString::try_from("http://user:@example.com/path?query")?;
670    /// iri.remove_nonempty_password_inline();
671    /// assert_eq!(iri, "http://user:@example.com/path?query");
672    /// # }
673    /// # Ok::<_, Error>(())
674    /// ```
675    pub fn remove_nonempty_password_inline(&mut self) {
676        let pw_range = match password_range_to_hide(self.as_slice()) {
677            Some(v) if !v.is_empty() => v,
678            _ => return,
679        };
680        debug_assert_eq!(
681            self.as_str().as_bytes().get(pw_range.start - 1).copied(),
682            Some(b':'),
683            "[validity] the password component must be prefixed with a separator colon"
684        );
685        // SAFETY: the IRI must be valid after the password component is
686        // replaced with the empty password.
687        unsafe {
688            let buf = self.as_inner_mut();
689            buf.drain(pw_range);
690            debug_assert!(
691                RiReferenceStr::<S>::new(buf).is_ok(),
692                "[validity] the IRI must be valid after the password component \
693                 is replaced with the empty password"
694            );
695        }
696    }
697}