iri_string/types/generic/absolute.rs
1//! Absolute IRI (without fragment part).
2
3use crate::components::AuthorityComponents;
4#[cfg(feature = "alloc")]
5use crate::mask_password::password_range_to_hide;
6use crate::mask_password::PasswordMasked;
7use crate::normalize::{Error, NormalizationInput, Normalized, NormalizednessCheckMode};
8use crate::parser::trusted as trusted_parser;
9use crate::spec::Spec;
10use crate::types::{RiQueryStr, RiReferenceStr, RiStr};
11#[cfg(feature = "alloc")]
12use crate::types::{RiReferenceString, RiString};
13use crate::validate::absolute_iri;
14
15define_custom_string_slice! {
16 /// A borrowed slice of an absolute IRI without fragment part.
17 ///
18 /// This corresponds to [`absolute-IRI` rule] in [RFC 3987]
19 /// (and [`absolute-URI` rule] in [RFC 3986]).
20 /// In other words, this is [`RiStr`] without fragment part.
21 ///
22 /// If you want to accept fragment part, use [`RiStr`].
23 ///
24 /// # Valid values
25 ///
26 /// This type can have an absolute IRI without fragment part.
27 ///
28 /// ```
29 /// # use iri_string::types::IriAbsoluteStr;
30 /// assert!(IriAbsoluteStr::new("https://example.com/foo?bar=baz").is_ok());
31 /// assert!(IriAbsoluteStr::new("foo:bar").is_ok());
32 /// // Scheme `foo` and empty path.
33 /// assert!(IriAbsoluteStr::new("foo:").is_ok());
34 /// // `foo://.../` below are all allowed. See the crate documentation for detail.
35 /// assert!(IriAbsoluteStr::new("foo:/").is_ok());
36 /// assert!(IriAbsoluteStr::new("foo://").is_ok());
37 /// assert!(IriAbsoluteStr::new("foo:///").is_ok());
38 /// assert!(IriAbsoluteStr::new("foo:////").is_ok());
39 /// assert!(IriAbsoluteStr::new("foo://///").is_ok());
40 ///
41 /// ```
42 ///
43 /// Relative IRI is not allowed.
44 ///
45 /// ```
46 /// # use iri_string::types::IriAbsoluteStr;
47 /// // This is relative path.
48 /// assert!(IriAbsoluteStr::new("foo/bar").is_err());
49 /// // `/foo/bar` is an absolute path, but it is authority-relative.
50 /// assert!(IriAbsoluteStr::new("/foo/bar").is_err());
51 /// // `//foo/bar` is termed "network-path reference",
52 /// // or usually called "protocol-relative reference".
53 /// assert!(IriAbsoluteStr::new("//foo/bar").is_err());
54 /// // Empty string is not a valid absolute IRI.
55 /// assert!(IriAbsoluteStr::new("").is_err());
56 /// ```
57 ///
58 /// Fragment part (such as trailing `#foo`) is not allowed.
59 ///
60 /// ```
61 /// # use iri_string::types::IriAbsoluteStr;
62 /// // Fragment part is not allowed.
63 /// assert!(IriAbsoluteStr::new("https://example.com/foo?bar=baz#qux").is_err());
64 /// ```
65 ///
66 /// Some characters and sequences cannot used in an absolute IRI.
67 ///
68 /// ```
69 /// # use iri_string::types::IriAbsoluteStr;
70 /// // `<` and `>` cannot directly appear in an absolute IRI.
71 /// assert!(IriAbsoluteStr::new("<not allowed>").is_err());
72 /// // Broken percent encoding cannot appear in an absolute IRI.
73 /// assert!(IriAbsoluteStr::new("%").is_err());
74 /// assert!(IriAbsoluteStr::new("%GG").is_err());
75 /// ```
76 ///
77 /// [RFC 3986]: https://tools.ietf.org/html/rfc3986
78 /// [RFC 3987]: https://tools.ietf.org/html/rfc3987
79 /// [`absolute-IRI` rule]: https://tools.ietf.org/html/rfc3987#section-2.2
80 /// [`absolute-URI` rule]: https://tools.ietf.org/html/rfc3986#section-4.3
81 /// [`RiStr`]: struct.RiStr.html
82 struct RiAbsoluteStr {
83 validator = absolute_iri,
84 expecting_msg = "Absolute IRI string",
85 }
86}
87
88#[cfg(feature = "alloc")]
89define_custom_string_owned! {
90 /// An owned string of an absolute IRI without fragment part.
91 ///
92 /// This corresponds to [`absolute-IRI` rule] in [RFC 3987]
93 /// (and [`absolute-URI` rule] in [RFC 3986]).
94 /// The rule for `absolute-IRI` is `scheme ":" ihier-part [ "?" iquery ]`.
95 /// In other words, this is [`RiString`] without fragment part.
96 ///
97 /// If you want to accept fragment part, use [`RiString`].
98 ///
99 /// For details, see the document for [`RiAbsoluteStr`].
100 ///
101 /// Enabled by `alloc` or `std` feature.
102 ///
103 /// [RFC 3986]: https://tools.ietf.org/html/rfc3986
104 /// [RFC 3987]: https://tools.ietf.org/html/rfc3987
105 /// [`absolute-IRI` rule]: https://tools.ietf.org/html/rfc3987#section-2.2
106 /// [`absolute-URI` rule]: https://tools.ietf.org/html/rfc3986#section-4.3
107 /// [`RiAbsoluteStr`]: struct.RiAbsoluteStr.html
108 /// [`RiString`]: struct.RiString.html
109 struct RiAbsoluteString {
110 validator = absolute_iri,
111 slice = RiAbsoluteStr,
112 expecting_msg = "Absolute IRI string",
113 }
114}
115
116impl<S: Spec> RiAbsoluteStr<S> {
117 /// Returns Ok`(())` if the IRI is normalizable by the RFC 3986 algorithm.
118 ///
119 /// # Examples
120 ///
121 /// ```
122 /// # use iri_string::validate::Error;
123 /// use iri_string::types::IriAbsoluteStr;
124 ///
125 /// let iri = IriAbsoluteStr::new("HTTP://example.COM/foo/%2e/bar/..")?;
126 /// assert!(iri.ensure_rfc3986_normalizable().is_ok());
127 ///
128 /// let iri2 = IriAbsoluteStr::new("scheme:/..//bar")?;
129 /// // The normalization result would be `scheme://bar` according to RFC
130 /// // 3986, but it is unintended and should be treated as a failure.
131 /// // This crate automatically handles this case so that `.normalize()` won't fail.
132 /// assert!(!iri.ensure_rfc3986_normalizable().is_err());
133 /// # Ok::<_, Error>(())
134 /// ```
135 #[inline]
136 pub fn ensure_rfc3986_normalizable(&self) -> Result<(), Error> {
137 NormalizationInput::from(self).ensure_rfc3986_normalizable()
138 }
139
140 /// Returns `true` if the IRI is already normalized.
141 ///
142 /// This returns the same result as `self.normalize().to_string() == self`,
143 /// but does this more efficiently without heap allocation.
144 ///
145 /// # Examples
146 ///
147 /// ```
148 /// # use iri_string::validate::Error;
149 /// # #[cfg(feature = "alloc")] {
150 /// use iri_string::format::ToDedicatedString;
151 /// use iri_string::types::IriAbsoluteStr;
152 ///
153 /// let iri = IriAbsoluteStr::new("HTTP://example.COM/foo/./bar/%2e%2e/../baz?query")?;
154 /// assert!(!iri.is_normalized());
155 ///
156 /// let normalized = iri.normalize().to_dedicated_string();
157 /// assert_eq!(normalized, "http://example.com/baz?query");
158 /// assert!(normalized.is_normalized());
159 /// # }
160 /// # Ok::<_, Error>(())
161 /// ```
162 ///
163 /// ```
164 /// # use iri_string::validate::Error;
165 /// # #[cfg(feature = "alloc")] {
166 /// use iri_string::format::ToDedicatedString;
167 /// use iri_string::types::IriAbsoluteStr;
168 ///
169 /// let iri = IriAbsoluteStr::new("scheme:/.///foo")?;
170 /// // Already normalized.
171 /// assert!(iri.is_normalized());
172 /// # }
173 /// # Ok::<_, Error>(())
174 /// ```
175 ///
176 /// ```
177 /// # use iri_string::validate::Error;
178 /// # #[cfg(feature = "alloc")] {
179 /// use iri_string::format::ToDedicatedString;
180 /// use iri_string::types::IriAbsoluteStr;
181 ///
182 /// let iri = IriAbsoluteStr::new("scheme:relative/..//not-a-host")?;
183 /// // Default normalization algorithm assumes the path part to be NOT opaque.
184 /// assert!(!iri.is_normalized());
185 ///
186 /// let normalized = iri.normalize().to_dedicated_string();
187 /// assert_eq!(normalized, "scheme:/.//not-a-host");
188 /// # }
189 /// # Ok::<_, Error>(())
190 /// ```
191 #[inline]
192 #[must_use]
193 pub fn is_normalized(&self) -> bool {
194 trusted_parser::is_normalized::<S>(self.as_str(), NormalizednessCheckMode::Default)
195 }
196
197 /// Returns `true` if the IRI is already normalized.
198 ///
199 /// This returns the same result as
200 /// `self.ensure_rfc3986_normalizable() && (self.normalize().to_string() == self)`,
201 /// does this more efficiently without heap allocation.
202 ///
203 /// # Examples
204 ///
205 /// ```
206 /// # use iri_string::validate::Error;
207 /// # #[cfg(feature = "alloc")] {
208 /// use iri_string::format::ToDedicatedString;
209 /// use iri_string::types::IriAbsoluteStr;
210 ///
211 /// let iri = IriAbsoluteStr::new("HTTP://example.COM/foo/./bar/%2e%2e/../baz?query")?;
212 /// assert!(!iri.is_normalized_rfc3986());
213 ///
214 /// let normalized = iri.normalize().to_dedicated_string();
215 /// assert_eq!(normalized, "http://example.com/baz?query");
216 /// assert!(normalized.is_normalized_rfc3986());
217 /// # }
218 /// # Ok::<_, Error>(())
219 /// ```
220 ///
221 /// ```
222 /// # use iri_string::validate::Error;
223 /// # #[cfg(feature = "alloc")] {
224 /// use iri_string::format::ToDedicatedString;
225 /// use iri_string::types::IriAbsoluteStr;
226 ///
227 /// let iri = IriAbsoluteStr::new("scheme:/.///foo")?;
228 /// // Not normalized in the sense of RFC 3986.
229 /// assert!(!iri.is_normalized_rfc3986());
230 /// # }
231 /// # Ok::<_, Error>(())
232 /// ```
233 ///
234 /// ```
235 /// # use iri_string::validate::Error;
236 /// # #[cfg(feature = "alloc")] {
237 /// use iri_string::format::ToDedicatedString;
238 /// use iri_string::types::IriAbsoluteStr;
239 ///
240 /// let iri = IriAbsoluteStr::new("scheme:relative/..//not-a-host")?;
241 /// // RFC 3986 normalization algorithm assumes the path part to be NOT opaque.
242 /// assert!(!iri.is_normalized_rfc3986());
243 ///
244 /// let normalized = iri.normalize().to_dedicated_string();
245 /// assert_eq!(normalized, "scheme:/.//not-a-host");
246 /// # }
247 /// # Ok::<_, Error>(())
248 /// ```
249 #[inline]
250 #[must_use]
251 pub fn is_normalized_rfc3986(&self) -> bool {
252 trusted_parser::is_normalized::<S>(self.as_str(), NormalizednessCheckMode::Rfc3986)
253 }
254
255 /// Returns `true` if the IRI is already normalized in the sense of
256 /// [`normalize_but_preserve_authorityless_relative_path`] method.
257 ///
258 /// This returns the same result as
259 /// `self.normalize_but_preserve_authorityless_relative_path().to_string() == self`,
260 /// but does this more efficiently without heap allocation.
261 ///
262 /// # Examples
263 ///
264 /// ```
265 /// # use iri_string::validate::Error;
266 /// # #[cfg(feature = "alloc")] {
267 /// use iri_string::format::ToDedicatedString;
268 /// use iri_string::types::IriAbsoluteStr;
269 ///
270 /// let iri = IriAbsoluteStr::new("HTTP://example.COM/foo/./bar/%2e%2e/../baz?query")?;
271 /// assert!(!iri.is_normalized_but_authorityless_relative_path_preserved());
272 ///
273 /// let normalized = iri
274 /// .normalize_but_preserve_authorityless_relative_path()
275 /// .to_dedicated_string();
276 /// assert_eq!(normalized, "http://example.com/baz?query");
277 /// assert!(normalized.is_normalized());
278 /// # }
279 /// # Ok::<_, Error>(())
280 /// ```
281 ///
282 /// ```
283 /// # use iri_string::validate::Error;
284 /// # #[cfg(feature = "alloc")] {
285 /// use iri_string::format::ToDedicatedString;
286 /// use iri_string::types::IriAbsoluteStr;
287 ///
288 /// let iri = IriAbsoluteStr::new("scheme:/.///foo")?;
289 /// // Already normalized in the sense of
290 /// // `normalize_but_opaque_authorityless_relative_path()` method.
291 /// assert!(iri.is_normalized_but_authorityless_relative_path_preserved());
292 /// # }
293 /// # Ok::<_, Error>(())
294 /// ```
295 ///
296 /// ```
297 /// # use iri_string::validate::Error;
298 /// # #[cfg(feature = "alloc")] {
299 /// use iri_string::format::ToDedicatedString;
300 /// use iri_string::types::IriAbsoluteStr;
301 ///
302 /// let iri = IriAbsoluteStr::new("scheme:relative/..//not-a-host")?;
303 /// // Relative path is treated as opaque since the autority component is absent.
304 /// assert!(iri.is_normalized_but_authorityless_relative_path_preserved());
305 /// # }
306 /// # Ok::<_, Error>(())
307 /// ```
308 ///
309 /// [`normalize_but_preserve_authorityless_relative_path`]:
310 /// `Self::normalize_but_preserve_authorityless_relative_path`
311 #[inline]
312 #[must_use]
313 pub fn is_normalized_but_authorityless_relative_path_preserved(&self) -> bool {
314 trusted_parser::is_normalized::<S>(
315 self.as_str(),
316 NormalizednessCheckMode::PreserveAuthoritylessRelativePath,
317 )
318 }
319
320 /// Returns the normalized IRI.
321 ///
322 /// # Notes
323 ///
324 /// For some abnormal IRIs, the normalization can produce semantically
325 /// incorrect string that looks syntactically valid. To avoid security
326 /// issues by this trap, the normalization algorithm by this crate
327 /// automatically applies the workaround.
328 ///
329 /// If you worry about this, test by
330 /// [`RiAbsoluteStr::ensure_rfc3986_normalizable`] method or
331 /// [`Normalized::ensure_rfc3986_normalizable`] before using the result
332 /// string.
333 ///
334 /// # Examples
335 ///
336 /// ```
337 /// # use iri_string::validate::Error;
338 /// # #[cfg(feature = "alloc")] {
339 /// use iri_string::format::ToDedicatedString;
340 /// use iri_string::types::IriAbsoluteStr;
341 ///
342 /// let iri = IriAbsoluteStr::new("HTTP://example.COM/foo/./bar/%2e%2e/../baz?query")?;
343 ///
344 /// let normalized = iri.normalize().to_dedicated_string();
345 /// assert_eq!(normalized, "http://example.com/baz?query");
346 /// # }
347 /// # Ok::<_, Error>(())
348 /// ```
349 #[inline]
350 #[must_use]
351 pub fn normalize(&self) -> Normalized<'_, Self> {
352 Normalized::from_input(NormalizationInput::from(self)).and_normalize()
353 }
354
355 /// Returns the normalized IRI, but preserving dot segments in relative path
356 /// if the authority component is absent.
357 ///
358 /// This normalization would be similar to that of [WHATWG URL Standard]
359 /// while this implementation is not guaranteed to stricly follow the spec.
360 ///
361 /// Note that this normalization algorithm is not compatible with RFC 3986
362 /// algorithm for some inputs.
363 ///
364 /// Note that case normalization and percent-encoding normalization will
365 /// still be applied to any path.
366 ///
367 /// # Examples
368 ///
369 /// ```
370 /// # use iri_string::validate::Error;
371 /// # #[cfg(feature = "alloc")] {
372 /// use iri_string::format::ToDedicatedString;
373 /// use iri_string::types::IriAbsoluteStr;
374 ///
375 /// let iri = IriAbsoluteStr::new("HTTP://example.COM/foo/./bar/%2e%2e/../baz?query")?;
376 ///
377 /// let normalized = iri
378 /// .normalize_but_preserve_authorityless_relative_path()
379 /// .to_dedicated_string();
380 /// assert_eq!(normalized, "http://example.com/baz?query");
381 /// # }
382 /// # Ok::<_, Error>(())
383 /// ```
384 ///
385 /// ```
386 /// # use iri_string::validate::Error;
387 /// # #[cfg(feature = "alloc")] {
388 /// use iri_string::format::ToDedicatedString;
389 /// use iri_string::types::IriAbsoluteStr;
390 ///
391 /// let iri = IriAbsoluteStr::new("scheme:relative/../f%6f%6f")?;
392 ///
393 /// let normalized = iri
394 /// .normalize_but_preserve_authorityless_relative_path()
395 /// .to_dedicated_string();
396 /// assert_eq!(normalized, "scheme:relative/../foo");
397 /// // `.normalize()` would normalize this to `scheme:/foo`.
398 /// # assert_eq!(iri.normalize().to_dedicated_string(), "scheme:/foo");
399 /// # }
400 /// # Ok::<_, Error>(())
401 /// ```
402 ///
403 /// [WHATWG URL Standard]: https://url.spec.whatwg.org/
404 #[inline]
405 #[must_use]
406 pub fn normalize_but_preserve_authorityless_relative_path(&self) -> Normalized<'_, Self> {
407 Normalized::from_input(NormalizationInput::from(self))
408 .and_normalize_but_preserve_authorityless_relative_path()
409 }
410
411 /// Returns the proxy to the IRI with password masking feature.
412 ///
413 /// # Examples
414 ///
415 /// ```
416 /// # use iri_string::validate::Error;
417 /// # #[cfg(feature = "alloc")] {
418 /// use iri_string::format::ToDedicatedString;
419 /// use iri_string::types::IriAbsoluteStr;
420 ///
421 /// let iri = IriAbsoluteStr::new("http://user:password@example.com/path?query")?;
422 /// let masked = iri.mask_password();
423 /// assert_eq!(masked.to_dedicated_string(), "http://user:@example.com/path?query");
424 ///
425 /// assert_eq!(
426 /// masked.replace_password("${password}").to_string(),
427 /// "http://user:${password}@example.com/path?query"
428 /// );
429 /// # }
430 /// # Ok::<_, Error>(())
431 /// ```
432 #[inline]
433 #[must_use]
434 pub fn mask_password(&self) -> PasswordMasked<'_, Self> {
435 PasswordMasked::new(self)
436 }
437}
438
439/// Components getters.
440impl<S: Spec> RiAbsoluteStr<S> {
441 /// Returns the scheme.
442 ///
443 /// The following colon is truncated.
444 ///
445 /// # Examples
446 ///
447 /// ```
448 /// # use iri_string::validate::Error;
449 /// use iri_string::types::IriAbsoluteStr;
450 ///
451 /// let iri = IriAbsoluteStr::new("http://example.com/pathpath?queryquery")?;
452 /// assert_eq!(iri.scheme_str(), "http");
453 /// # Ok::<_, Error>(())
454 /// ```
455 #[inline]
456 #[must_use]
457 pub fn scheme_str(&self) -> &str {
458 trusted_parser::extract_scheme_absolute(self.as_str())
459 }
460
461 /// Returns the authority.
462 ///
463 /// The leading `//` is truncated.
464 ///
465 /// # Examples
466 ///
467 /// ```
468 /// # use iri_string::validate::Error;
469 /// use iri_string::types::IriAbsoluteStr;
470 ///
471 /// let iri = IriAbsoluteStr::new("http://example.com/pathpath?queryquery")?;
472 /// assert_eq!(iri.authority_str(), Some("example.com"));
473 /// # Ok::<_, Error>(())
474 /// ```
475 ///
476 /// ```
477 /// # use iri_string::validate::Error;
478 /// use iri_string::types::IriAbsoluteStr;
479 ///
480 /// let iri = IriAbsoluteStr::new("urn:uuid:10db315b-fcd1-4428-aca8-15babc9a2da2")?;
481 /// assert_eq!(iri.authority_str(), None);
482 /// # Ok::<_, Error>(())
483 /// ```
484 #[inline]
485 #[must_use]
486 pub fn authority_str(&self) -> Option<&str> {
487 trusted_parser::extract_authority_absolute(self.as_str())
488 }
489
490 /// Returns the path.
491 ///
492 /// # Examples
493 ///
494 /// ```
495 /// # use iri_string::validate::Error;
496 /// use iri_string::types::IriAbsoluteStr;
497 ///
498 /// let iri = IriAbsoluteStr::new("http://example.com/pathpath?queryquery")?;
499 /// assert_eq!(iri.path_str(), "/pathpath");
500 /// # Ok::<_, Error>(())
501 /// ```
502 ///
503 /// ```
504 /// # use iri_string::validate::Error;
505 /// use iri_string::types::IriAbsoluteStr;
506 ///
507 /// let iri = IriAbsoluteStr::new("urn:uuid:10db315b-fcd1-4428-aca8-15babc9a2da2")?;
508 /// assert_eq!(iri.path_str(), "uuid:10db315b-fcd1-4428-aca8-15babc9a2da2");
509 /// # Ok::<_, Error>(())
510 /// ```
511 #[inline]
512 #[must_use]
513 pub fn path_str(&self) -> &str {
514 trusted_parser::extract_path_absolute(self.as_str())
515 }
516
517 /// Returns the query.
518 ///
519 /// The leading question mark (`?`) is truncated.
520 ///
521 /// # Examples
522 ///
523 /// ```
524 /// # use iri_string::validate::Error;
525 /// use iri_string::types::{IriAbsoluteStr, IriQueryStr};
526 ///
527 /// let iri = IriAbsoluteStr::new("http://example.com/pathpath?queryquery")?;
528 /// let query = IriQueryStr::new("queryquery")?;
529 /// assert_eq!(iri.query(), Some(query));
530 /// # Ok::<_, Error>(())
531 /// ```
532 ///
533 /// ```
534 /// # use iri_string::validate::Error;
535 /// use iri_string::types::IriAbsoluteStr;
536 ///
537 /// let iri = IriAbsoluteStr::new("urn:uuid:10db315b-fcd1-4428-aca8-15babc9a2da2")?;
538 /// assert_eq!(iri.query(), None);
539 /// # Ok::<_, Error>(())
540 /// ```
541 #[inline]
542 #[must_use]
543 pub fn query(&self) -> Option<&RiQueryStr<S>> {
544 trusted_parser::extract_query_absolute_iri(self.as_str()).map(|query| {
545 // SAFETY: `trusted_parser::extract_query_absolute_iri()` must return
546 // the query part of an IRI (including the leading `?` character),
547 // and the returned string consists of allowed characters since it
548 // is a substring of the source IRI.
549 unsafe { RiQueryStr::new_maybe_unchecked(query) }
550 })
551 }
552
553 /// Returns the query in a raw string slice.
554 ///
555 /// The leading question mark (`?`) is truncated.
556 ///
557 /// # Examples
558 ///
559 /// ```
560 /// # use iri_string::validate::Error;
561 /// use iri_string::types::IriAbsoluteStr;
562 ///
563 /// let iri = IriAbsoluteStr::new("http://example.com/pathpath?queryquery")?;
564 /// assert_eq!(iri.query_str(), Some("queryquery"));
565 /// # Ok::<_, Error>(())
566 /// ```
567 ///
568 /// ```
569 /// # use iri_string::validate::Error;
570 /// use iri_string::types::IriAbsoluteStr;
571 ///
572 /// let iri = IriAbsoluteStr::new("urn:uuid:10db315b-fcd1-4428-aca8-15babc9a2da2")?;
573 /// assert_eq!(iri.query_str(), None);
574 /// # Ok::<_, Error>(())
575 /// ```
576 #[inline]
577 #[must_use]
578 pub fn query_str(&self) -> Option<&str> {
579 trusted_parser::extract_query_absolute_iri(self.as_str())
580 }
581
582 /// Returns the authority components.
583 ///
584 /// # Examples
585 ///
586 /// ```
587 /// # use iri_string::validate::Error;
588 /// use iri_string::types::IriAbsoluteStr;
589 ///
590 /// let iri = IriAbsoluteStr::new("http://user:pass@example.com:8080/pathpath?queryquery")?;
591 /// let authority = iri.authority_components()
592 /// .expect("authority is available");
593 /// assert_eq!(authority.userinfo(), Some("user:pass"));
594 /// assert_eq!(authority.host(), "example.com");
595 /// assert_eq!(authority.port(), Some("8080"));
596 /// # Ok::<_, Error>(())
597 /// ```
598 ///
599 /// ```
600 /// # use iri_string::validate::Error;
601 /// use iri_string::types::IriAbsoluteStr;
602 ///
603 /// let iri = IriAbsoluteStr::new("urn:uuid:10db315b-fcd1-4428-aca8-15babc9a2da2")?;
604 /// assert_eq!(iri.authority_str(), None);
605 /// # Ok::<_, Error>(())
606 /// ```
607 #[inline]
608 #[must_use]
609 pub fn authority_components(&self) -> Option<AuthorityComponents<'_>> {
610 AuthorityComponents::from_iri(self.as_ref())
611 }
612}
613
614#[cfg(feature = "alloc")]
615impl<S: Spec> RiAbsoluteString<S> {
616 /// Removes the password completely (including separator colon) from `self` even if it is empty.
617 ///
618 /// # Examples
619 ///
620 /// ```
621 /// # use iri_string::validate::Error;
622 /// # #[cfg(feature = "alloc")] {
623 /// use iri_string::types::IriAbsoluteString;
624 ///
625 /// let mut iri = IriAbsoluteString::try_from("http://user:password@example.com/path?query")?;
626 /// iri.remove_password_inline();
627 /// assert_eq!(iri, "http://user@example.com/path?query");
628 /// # }
629 /// # Ok::<_, Error>(())
630 /// ```
631 ///
632 /// Even if the password is empty, the password and separator will be removed.
633 ///
634 /// ```
635 /// # use iri_string::validate::Error;
636 /// # #[cfg(feature = "alloc")] {
637 /// use iri_string::types::IriAbsoluteString;
638 ///
639 /// let mut iri = IriAbsoluteString::try_from("http://user:@example.com/path?query")?;
640 /// iri.remove_password_inline();
641 /// assert_eq!(iri, "http://user@example.com/path?query");
642 /// # }
643 /// # Ok::<_, Error>(())
644 /// ```
645 pub fn remove_password_inline(&mut self) {
646 let pw_range = match password_range_to_hide(self.as_slice().as_ref()) {
647 Some(v) => v,
648 None => return,
649 };
650 let separator_colon = pw_range.start - 1;
651 // SAFETY: the IRI must still be valid after the password component and
652 // the leading separator colon is removed.
653 unsafe {
654 let buf = self.as_inner_mut();
655 buf.drain(separator_colon..pw_range.end);
656 debug_assert!(
657 RiAbsoluteStr::<S>::new(buf).is_ok(),
658 "[validity] the IRI must be valid after the password component is removed"
659 );
660 }
661 }
662
663 /// Replaces the non-empty password in `self` to the empty password.
664 ///
665 /// This leaves the separator colon if the password part was available.
666 ///
667 /// # Examples
668 ///
669 /// ```
670 /// # use iri_string::validate::Error;
671 /// # #[cfg(feature = "alloc")] {
672 /// use iri_string::types::IriAbsoluteString;
673 ///
674 /// let mut iri = IriAbsoluteString::try_from("http://user:password@example.com/path?query")?;
675 /// iri.remove_nonempty_password_inline();
676 /// assert_eq!(iri, "http://user:@example.com/path?query");
677 /// # }
678 /// # Ok::<_, Error>(())
679 /// ```
680 ///
681 /// If the password is empty, it is left as is.
682 ///
683 /// ```
684 /// # use iri_string::validate::Error;
685 /// # #[cfg(feature = "alloc")] {
686 /// use iri_string::types::IriAbsoluteString;
687 ///
688 /// let mut iri = IriAbsoluteString::try_from("http://user:@example.com/path?query")?;
689 /// iri.remove_nonempty_password_inline();
690 /// assert_eq!(iri, "http://user:@example.com/path?query");
691 /// # }
692 /// # Ok::<_, Error>(())
693 /// ```
694 pub fn remove_nonempty_password_inline(&mut self) {
695 let pw_range = match password_range_to_hide(self.as_slice().as_ref()) {
696 Some(v) if !v.is_empty() => v,
697 _ => return,
698 };
699 debug_assert_eq!(
700 self.as_str().as_bytes().get(pw_range.start - 1).copied(),
701 Some(b':'),
702 "[validity] the password component must be prefixed with a separator colon"
703 );
704 // SAFETY: the IRI must be valid after the password is replaced with empty string.
705 unsafe {
706 let buf = self.as_inner_mut();
707 buf.drain(pw_range);
708 debug_assert!(
709 RiAbsoluteStr::<S>::new(buf).is_ok(),
710 "[validity] the IRI must be valid after the password component is removed"
711 );
712 }
713 }
714}
715
716impl_trivial_conv_between_iri! {
717 from_slice: RiAbsoluteStr,
718 from_owned: RiAbsoluteString,
719 to_slice: RiStr,
720 to_owned: RiString,
721}
722
723impl_trivial_conv_between_iri! {
724 from_slice: RiAbsoluteStr,
725 from_owned: RiAbsoluteString,
726 to_slice: RiReferenceStr,
727 to_owned: RiReferenceString,
728}