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}