1#![no_std]
146#![doc(html_root_url = "https://docs.rs/url/2.5.7")]
147#![cfg_attr(
148 feature = "debugger_visualizer",
149 debugger_visualizer(natvis_file = "../../debug_metadata/url.natvis")
150)]
151
152pub use form_urlencoded;
153
154#[cfg(feature = "std")]
156extern crate std;
157
158#[macro_use]
159extern crate alloc;
160
161#[cfg(feature = "serde")]
162extern crate serde;
163
164use crate::host::HostInternal;
165
166use crate::net::IpAddr;
167#[cfg(feature = "std")]
168#[cfg(any(
169 unix,
170 windows,
171 target_os = "redox",
172 target_os = "wasi",
173 target_os = "hermit"
174))]
175use crate::net::{SocketAddr, ToSocketAddrs};
176use crate::parser::{to_u32, Context, Parser, SchemeType, USERINFO};
177use alloc::borrow::Cow;
178use alloc::borrow::ToOwned;
179use alloc::str;
180use alloc::string::{String, ToString};
181use core::borrow::Borrow;
182use core::convert::TryFrom;
183use core::fmt::Write;
184use core::ops::{Range, RangeFrom, RangeTo};
185use core::{cmp, fmt, hash, mem};
186use percent_encoding::utf8_percent_encode;
187#[cfg(feature = "std")]
188#[cfg(any(
189 unix,
190 windows,
191 target_os = "redox",
192 target_os = "wasi",
193 target_os = "hermit"
194))]
195use std::io;
196#[cfg(feature = "std")]
197use std::path::{Path, PathBuf};
198
199#[cfg(feature = "std")]
201pub(crate) mod net {
202 pub use std::net::*;
203}
204#[cfg(not(feature = "std"))]
206pub(crate) mod net {
207 pub use core::net::*;
208}
209
210pub use crate::host::Host;
211pub use crate::origin::{OpaqueOrigin, Origin};
212pub use crate::parser::{ParseError, SyntaxViolation};
213pub use crate::path_segments::PathSegmentsMut;
214pub use crate::slicing::Position;
215pub use form_urlencoded::EncodingOverride;
216
217mod host;
218mod origin;
219mod parser;
220mod path_segments;
221mod slicing;
222
223#[doc(hidden)]
224pub mod quirks;
225
226)]
228pub struct Url {
229 serialization: String,
239
240 scheme_end: u32, username_end: u32, host_start: u32,
244 host_end: u32,
245 host: HostInternal,
246 port: Option<u16>,
247 path_start: u32, query_start: Option<u32>, fragment_start: Option<u32>, }
251
252)]
254#[must_use]
255pub struct ParseOptions<'a> {
256 base_url: Option<&'a Url>,
257 encoding_override: EncodingOverride<'a>,
258 violation_fn: Option<&'a dyn Fn(SyntaxViolation)>,
259}
260
261impl<'a> ParseOptions<'a> {
262 pub fn base_url(mut self, new: Option<&'a Url>) -> Self {
267 self.base_url = new;
268 self
269 }
270
271 pub fn encoding_override(mut self, new: EncodingOverride<'a>) -> Self {
274 self.encoding_override = new;
275 self
276 }
277
278 pub fn syntax_violation_callback(mut self, new: Option<&'a dyn Fn(SyntaxViolation)>) -> Self {
301 self.violation_fn = new;
302 self
303 }
304
305 pub fn parse(self, input: &str) -> Result<Url, crate::ParseError> {
307 Parser {
308 serialization: String::with_capacity(input.len()),
309 base_url: self.base_url,
310 query_encoding_override: self.encoding_override,
311 violation_fn: self.violation_fn,
312 context: Context::UrlParser,
313 }
314 .parse_url(input)
315 }
316}
317
318impl Url {
319 #[inline]
341 pub fn parse(input: &str) -> Result<Self, crate::ParseError> {
342 Self::options().parse(input)
343 }
344
345 #[inline]
371 pub fn parse_with_params<I, K, V>(input: &str, iter: I) -> Result<Self, crate::ParseError>
372 where
373 I: IntoIterator,
374 I::Item: Borrow<(K, V)>,
375 K: AsRef<str>,
376 V: AsRef<str>,
377 {
378 let mut url = Self::options().parse(input);
379
380 if let Ok(ref mut url) = url {
381 url.query_pairs_mut().extend_pairs(iter);
382 }
383
384 url
385 }
386
387 fn strip_trailing_spaces_from_opaque_path(&mut self) {
389 if !self.cannot_be_a_base() {
390 return;
391 }
392
393 if self.fragment_start.is_some() {
394 return;
395 }
396
397 if self.query_start.is_some() {
398 return;
399 }
400
401 let trailing_space_count = self
402 .serialization
403 .chars()
404 .rev()
405 .take_while(|c| *c == ' ')
406 .count();
407
408 let start = self.serialization.len() - trailing_space_count;
409
410 self.serialization.truncate(start);
411 }
412
413 #[inline]
471 pub fn join(&self, input: &str) -> Result<Self, crate::ParseError> {
472 Self::options().base_url(Some(self)).parse(input)
473 }
474
475 pub fn make_relative(&self, url: &Self) -> Option<String> {
517 if self.cannot_be_a_base() {
518 return None;
519 }
520
521 if self.scheme() != url.scheme() || self.host() != url.host() || self.port() != url.port() {
523 return None;
524 }
525
526 let mut relative = String::new();
530
531 fn extract_path_filename(s: &str) -> (&str, &str) {
533 let last_slash_idx = s.rfind('/').unwrap_or(0);
534 let (path, filename) = s.split_at(last_slash_idx);
535 if filename.is_empty() {
536 (path, "")
537 } else {
538 (path, &filename[1..])
539 }
540 }
541
542 let (base_path, base_filename) = extract_path_filename(self.path());
543 let (url_path, url_filename) = extract_path_filename(url.path());
544
545 let mut base_path = base_path.split('/').peekable();
546 let mut url_path = url_path.split('/').peekable();
547
548 while base_path.peek().is_some() && base_path.peek() == url_path.peek() {
550 base_path.next();
551 url_path.next();
552 }
553
554 for base_path_segment in base_path {
556 if base_path_segment.is_empty() {
558 break;
559 }
560
561 if !relative.is_empty() {
562 relative.push('/');
563 }
564
565 relative.push_str("..");
566 }
567
568 for url_path_segment in url_path {
570 if !relative.is_empty() {
571 relative.push('/');
572 }
573
574 relative.push_str(url_path_segment);
575 }
576
577 if !relative.is_empty() || base_filename != url_filename {
579 if url_filename.is_empty() {
584 relative.push('/');
585 } else {
586 if !relative.is_empty() {
587 relative.push('/');
588 }
589 relative.push_str(url_filename);
590 }
591 }
592
593 if let Some(query) = url.query() {
595 relative.push('?');
596 relative.push_str(query);
597 }
598
599 if let Some(fragment) = url.fragment() {
600 relative.push('#');
601 relative.push_str(fragment);
602 }
603
604 Some(relative)
605 }
606
607 pub fn options<'a>() -> ParseOptions<'a> {
627 ParseOptions {
628 base_url: None,
629 encoding_override: None,
630 violation_fn: None,
631 }
632 }
633
634 #[inline]
653 pub fn as_str(&self) -> &str {
654 &self.serialization
655 }
656
657 #[inline]
676 #[deprecated(since = "2.3.0", note = "use Into<String>")]
677 pub fn into_string(self) -> String {
678 self.into()
679 }
680
681 #[doc(hidden)]
687 pub fn check_invariants(&self) -> Result<(), String> {
688 macro_rules! assert {
689 ($x: expr) => {
690 if !$x {
691 return Err(format!(
692 "!( {} ) for URL {:?}",
693 stringify!($x),
694 self.serialization
695 ));
696 }
697 };
698 }
699
700 macro_rules! assert_eq {
701 ($a: expr, $b: expr) => {
702 {
703 let a = $a;
704 let b = $b;
705 if a != b {
706 return Err(format!("{:?} != {:?} ({} != {}) for URL {:?}",
707 a, b, stringify!($a), stringify!($b),
708 self.serialization))
709 }
710 }
711 }
712 }
713
714 if !(self.scheme_end >= 1) {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("!( {0} ) for URL {1:?}",
"self.scheme_end >= 1", self.serialization))
}));
};assert!(self.scheme_end >= 1);
715 if !self.byte_at(0).is_ascii_alphabetic() {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("!( {0} ) for URL {1:?}",
"self.byte_at(0).is_ascii_alphabetic()",
self.serialization))
}));
};assert!(self.byte_at(0).is_ascii_alphabetic());
716 if !self.slice(1..self.scheme_end).chars().all(|c|
#[allow(non_exhaustive_omitted_patterns)] match c {
'a'..='z' | 'A'..='Z' | '0'..='9' | '+' | '-' | '.' => true,
_ => false,
}) {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("!( {0} ) for URL {1:?}",
"self.slice(1..self.scheme_end).chars().all(|c|\nmatches!(c, \'a\'..=\'z\' | \'A\'..=\'Z\' | \'0\'..=\'9\' | \'+\' | \'-\' | \'.\'))",
self.serialization))
}));
};assert!(self
717 .slice(1..self.scheme_end)
718 .chars()
719 .all(|c| matches!(c, 'a'..='z' | 'A'..='Z' | '0'..='9' | '+' | '-' | '.')));
720 {
let a = self.byte_at(self.scheme_end);
let b = b':';
if a != b {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?} != {1:?} ({2} != {3}) for URL {4:?}",
a, b, "self.byte_at(self.scheme_end)", "b\':\'",
self.serialization))
}))
}
};assert_eq!(self.byte_at(self.scheme_end), b':');
721
722 if self.slice(self.scheme_end + 1..).starts_with("//") {
723 if self.username_end != self.serialization.len() as u32 {
725 match self.byte_at(self.username_end) {
726 b':' => {
727 if !(self.host_start >= self.username_end + 2) {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("!( {0} ) for URL {1:?}",
"self.host_start >= self.username_end + 2",
self.serialization))
}));
};assert!(self.host_start >= self.username_end + 2);
728 {
let a = self.byte_at(self.host_start - 1);
let b = b'@';
if a != b {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?} != {1:?} ({2} != {3}) for URL {4:?}",
a, b, "self.byte_at(self.host_start - 1)", "b\'@\'",
self.serialization))
}))
}
};assert_eq!(self.byte_at(self.host_start - 1), b'@');
729 }
730 b'@' => if !(self.host_start == self.username_end + 1) {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("!( {0} ) for URL {1:?}",
"self.host_start == self.username_end + 1",
self.serialization))
}));
}assert!(self.host_start == self.username_end + 1),
731 _ => {
let a = self.username_end;
let b = self.scheme_end + 3;
if a != b {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?} != {1:?} ({2} != {3}) for URL {4:?}",
a, b, "self.username_end", "self.scheme_end + 3",
self.serialization))
}))
}
}assert_eq!(self.username_end, self.scheme_end + 3),
732 }
733 }
734 if !(self.host_start >= self.username_end) {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("!( {0} ) for URL {1:?}",
"self.host_start >= self.username_end", self.serialization))
}));
};assert!(self.host_start >= self.username_end);
735 if !(self.host_end >= self.host_start) {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("!( {0} ) for URL {1:?}",
"self.host_end >= self.host_start", self.serialization))
}));
};assert!(self.host_end >= self.host_start);
736 let host_str = self.slice(self.host_start..self.host_end);
737 match self.host {
738 HostInternal::None => {
let a = host_str;
let b = "";
if a != b {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?} != {1:?} ({2} != {3}) for URL {4:?}",
a, b, "host_str", "\"\"", self.serialization))
}))
}
}assert_eq!(host_str, ""),
739 HostInternal::Ipv4(address) => {
let a = host_str;
let b = address.to_string();
if a != b {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?} != {1:?} ({2} != {3}) for URL {4:?}",
a, b, "host_str", "address.to_string()",
self.serialization))
}))
}
}assert_eq!(host_str, address.to_string()),
740 HostInternal::Ipv6(address) => {
741 let h: Host<String> = Host::Ipv6(address);
742 {
let a = host_str;
let b = h.to_string();
if a != b {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?} != {1:?} ({2} != {3}) for URL {4:?}",
a, b, "host_str", "h.to_string()", self.serialization))
}))
}
}assert_eq!(host_str, h.to_string())
743 }
744 HostInternal::Domain => {
745 if SchemeType::from(self.scheme()).is_special() {
746 if !!host_str.is_empty() {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("!( {0} ) for URL {1:?}",
"!host_str.is_empty()", self.serialization))
}));
}assert!(!host_str.is_empty())
747 }
748 }
749 }
750 if self.path_start == self.host_end {
751 {
let a = self.port;
let b = None;
if a != b {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?} != {1:?} ({2} != {3}) for URL {4:?}",
a, b, "self.port", "None", self.serialization))
}))
}
};assert_eq!(self.port, None);
752 } else {
753 {
let a = self.byte_at(self.host_end);
let b = b':';
if a != b {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?} != {1:?} ({2} != {3}) for URL {4:?}",
a, b, "self.byte_at(self.host_end)", "b\':\'",
self.serialization))
}))
}
};assert_eq!(self.byte_at(self.host_end), b':');
754 let port_str = self.slice(self.host_end + 1..self.path_start);
755 {
let a = self.port;
let b = Some(port_str.parse::<u16>().expect("Couldn't parse port?"));
if a != b {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?} != {1:?} ({2} != {3}) for URL {4:?}",
a, b, "self.port",
"Some(port_str.parse::<u16>().expect(\"Couldn\'t parse port?\"))",
self.serialization))
}))
}
};assert_eq!(
756 self.port,
757 Some(port_str.parse::<u16>().expect("Couldn't parse port?"))
758 );
759 }
760 if !(self.path_start as usize == self.serialization.len() ||
#[allow(non_exhaustive_omitted_patterns)] match self.byte_at(self.path_start)
{
b'/' | b'#' | b'?' => true,
_ => false,
}) {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("!( {0} ) for URL {1:?}",
"self.path_start as usize == self.serialization.len() ||\nmatches!(self.byte_at(self.path_start), b\'/\' | b\'#\' | b\'?\')",
self.serialization))
}));
};assert!(
761 self.path_start as usize == self.serialization.len()
762 || matches!(self.byte_at(self.path_start), b'/' | b'#' | b'?')
763 );
764 } else {
765 {
let a = self.username_end;
let b = self.scheme_end + 1;
if a != b {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?} != {1:?} ({2} != {3}) for URL {4:?}",
a, b, "self.username_end", "self.scheme_end + 1",
self.serialization))
}))
}
};assert_eq!(self.username_end, self.scheme_end + 1);
767 {
let a = self.host_start;
let b = self.scheme_end + 1;
if a != b {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?} != {1:?} ({2} != {3}) for URL {4:?}",
a, b, "self.host_start", "self.scheme_end + 1",
self.serialization))
}))
}
};assert_eq!(self.host_start, self.scheme_end + 1);
768 {
let a = self.host_end;
let b = self.scheme_end + 1;
if a != b {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?} != {1:?} ({2} != {3}) for URL {4:?}",
a, b, "self.host_end", "self.scheme_end + 1",
self.serialization))
}))
}
};assert_eq!(self.host_end, self.scheme_end + 1);
769 {
let a = self.host;
let b = HostInternal::None;
if a != b {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?} != {1:?} ({2} != {3}) for URL {4:?}",
a, b, "self.host", "HostInternal::None",
self.serialization))
}))
}
};assert_eq!(self.host, HostInternal::None);
770 {
let a = self.port;
let b = None;
if a != b {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?} != {1:?} ({2} != {3}) for URL {4:?}",
a, b, "self.port", "None", self.serialization))
}))
}
};assert_eq!(self.port, None);
771 if self.path().starts_with("//") {
772 {
let a = self.byte_at(self.scheme_end + 1);
let b = b'/';
if a != b {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?} != {1:?} ({2} != {3}) for URL {4:?}",
a, b, "self.byte_at(self.scheme_end + 1)", "b\'/\'",
self.serialization))
}))
}
};assert_eq!(self.byte_at(self.scheme_end + 1), b'/');
774 {
let a = self.byte_at(self.scheme_end + 2);
let b = b'.';
if a != b {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?} != {1:?} ({2} != {3}) for URL {4:?}",
a, b, "self.byte_at(self.scheme_end + 2)", "b\'.\'",
self.serialization))
}))
}
};assert_eq!(self.byte_at(self.scheme_end + 2), b'.');
775 {
let a = self.path_start;
let b = self.scheme_end + 3;
if a != b {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?} != {1:?} ({2} != {3}) for URL {4:?}",
a, b, "self.path_start", "self.scheme_end + 3",
self.serialization))
}))
}
};assert_eq!(self.path_start, self.scheme_end + 3);
776 } else {
777 {
let a = self.path_start;
let b = self.scheme_end + 1;
if a != b {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?} != {1:?} ({2} != {3}) for URL {4:?}",
a, b, "self.path_start", "self.scheme_end + 1",
self.serialization))
}))
}
};assert_eq!(self.path_start, self.scheme_end + 1);
778 }
779 }
780 if let Some(start) = self.query_start {
781 if !(start >= self.path_start) {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("!( {0} ) for URL {1:?}",
"start >= self.path_start", self.serialization))
}));
};assert!(start >= self.path_start);
782 {
let a = self.byte_at(start);
let b = b'?';
if a != b {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?} != {1:?} ({2} != {3}) for URL {4:?}",
a, b, "self.byte_at(start)", "b\'?\'", self.serialization))
}))
}
};assert_eq!(self.byte_at(start), b'?');
783 }
784 if let Some(start) = self.fragment_start {
785 if !(start >= self.path_start) {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("!( {0} ) for URL {1:?}",
"start >= self.path_start", self.serialization))
}));
};assert!(start >= self.path_start);
786 {
let a = self.byte_at(start);
let b = b'#';
if a != b {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?} != {1:?} ({2} != {3}) for URL {4:?}",
a, b, "self.byte_at(start)", "b\'#\'", self.serialization))
}))
}
};assert_eq!(self.byte_at(start), b'#');
787 }
788 if let (Some(query_start), Some(fragment_start)) = (self.query_start, self.fragment_start) {
789 if !(fragment_start > query_start) {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("!( {0} ) for URL {1:?}",
"fragment_start > query_start", self.serialization))
}));
};assert!(fragment_start > query_start);
790 }
791
792 let other = Self::parse(self.as_str()).expect("Failed to parse myself?");
793 {
let a = &self.serialization;
let b = &other.serialization;
if a != b {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?} != {1:?} ({2} != {3}) for URL {4:?}",
a, b, "&self.serialization", "&other.serialization",
self.serialization))
}))
}
};assert_eq!(&self.serialization, &other.serialization);
794 {
let a = self.scheme_end;
let b = other.scheme_end;
if a != b {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?} != {1:?} ({2} != {3}) for URL {4:?}",
a, b, "self.scheme_end", "other.scheme_end",
self.serialization))
}))
}
};assert_eq!(self.scheme_end, other.scheme_end);
795 {
let a = self.username_end;
let b = other.username_end;
if a != b {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?} != {1:?} ({2} != {3}) for URL {4:?}",
a, b, "self.username_end", "other.username_end",
self.serialization))
}))
}
};assert_eq!(self.username_end, other.username_end);
796 {
let a = self.host_start;
let b = other.host_start;
if a != b {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?} != {1:?} ({2} != {3}) for URL {4:?}",
a, b, "self.host_start", "other.host_start",
self.serialization))
}))
}
};assert_eq!(self.host_start, other.host_start);
797 {
let a = self.host_end;
let b = other.host_end;
if a != b {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?} != {1:?} ({2} != {3}) for URL {4:?}",
a, b, "self.host_end", "other.host_end",
self.serialization))
}))
}
};assert_eq!(self.host_end, other.host_end);
798 if !(self.host == other.host ||
(self.host_str(), other.host_str()) == (None, Some(""))) {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("!( {0} ) for URL {1:?}",
"self.host == other.host || (self.host_str(), other.host_str()) ==\n(None, Some(\"\"))",
self.serialization))
}));
};assert!(
799 self.host == other.host ||
800 (self.host_str(), other.host_str()) == (None, Some(""))
803 );
804 {
let a = self.port;
let b = other.port;
if a != b {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?} != {1:?} ({2} != {3}) for URL {4:?}",
a, b, "self.port", "other.port", self.serialization))
}))
}
};assert_eq!(self.port, other.port);
805 {
let a = self.path_start;
let b = other.path_start;
if a != b {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?} != {1:?} ({2} != {3}) for URL {4:?}",
a, b, "self.path_start", "other.path_start",
self.serialization))
}))
}
};assert_eq!(self.path_start, other.path_start);
806 {
let a = self.query_start;
let b = other.query_start;
if a != b {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?} != {1:?} ({2} != {3}) for URL {4:?}",
a, b, "self.query_start", "other.query_start",
self.serialization))
}))
}
};assert_eq!(self.query_start, other.query_start);
807 {
let a = self.fragment_start;
let b = other.fragment_start;
if a != b {
return Err(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0:?} != {1:?} ({2} != {3}) for URL {4:?}",
a, b, "self.fragment_start", "other.fragment_start",
self.serialization))
}))
}
};assert_eq!(self.fragment_start, other.fragment_start);
808 Ok(())
809 }
810
811 #[inline]
883 pub fn origin(&self) -> Origin {
884 origin::url_origin(self)
885 }
886
887 #[inline]
903 pub fn scheme(&self) -> &str {
904 self.slice(..self.scheme_end)
905 }
906
907 pub fn is_special(&self) -> bool {
924 let scheme_type = SchemeType::from(self.scheme());
925 scheme_type.is_special()
926 }
927
928 #[inline]
956 pub fn has_authority(&self) -> bool {
957 if true {
if !(self.byte_at(self.scheme_end) == b':') {
::core::panicking::panic("assertion failed: self.byte_at(self.scheme_end) == b\':\'")
};
};debug_assert!(self.byte_at(self.scheme_end) == b':');
958 self.slice(self.scheme_end..).starts_with("://")
959 }
960
961 pub fn authority(&self) -> &str {
994 let scheme_separator_len = "://".len() as u32;
995 if self.has_authority() && self.path_start > self.scheme_end + scheme_separator_len {
996 self.slice(self.scheme_end + scheme_separator_len..self.path_start)
997 } else {
998 ""
999 }
1000 }
1001
1002 #[inline]
1028 pub fn cannot_be_a_base(&self) -> bool {
1029 !self.slice(self.scheme_end + 1..).starts_with('/')
1030 }
1031
1032 pub fn username(&self) -> &str {
1055 let scheme_separator_len = "://".len() as u32;
1056 if self.has_authority() && self.username_end > self.scheme_end + scheme_separator_len {
1057 self.slice(self.scheme_end + scheme_separator_len..self.username_end)
1058 } else {
1059 ""
1060 }
1061 }
1062
1063 pub fn password(&self) -> Option<&str> {
1088 if self.has_authority()
1091 && self.username_end != self.serialization.len() as u32
1092 && self.byte_at(self.username_end) == b':'
1093 {
1094 if true {
if !(self.byte_at(self.host_start - 1) == b'@') {
::core::panicking::panic("assertion failed: self.byte_at(self.host_start - 1) == b\'@\'")
};
};debug_assert!(self.byte_at(self.host_start - 1) == b'@');
1095 Some(self.slice(self.username_end + 1..self.host_start - 1))
1096 } else {
1097 None
1098 }
1099 }
1100
1101 pub fn has_host(&self) -> bool {
1123 !#[allow(non_exhaustive_omitted_patterns)] match self.host {
HostInternal::None => true,
_ => false,
}matches!(self.host, HostInternal::None)
1124 }
1125
1126 pub fn host_str(&self) -> Option<&str> {
1163 if self.has_host() {
1164 Some(self.slice(self.host_start..self.host_end))
1165 } else {
1166 None
1167 }
1168 }
1169
1170 pub fn host(&self) -> Option<Host<&str>> {
1202 match self.host {
1203 HostInternal::None => None,
1204 HostInternal::Domain => Some(Host::Domain(self.slice(self.host_start..self.host_end))),
1205 HostInternal::Ipv4(address) => Some(Host::Ipv4(address)),
1206 HostInternal::Ipv6(address) => Some(Host::Ipv6(address)),
1207 }
1208 }
1209
1210 pub fn domain(&self) -> Option<&str> {
1238 match self.host {
1239 HostInternal::Domain => Some(self.slice(self.host_start..self.host_end)),
1240 _ => None,
1241 }
1242 }
1243
1244 #[inline]
1269 pub fn port(&self) -> Option<u16> {
1270 self.port
1271 }
1272
1273 #[inline]
1301 pub fn port_or_known_default(&self) -> Option<u16> {
1302 self.port.or_else(|| parser::default_port(self.scheme()))
1303 }
1304
1305 #[cfg(feature = "std")]
1333 #[cfg(any(
1334 unix,
1335 windows,
1336 target_os = "redox",
1337 target_os = "wasi",
1338 target_os = "hermit"
1339 ))]
1340 pub fn socket_addrs(
1341 &self,
1342 default_port_number: impl Fn() -> Option<u16>,
1343 ) -> io::Result<alloc::vec::Vec<SocketAddr>> {
1344 fn io_result<T>(opt: Option<T>, message: &str) -> io::Result<T> {
1353 opt.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, message))
1354 }
1355
1356 let host = io_result(self.host(), "No host name in the URL")?;
1357 let port = io_result(
1358 self.port_or_known_default().or_else(default_port_number),
1359 "No port number in the URL",
1360 )?;
1361 Ok(match host {
1362 Host::Domain(domain) => (domain, port).to_socket_addrs()?.collect(),
1363 Host::Ipv4(ip) => <[_]>::into_vec(::alloc::boxed::box_new([(ip, port).into()]))vec![(ip, port).into()],
1364 Host::Ipv6(ip) => <[_]>::into_vec(::alloc::boxed::box_new([(ip, port).into()]))vec![(ip, port).into()],
1365 })
1366 }
1367
1368 pub fn path(&self) -> &str {
1392 match (self.query_start, self.fragment_start) {
1393 (None, None) => self.slice(self.path_start..),
1394 (Some(next_component_start), _) | (None, Some(next_component_start)) => {
1395 self.slice(self.path_start..next_component_start)
1396 }
1397 }
1398 }
1399
1400 pub fn path_segments(&self) -> Option<str::Split<'_, char>> {
1443 let path = self.path();
1444 path.strip_prefix('/').map(|remainder| remainder.split('/'))
1445 }
1446
1447 pub fn query(&self) -> Option<&str> {
1472 match (self.query_start, self.fragment_start) {
1473 (None, _) => None,
1474 (Some(query_start), None) => {
1475 if true {
if !(self.byte_at(query_start) == b'?') {
::core::panicking::panic("assertion failed: self.byte_at(query_start) == b\'?\'")
};
};debug_assert!(self.byte_at(query_start) == b'?');
1476 Some(self.slice(query_start + 1..))
1477 }
1478 (Some(query_start), Some(fragment_start)) => {
1479 if true {
if !(self.byte_at(query_start) == b'?') {
::core::panicking::panic("assertion failed: self.byte_at(query_start) == b\'?\'")
};
};debug_assert!(self.byte_at(query_start) == b'?');
1480 Some(self.slice(query_start + 1..fragment_start))
1481 }
1482 }
1483 }
1484
1485 #[inline]
1509 pub fn query_pairs(&self) -> form_urlencoded::Parse<'_> {
1510 form_urlencoded::parse(self.query().unwrap_or("").as_bytes())
1511 }
1512
1513 pub fn fragment(&self) -> Option<&str> {
1546 self.fragment_start.map(|start| {
1547 if true {
if !(self.byte_at(start) == b'#') {
::core::panicking::panic("assertion failed: self.byte_at(start) == b\'#\'")
};
};debug_assert!(self.byte_at(start) == b'#');
1548 self.slice(start + 1..)
1549 })
1550 }
1551
1552 fn mutate<F: FnOnce(&mut Parser<'_>) -> R, R>(&mut self, f: F) -> R {
1553 let mut parser = Parser::for_setter(mem::take(&mut self.serialization));
1554 let result = f(&mut parser);
1555 self.serialization = parser.serialization;
1556 result
1557 }
1558
1559 pub fn set_fragment(&mut self, fragment: Option<&str>) {
1583 if let Some(start) = self.fragment_start {
1585 if true {
if !(self.byte_at(start) == b'#') {
::core::panicking::panic("assertion failed: self.byte_at(start) == b\'#\'")
};
};debug_assert!(self.byte_at(start) == b'#');
1586 self.serialization.truncate(start as usize);
1587 }
1588 if let Some(input) = fragment {
1590 self.fragment_start = Some(to_u32(self.serialization.len()).unwrap());
1591 self.serialization.push('#');
1592 self.mutate(|parser| parser.parse_fragment(parser::Input::new_no_trim(input)))
1593 } else {
1594 self.fragment_start = None;
1595 self.strip_trailing_spaces_from_opaque_path();
1596 }
1597 }
1598
1599 fn take_fragment(&mut self) -> Option<String> {
1600 self.fragment_start.take().map(|start| {
1601 if true {
if !(self.byte_at(start) == b'#') {
::core::panicking::panic("assertion failed: self.byte_at(start) == b\'#\'")
};
};debug_assert!(self.byte_at(start) == b'#');
1602 let fragment = self.slice(start + 1..).to_owned();
1603 self.serialization.truncate(start as usize);
1604 fragment
1605 })
1606 }
1607
1608 fn restore_already_parsed_fragment(&mut self, fragment: Option<String>) {
1609 if let Some(ref fragment) = fragment {
1610 if !self.fragment_start.is_none() {
::core::panicking::panic("assertion failed: self.fragment_start.is_none()")
};assert!(self.fragment_start.is_none());
1611 self.fragment_start = Some(to_u32(self.serialization.len()).unwrap());
1612 self.serialization.push('#');
1613 self.serialization.push_str(fragment);
1614 }
1615 }
1616
1617 pub fn set_query(&mut self, query: Option<&str>) {
1638 let fragment = self.take_fragment();
1639
1640 if let Some(start) = self.query_start.take() {
1642 if true {
if !(self.byte_at(start) == b'?') {
::core::panicking::panic("assertion failed: self.byte_at(start) == b\'?\'")
};
};debug_assert!(self.byte_at(start) == b'?');
1643 self.serialization.truncate(start as usize);
1644 }
1645 if let Some(input) = query {
1647 self.query_start = Some(to_u32(self.serialization.len()).unwrap());
1648 self.serialization.push('?');
1649 let scheme_type = SchemeType::from(self.scheme());
1650 let scheme_end = self.scheme_end;
1651 self.mutate(|parser| {
1652 let vfn = parser.violation_fn;
1653 parser.parse_query(
1654 scheme_type,
1655 scheme_end,
1656 parser::Input::new_trim_tab_and_newlines(input, vfn),
1657 )
1658 });
1659 } else {
1660 self.query_start = None;
1661 if fragment.is_none() {
1662 self.strip_trailing_spaces_from_opaque_path();
1663 }
1664 }
1665
1666 self.restore_already_parsed_fragment(fragment);
1667 }
1668
1669 pub fn query_pairs_mut(&mut self) -> form_urlencoded::Serializer<'_, UrlQuery<'_>> {
1702 let fragment = self.take_fragment();
1703
1704 let query_start;
1705 if let Some(start) = self.query_start {
1706 if true {
if !(self.byte_at(start) == b'?') {
::core::panicking::panic("assertion failed: self.byte_at(start) == b\'?\'")
};
};debug_assert!(self.byte_at(start) == b'?');
1707 query_start = start as usize;
1708 } else {
1709 query_start = self.serialization.len();
1710 self.query_start = Some(to_u32(query_start).unwrap());
1711 self.serialization.push('?');
1712 }
1713
1714 let query = UrlQuery {
1715 url: Some(self),
1716 fragment,
1717 };
1718 form_urlencoded::Serializer::for_suffix(query, query_start + "?".len())
1719 }
1720
1721 fn take_after_path(&mut self) -> String {
1722 match (self.query_start, self.fragment_start) {
1723 (Some(i), _) | (None, Some(i)) => {
1724 let after_path = self.slice(i..).to_owned();
1725 self.serialization.truncate(i as usize);
1726 after_path
1727 }
1728 (None, None) => String::new(),
1729 }
1730 }
1731
1732 pub fn set_path(&mut self, mut path: &str) {
1768 let after_path = self.take_after_path();
1769 let old_after_path_pos = to_u32(self.serialization.len()).unwrap();
1770 let cannot_be_a_base = self.cannot_be_a_base();
1771 let scheme_type = SchemeType::from(self.scheme());
1772 self.serialization.truncate(self.path_start as usize);
1773 self.mutate(|parser| {
1774 if cannot_be_a_base {
1775 if path.starts_with('/') {
1776 parser.serialization.push_str("%2F");
1777 path = &path[1..];
1778 }
1779 parser.parse_cannot_be_a_base_path(parser::Input::new_no_trim(path));
1780 } else {
1781 let mut has_host = true; parser.parse_path_start(
1783 scheme_type,
1784 &mut has_host,
1785 parser::Input::new_no_trim(path),
1786 );
1787 }
1788 });
1789 self.restore_after_path(old_after_path_pos, &after_path);
1790 }
1791
1792 #[allow(clippy::result_unit_err)]
1796 pub fn path_segments_mut(&mut self) -> Result<PathSegmentsMut<'_>, ()> {
1797 if self.cannot_be_a_base() {
1798 Err(())
1799 } else {
1800 Ok(path_segments::new(self))
1801 }
1802 }
1803
1804 fn restore_after_path(&mut self, old_after_path_position: u32, after_path: &str) {
1805 let new_after_path_position = to_u32(self.serialization.len()).unwrap();
1806 let adjust = |index: &mut u32| {
1807 *index -= old_after_path_position;
1808 *index += new_after_path_position;
1809 };
1810 if let Some(ref mut index) = self.query_start {
1811 adjust(index)
1812 }
1813 if let Some(ref mut index) = self.fragment_start {
1814 adjust(index)
1815 }
1816 self.serialization.push_str(after_path)
1817 }
1818
1819 #[allow(clippy::result_unit_err)]
1888 pub fn set_port(&mut self, mut port: Option<u16>) -> Result<(), ()> {
1889 if !self.has_host() || self.host() == Some(Host::Domain("")) || self.scheme() == "file" {
1891 return Err(());
1892 }
1893 if port.is_some() && port == parser::default_port(self.scheme()) {
1894 port = None
1895 }
1896 self.set_port_internal(port);
1897 Ok(())
1898 }
1899
1900 fn set_port_internal(&mut self, port: Option<u16>) {
1901 match (self.port, port) {
1902 (None, None) => {}
1903 (Some(_), None) => {
1904 self.serialization
1905 .drain(self.host_end as usize..self.path_start as usize);
1906 let offset = self.path_start - self.host_end;
1907 self.path_start = self.host_end;
1908 if let Some(ref mut index) = self.query_start {
1909 *index -= offset
1910 }
1911 if let Some(ref mut index) = self.fragment_start {
1912 *index -= offset
1913 }
1914 }
1915 (Some(old), Some(new)) if old == new => {}
1916 (_, Some(new)) => {
1917 let path_and_after = self.slice(self.path_start..).to_owned();
1918 self.serialization.truncate(self.host_end as usize);
1919 (&mut self.serialization).write_fmt(format_args!(":{0}", new))write!(&mut self.serialization, ":{new}").unwrap();
1920 let old_path_start = self.path_start;
1921 let new_path_start = to_u32(self.serialization.len()).unwrap();
1922 self.path_start = new_path_start;
1923 let adjust = |index: &mut u32| {
1924 *index -= old_path_start;
1925 *index += new_path_start;
1926 };
1927 if let Some(ref mut index) = self.query_start {
1928 adjust(index)
1929 }
1930 if let Some(ref mut index) = self.fragment_start {
1931 adjust(index)
1932 }
1933 self.serialization.push_str(&path_and_after);
1934 }
1935 }
1936 self.port = port;
1937 }
1938
1939 pub fn set_host(&mut self, host: Option<&str>) -> Result<(), ParseError> {
2022 if self.cannot_be_a_base() {
2023 return Err(ParseError::SetHostOnCannotBeABaseUrl);
2024 }
2025
2026 let scheme_type = SchemeType::from(self.scheme());
2027
2028 if let Some(host) = host {
2029 if host.is_empty() && scheme_type.is_special() && !scheme_type.is_file() {
2030 return Err(ParseError::EmptyHost);
2031 }
2032 let mut host_substr = host;
2033 if !host.starts_with('[') || !host.ends_with(']') {
2035 match host.find(':') {
2036 Some(0) => {
2037 return Err(ParseError::InvalidDomainCharacter);
2039 }
2040 Some(colon_index) => {
2042 host_substr = &host[..colon_index];
2043 }
2044 None => {}
2045 }
2046 }
2047 if SchemeType::from(self.scheme()).is_special() {
2048 self.set_host_internal(Host::parse_cow(host_substr.into())?, None);
2049 } else {
2050 self.set_host_internal(Host::parse_opaque_cow(host_substr.into())?, None);
2051 }
2052 } else if self.has_host() {
2053 if scheme_type.is_special() && !scheme_type.is_file() {
2054 return Err(ParseError::EmptyHost);
2055 } else if self.serialization.len() == self.path_start as usize {
2056 self.serialization.push('/');
2057 }
2058 if true {
if !(self.byte_at(self.scheme_end) == b':') {
::core::panicking::panic("assertion failed: self.byte_at(self.scheme_end) == b\':\'")
};
};debug_assert!(self.byte_at(self.scheme_end) == b':');
2059 if true {
if !(self.byte_at(self.path_start) == b'/') {
::core::panicking::panic("assertion failed: self.byte_at(self.path_start) == b\'/\'")
};
};debug_assert!(self.byte_at(self.path_start) == b'/');
2060
2061 let new_path_start = if scheme_type.is_file() {
2062 self.scheme_end + 3
2063 } else {
2064 self.scheme_end + 1
2065 };
2066
2067 self.serialization
2068 .drain(new_path_start as usize..self.path_start as usize);
2069 let offset = self.path_start - new_path_start;
2070 self.path_start = new_path_start;
2071 self.username_end = new_path_start;
2072 self.host_start = new_path_start;
2073 self.host_end = new_path_start;
2074 self.port = None;
2075 if let Some(ref mut index) = self.query_start {
2076 *index -= offset
2077 }
2078 if let Some(ref mut index) = self.fragment_start {
2079 *index -= offset
2080 }
2081 }
2082 Ok(())
2083 }
2084
2085 fn set_host_internal(&mut self, host: Host<Cow<'_, str>>, opt_new_port: Option<Option<u16>>) {
2087 let old_suffix_pos = if opt_new_port.is_some() {
2088 self.path_start
2089 } else {
2090 self.host_end
2091 };
2092 let suffix = self.slice(old_suffix_pos..).to_owned();
2093 self.serialization.truncate(self.host_start as usize);
2094 if !self.has_authority() {
2095 if true {
if !(self.slice(self.scheme_end..self.host_start) == ":") {
::core::panicking::panic("assertion failed: self.slice(self.scheme_end..self.host_start) == \":\"")
};
};debug_assert!(self.slice(self.scheme_end..self.host_start) == ":");
2096 if true {
if !(self.username_end == self.host_start) {
::core::panicking::panic("assertion failed: self.username_end == self.host_start")
};
};debug_assert!(self.username_end == self.host_start);
2097 self.serialization.push('/');
2098 self.serialization.push('/');
2099 self.username_end += 2;
2100 self.host_start += 2;
2101 }
2102 (&mut self.serialization).write_fmt(format_args!("{0}", host))write!(&mut self.serialization, "{host}").unwrap();
2103 self.host_end = to_u32(self.serialization.len()).unwrap();
2104 self.host = host.into();
2105
2106 if let Some(new_port) = opt_new_port {
2107 self.port = new_port;
2108 if let Some(port) = new_port {
2109 (&mut self.serialization).write_fmt(format_args!(":{0}", port))write!(&mut self.serialization, ":{port}").unwrap();
2110 }
2111 }
2112 let new_suffix_pos = to_u32(self.serialization.len()).unwrap();
2113 self.serialization.push_str(&suffix);
2114
2115 let adjust = |index: &mut u32| {
2116 *index -= old_suffix_pos;
2117 *index += new_suffix_pos;
2118 };
2119 adjust(&mut self.path_start);
2120 if let Some(ref mut index) = self.query_start {
2121 adjust(index)
2122 }
2123 if let Some(ref mut index) = self.fragment_start {
2124 adjust(index)
2125 }
2126 }
2127
2128 #[allow(clippy::result_unit_err)]
2166 pub fn set_ip_host(&mut self, address: IpAddr) -> Result<(), ()> {
2167 if self.cannot_be_a_base() {
2168 return Err(());
2169 }
2170
2171 let address = match address {
2172 IpAddr::V4(address) => Host::Ipv4(address),
2173 IpAddr::V6(address) => Host::Ipv6(address),
2174 };
2175 self.set_host_internal(address, None);
2176 Ok(())
2177 }
2178
2179 #[allow(clippy::result_unit_err)]
2206 pub fn set_password(&mut self, password: Option<&str>) -> Result<(), ()> {
2207 if !self.has_host() || self.host() == Some(Host::Domain("")) || self.scheme() == "file" {
2209 return Err(());
2210 }
2211 let password = password.unwrap_or_default();
2212 if !password.is_empty() {
2213 let host_and_after = self.slice(self.host_start..).to_owned();
2214 self.serialization.truncate(self.username_end as usize);
2215 self.serialization.push(':');
2216 self.serialization
2217 .extend(utf8_percent_encode(password, USERINFO));
2218 self.serialization.push('@');
2219
2220 let old_host_start = self.host_start;
2221 let new_host_start = to_u32(self.serialization.len()).unwrap();
2222 let adjust = |index: &mut u32| {
2223 *index -= old_host_start;
2224 *index += new_host_start;
2225 };
2226 self.host_start = new_host_start;
2227 adjust(&mut self.host_end);
2228 adjust(&mut self.path_start);
2229 if let Some(ref mut index) = self.query_start {
2230 adjust(index)
2231 }
2232 if let Some(ref mut index) = self.fragment_start {
2233 adjust(index)
2234 }
2235
2236 self.serialization.push_str(&host_and_after);
2237 } else if self.byte_at(self.username_end) == b':' {
2238 let has_username_or_password = self.byte_at(self.host_start - 1) == b'@';
2240 if true {
if !has_username_or_password {
::core::panicking::panic("assertion failed: has_username_or_password")
};
};debug_assert!(has_username_or_password);
2241 let username_start = self.scheme_end + 3;
2242 let empty_username = username_start == self.username_end;
2243 let start = self.username_end; let end = if empty_username {
2245 self.host_start } else {
2247 self.host_start - 1 };
2249 self.serialization.drain(start as usize..end as usize);
2250 let offset = end - start;
2251 self.host_start -= offset;
2252 self.host_end -= offset;
2253 self.path_start -= offset;
2254 if let Some(ref mut index) = self.query_start {
2255 *index -= offset
2256 }
2257 if let Some(ref mut index) = self.fragment_start {
2258 *index -= offset
2259 }
2260 }
2261 Ok(())
2262 }
2263
2264 #[allow(clippy::result_unit_err)]
2300 pub fn set_username(&mut self, username: &str) -> Result<(), ()> {
2301 if !self.has_host() || self.host() == Some(Host::Domain("")) || self.scheme() == "file" {
2303 return Err(());
2304 }
2305 let username_start = self.scheme_end + 3;
2306 if true {
if !(self.slice(self.scheme_end..username_start) == "://") {
::core::panicking::panic("assertion failed: self.slice(self.scheme_end..username_start) == \"://\"")
};
};debug_assert!(self.slice(self.scheme_end..username_start) == "://");
2307 if self.slice(username_start..self.username_end) == username {
2308 return Ok(());
2309 }
2310 let after_username = self.slice(self.username_end..).to_owned();
2311 self.serialization.truncate(username_start as usize);
2312 self.serialization
2313 .extend(utf8_percent_encode(username, USERINFO));
2314
2315 let mut removed_bytes = self.username_end;
2316 self.username_end = to_u32(self.serialization.len()).unwrap();
2317 let mut added_bytes = self.username_end;
2318
2319 let new_username_is_empty = self.username_end == username_start;
2320 match (new_username_is_empty, after_username.chars().next()) {
2321 (true, Some('@')) => {
2322 removed_bytes += 1;
2323 self.serialization.push_str(&after_username[1..]);
2324 }
2325 (false, Some('@')) | (_, Some(':')) | (true, _) => {
2326 self.serialization.push_str(&after_username);
2327 }
2328 (false, _) => {
2329 added_bytes += 1;
2330 self.serialization.push('@');
2331 self.serialization.push_str(&after_username);
2332 }
2333 }
2334
2335 let adjust = |index: &mut u32| {
2336 *index -= removed_bytes;
2337 *index += added_bytes;
2338 };
2339 adjust(&mut self.host_start);
2340 adjust(&mut self.host_end);
2341 adjust(&mut self.path_start);
2342 if let Some(ref mut index) = self.query_start {
2343 adjust(index)
2344 }
2345 if let Some(ref mut index) = self.fragment_start {
2346 adjust(index)
2347 }
2348 Ok(())
2349 }
2350
2351 #[allow(clippy::result_unit_err, clippy::suspicious_operation_groupings)]
2462 pub fn set_scheme(&mut self, scheme: &str) -> Result<(), ()> {
2463 let mut parser = Parser::for_setter(String::new());
2464 let remaining = parser.parse_scheme(parser::Input::new_no_trim(scheme))?;
2465 let new_scheme_type = SchemeType::from(&parser.serialization);
2466 let old_scheme_type = SchemeType::from(self.scheme());
2467 if (new_scheme_type.is_special() && !old_scheme_type.is_special()) ||
2469 (!new_scheme_type.is_special() && old_scheme_type.is_special()) ||
2471 (new_scheme_type.is_file() && self.has_authority())
2474 {
2475 return Err(());
2476 }
2477
2478 if !remaining.is_empty() || (!self.has_host() && new_scheme_type.is_special()) {
2479 return Err(());
2480 }
2481 let old_scheme_end = self.scheme_end;
2482 let new_scheme_end = to_u32(parser.serialization.len()).unwrap();
2483 let adjust = |index: &mut u32| {
2484 *index -= old_scheme_end;
2485 *index += new_scheme_end;
2486 };
2487
2488 self.scheme_end = new_scheme_end;
2489 adjust(&mut self.username_end);
2490 adjust(&mut self.host_start);
2491 adjust(&mut self.host_end);
2492 adjust(&mut self.path_start);
2493 if let Some(ref mut index) = self.query_start {
2494 adjust(index)
2495 }
2496 if let Some(ref mut index) = self.fragment_start {
2497 adjust(index)
2498 }
2499
2500 parser.serialization.push_str(self.slice(old_scheme_end..));
2501 self.serialization = parser.serialization;
2502
2503 let previous_port = self.port();
2508 let _ = self.set_port(previous_port);
2509
2510 Ok(())
2511 }
2512
2513 #[cfg(all(
2543 feature = "std",
2544 any(
2545 unix,
2546 windows,
2547 target_os = "redox",
2548 target_os = "wasi",
2549 target_os = "hermit"
2550 )
2551 ))]
2552 #[allow(clippy::result_unit_err)]
2553 pub fn from_file_path<P: AsRef<std::path::Path>>(path: P) -> Result<Self, ()> {
2554 let mut serialization = "file://".to_owned();
2555 let host_start = serialization.len() as u32;
2556 let (host_end, host) = path_to_file_url_segments(path.as_ref(), &mut serialization)?;
2557 Ok(Self {
2558 serialization,
2559 scheme_end: "file".len() as u32,
2560 username_end: host_start,
2561 host_start,
2562 host_end,
2563 host,
2564 port: None,
2565 path_start: host_end,
2566 query_start: None,
2567 fragment_start: None,
2568 })
2569 }
2570
2571 #[cfg(all(
2591 feature = "std",
2592 any(
2593 unix,
2594 windows,
2595 target_os = "redox",
2596 target_os = "wasi",
2597 target_os = "hermit"
2598 )
2599 ))]
2600 #[allow(clippy::result_unit_err)]
2601 pub fn from_directory_path<P: AsRef<std::path::Path>>(path: P) -> Result<Self, ()> {
2602 let mut url = Self::from_file_path(path)?;
2603 if !url.serialization.ends_with('/') {
2604 url.serialization.push('/')
2605 }
2606 Ok(url)
2607 }
2608
2609 #[cfg(feature = "serde")]
2616 #[deny(unused)]
2617 pub fn serialize_internal<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
2618 where
2619 S: serde::Serializer,
2620 {
2621 use serde::Serialize;
2622 let Url {
2625 ref serialization,
2626 ref scheme_end,
2627 ref username_end,
2628 ref host_start,
2629 ref host_end,
2630 ref host,
2631 ref port,
2632 ref path_start,
2633 ref query_start,
2634 ref fragment_start,
2635 } = *self;
2636 (
2637 serialization,
2638 scheme_end,
2639 username_end,
2640 host_start,
2641 host_end,
2642 host,
2643 port,
2644 path_start,
2645 query_start,
2646 fragment_start,
2647 )
2648 .serialize(serializer)
2649 }
2650
2651 #[cfg(feature = "serde")]
2658 #[deny(unused)]
2659 pub fn deserialize_internal<'de, D>(deserializer: D) -> Result<Self, D::Error>
2660 where
2661 D: serde::Deserializer<'de>,
2662 {
2663 use serde::de::{Deserialize, Error};
2664 let (
2665 serialization,
2666 scheme_end,
2667 username_end,
2668 host_start,
2669 host_end,
2670 host,
2671 port,
2672 path_start,
2673 query_start,
2674 fragment_start,
2675 ) = Deserialize::deserialize(deserializer)?;
2676 let url = Url {
2677 serialization,
2678 scheme_end,
2679 username_end,
2680 host_start,
2681 host_end,
2682 host,
2683 port,
2684 path_start,
2685 query_start,
2686 fragment_start,
2687 };
2688 if truecfg!(debug_assertions) {
2689 url.check_invariants().map_err(Error::custom)?
2690 }
2691 Ok(url)
2692 }
2693
2694 #[inline]
2715 #[cfg(all(
2716 feature = "std",
2717 any(
2718 unix,
2719 windows,
2720 target_os = "redox",
2721 target_os = "wasi",
2722 target_os = "hermit"
2723 )
2724 ))]
2725 #[allow(clippy::result_unit_err)]
2726 pub fn to_file_path(&self) -> Result<PathBuf, ()> {
2727 if let Some(segments) = self.path_segments() {
2728 let host = match self.host() {
2729 None | Some(Host::Domain("localhost")) => None,
2730 Some(_) if falsecfg!(windows) && self.scheme() == "file" => {
2731 Some(&self.serialization[self.host_start as usize..self.host_end as usize])
2732 }
2733 _ => return Err(()),
2734 };
2735
2736 let str_len = self.as_str().len();
2737 let estimated_capacity = if falsecfg!(target_os = "redox") {
2738 let scheme_len = self.scheme().len();
2739 let file_scheme_len = "file".len();
2740 if scheme_len < file_scheme_len {
2742 let scheme_diff = file_scheme_len - scheme_len;
2743 (str_len + scheme_diff).saturating_sub(2)
2744 } else {
2745 let scheme_diff = scheme_len - file_scheme_len;
2746 str_len.saturating_sub(scheme_diff + 2)
2747 }
2748 } else if falsecfg!(windows) {
2749 str_len.saturating_sub(self.scheme().len() + 1)
2751 } else {
2752 str_len.saturating_sub(self.scheme().len() + 3)
2754 };
2755 return file_url_segments_to_pathbuf(estimated_capacity, host, segments);
2756 }
2757 Err(())
2758 }
2759
2760 #[inline]
2763 fn slice<R>(&self, range: R) -> &str
2764 where
2765 R: RangeArg,
2766 {
2767 range.slice_of(&self.serialization)
2768 }
2769
2770 #[inline]
2771 fn byte_at(&self, i: u32) -> u8 {
2772 self.serialization.as_bytes()[i as usize]
2773 }
2774}
2775
2776impl str::FromStr for Url {
2778 type Err = ParseError;
2779
2780 #[inline]
2781 fn from_str(input: &str) -> Result<Self, crate::ParseError> {
2782 Self::parse(input)
2783 }
2784}
2785
2786impl<'a> TryFrom<&'a str> for Url {
2787 type Error = ParseError;
2788
2789 fn try_from(s: &'a str) -> Result<Self, Self::Error> {
2790 Self::parse(s)
2791 }
2792}
2793
2794impl fmt::Display for Url {
2796 #[inline]
2797 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
2798 fmt::Display::fmt(&self.serialization, formatter)
2799 }
2800}
2801
2802impl From<Url> for String {
2804 fn from(value: Url) -> Self {
2805 value.serialization
2806 }
2807}
2808
2809impl fmt::Debug for Url {
2811 #[inline]
2812 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
2813 formatter
2814 .debug_struct("Url")
2815 .field("scheme", &self.scheme())
2816 .field("cannot_be_a_base", &self.cannot_be_a_base())
2817 .field("username", &self.username())
2818 .field("password", &self.password())
2819 .field("host", &self.host())
2820 .field("port", &self.port())
2821 .field("path", &self.path())
2822 .field("query", &self.query())
2823 .field("fragment", &self.fragment())
2824 .finish()
2825 }
2826}
2827
2828impl Eq for Url {}
2830
2831impl PartialEq for Url {
2833 #[inline]
2834 fn eq(&self, other: &Self) -> bool {
2835 self.serialization == other.serialization
2836 }
2837}
2838
2839impl Ord for Url {
2841 #[inline]
2842 fn cmp(&self, other: &Self) -> cmp::Ordering {
2843 self.serialization.cmp(&other.serialization)
2844 }
2845}
2846
2847impl PartialOrd for Url {
2849 #[inline]
2850 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
2851 Some(self.cmp(other))
2852 }
2853}
2854
2855impl hash::Hash for Url {
2857 #[inline]
2858 fn hash<H>(&self, state: &mut H)
2859 where
2860 H: hash::Hasher,
2861 {
2862 hash::Hash::hash(&self.serialization, state)
2863 }
2864}
2865
2866impl AsRef<str> for Url {
2868 #[inline]
2869 fn as_ref(&self) -> &str {
2870 &self.serialization
2871 }
2872}
2873
2874trait RangeArg {
2875 fn slice_of<'a>(&self, s: &'a str) -> &'a str;
2876}
2877
2878impl RangeArg for Range<u32> {
2879 #[inline]
2880 fn slice_of<'a>(&self, s: &'a str) -> &'a str {
2881 &s[self.start as usize..self.end as usize]
2882 }
2883}
2884
2885impl RangeArg for RangeFrom<u32> {
2886 #[inline]
2887 fn slice_of<'a>(&self, s: &'a str) -> &'a str {
2888 &s[self.start as usize..]
2889 }
2890}
2891
2892impl RangeArg for RangeTo<u32> {
2893 #[inline]
2894 fn slice_of<'a>(&self, s: &'a str) -> &'a str {
2895 &s[..self.end as usize]
2896 }
2897}
2898
2899#[cfg(feature = "serde")]
2903impl serde::Serialize for Url {
2904 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
2905 where
2906 S: serde::Serializer,
2907 {
2908 serializer.serialize_str(self.as_str())
2909 }
2910}
2911
2912#[cfg(feature = "serde")]
2916impl<'de> serde::Deserialize<'de> for Url {
2917 fn deserialize<D>(deserializer: D) -> Result<Url, D::Error>
2918 where
2919 D: serde::Deserializer<'de>,
2920 {
2921 use serde::de::{Error, Visitor};
2922
2923 struct UrlVisitor;
2924
2925 impl Visitor<'_> for UrlVisitor {
2926 type Value = Url;
2927
2928 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
2929 formatter.write_str("a string representing an URL")
2930 }
2931
2932 fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
2933 where
2934 E: Error,
2935 {
2936 Url::parse(s).map_err(|err| Error::custom(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0}: {1:?}", err, s))
})format!("{err}: {s:?}")))
2937 }
2938 }
2939
2940 deserializer.deserialize_str(UrlVisitor)
2941 }
2942}
2943
2944#[cfg(all(
2945 feature = "std",
2946 any(unix, target_os = "redox", target_os = "wasi", target_os = "hermit")
2947))]
2948fn path_to_file_url_segments(
2949 path: &Path,
2950 serialization: &mut String,
2951) -> Result<(u32, HostInternal), ()> {
2952 use parser::SPECIAL_PATH_SEGMENT;
2953 use percent_encoding::percent_encode;
2954 #[cfg(target_os = "hermit")]
2955 use std::os::hermit::ffi::OsStrExt;
2956 #[cfg(any(unix, target_os = "redox"))]
2957 use std::os::unix::prelude::OsStrExt;
2958 if !path.is_absolute() {
2959 return Err(());
2960 }
2961 let host_end = to_u32(serialization.len()).unwrap();
2962 let mut empty = true;
2963 for component in path.components().skip(1) {
2965 empty = false;
2966 serialization.push('/');
2967 #[cfg(not(target_os = "wasi"))]
2968 serialization.extend(percent_encode(
2969 component.as_os_str().as_bytes(),
2970 SPECIAL_PATH_SEGMENT,
2971 ));
2972 #[cfg(target_os = "wasi")]
2973 serialization.extend(percent_encode(
2974 component.as_os_str().to_string_lossy().as_bytes(),
2975 SPECIAL_PATH_SEGMENT,
2976 ));
2977 }
2978 if empty {
2979 serialization.push('/');
2981 }
2982 Ok((host_end, HostInternal::None))
2983}
2984
2985#[cfg(all(feature = "std", windows))]
2986fn path_to_file_url_segments(
2987 path: &Path,
2988 serialization: &mut String,
2989) -> Result<(u32, HostInternal), ()> {
2990 path_to_file_url_segments_windows(path, serialization)
2991}
2992
2993#[cfg(feature = "std")]
2995#[cfg_attr(not(windows), allow(dead_code))]
2996fn path_to_file_url_segments_windows(
2997 path: &Path,
2998 serialization: &mut String,
2999) -> Result<(u32, HostInternal), ()> {
3000 use crate::parser::PATH_SEGMENT;
3001 use percent_encoding::percent_encode;
3002 use std::path::{Component, Prefix};
3003 if !path.is_absolute() {
3004 return Err(());
3005 }
3006 let mut components = path.components();
3007
3008 let host_start = serialization.len() + 1;
3009 let host_end;
3010 let host_internal;
3011
3012 match components.next() {
3013 Some(Component::Prefix(ref p)) => match p.kind() {
3014 Prefix::Disk(letter) | Prefix::VerbatimDisk(letter) => {
3015 host_end = to_u32(serialization.len()).unwrap();
3016 host_internal = HostInternal::None;
3017 serialization.push('/');
3018 serialization.push(letter as char);
3019 serialization.push(':');
3020 }
3021 Prefix::UNC(server, share) | Prefix::VerbatimUNC(server, share) => {
3022 let host = Host::parse_cow(server.to_str().ok_or(())?.into()).map_err(|_| ())?;
3023 serialization.write_fmt(format_args!("{0}", host))write!(serialization, "{host}").unwrap();
3024 host_end = to_u32(serialization.len()).unwrap();
3025 host_internal = host.into();
3026 serialization.push('/');
3027 let share = share.to_str().ok_or(())?;
3028 serialization.extend(percent_encode(share.as_bytes(), PATH_SEGMENT));
3029 }
3030 _ => return Err(()),
3031 },
3032 _ => return Err(()),
3033 }
3034
3035 let mut path_only_has_prefix = true;
3036 for component in components {
3037 if component == Component::RootDir {
3038 continue;
3039 }
3040
3041 path_only_has_prefix = false;
3042 let component = component.as_os_str().to_str().ok_or(())?;
3044
3045 serialization.push('/');
3046 serialization.extend(percent_encode(component.as_bytes(), PATH_SEGMENT));
3047 }
3048
3049 if serialization.len() > host_start
3051 && parser::is_windows_drive_letter(&serialization[host_start..])
3052 && path_only_has_prefix
3053 {
3054 serialization.push('/');
3055 }
3056
3057 Ok((host_end, host_internal))
3058}
3059
3060#[cfg(all(
3061 feature = "std",
3062 any(unix, target_os = "redox", target_os = "wasi", target_os = "hermit")
3063))]
3064fn file_url_segments_to_pathbuf(
3065 estimated_capacity: usize,
3066 host: Option<&str>,
3067 segments: str::Split<'_, char>,
3068) -> Result<PathBuf, ()> {
3069 use alloc::vec::Vec;
3070 use percent_encoding::percent_decode;
3071 #[cfg(not(target_os = "wasi"))]
3072 use std::ffi::OsStr;
3073 #[cfg(target_os = "hermit")]
3074 use std::os::hermit::ffi::OsStrExt;
3075 #[cfg(any(unix, target_os = "redox"))]
3076 use std::os::unix::prelude::OsStrExt;
3077
3078 if host.is_some() {
3079 return Err(());
3080 }
3081
3082 let mut bytes = Vec::new();
3083 bytes.try_reserve(estimated_capacity).map_err(|_| ())?;
3084 if falsecfg!(target_os = "redox") {
3085 bytes.extend(b"file:");
3086 }
3087
3088 for segment in segments {
3089 bytes.push(b'/');
3090 bytes.extend(percent_decode(segment.as_bytes()));
3091 }
3092
3093 if bytes.len() > 2
3095 && bytes[bytes.len() - 2].is_ascii_alphabetic()
3096 && #[allow(non_exhaustive_omitted_patterns)] match bytes[bytes.len() - 1] {
b':' | b'|' => true,
_ => false,
}matches!(bytes[bytes.len() - 1], b':' | b'|')
3097 {
3098 bytes.push(b'/');
3099 }
3100
3101 #[cfg(not(target_os = "wasi"))]
3102 let path = PathBuf::from(OsStr::from_bytes(&bytes));
3103 #[cfg(target_os = "wasi")]
3104 let path = String::from_utf8(bytes)
3105 .map(|path| PathBuf::from(path))
3106 .map_err(|_| ())?;
3107
3108 if true {
if !path.is_absolute() {
::core::panicking::panic("to_file_path() failed to produce an absolute Path")
};
};debug_assert!(
3109 path.is_absolute(),
3110 "to_file_path() failed to produce an absolute Path"
3111 );
3112
3113 Ok(path)
3114}
3115
3116#[cfg(all(feature = "std", windows))]
3117fn file_url_segments_to_pathbuf(
3118 estimated_capacity: usize,
3119 host: Option<&str>,
3120 segments: str::Split<char>,
3121) -> Result<PathBuf, ()> {
3122 file_url_segments_to_pathbuf_windows(estimated_capacity, host, segments)
3123}
3124
3125#[cfg(feature = "std")]
3127#[cfg_attr(not(windows), allow(dead_code))]
3128fn file_url_segments_to_pathbuf_windows(
3129 estimated_capacity: usize,
3130 host: Option<&str>,
3131 mut segments: str::Split<'_, char>,
3132) -> Result<PathBuf, ()> {
3133 use percent_encoding::percent_decode_str;
3134 let mut string = String::new();
3135 string.try_reserve(estimated_capacity).map_err(|_| ())?;
3136 if let Some(host) = host {
3137 string.push_str(r"\\");
3138 string.push_str(host);
3139 } else {
3140 let first = segments.next().ok_or(())?;
3141
3142 match first.len() {
3143 2 => {
3144 if !first.starts_with(parser::ascii_alpha) || first.as_bytes()[1] != b':' {
3145 return Err(());
3146 }
3147
3148 string.push_str(first);
3149 }
3150
3151 4 => {
3152 if !first.starts_with(parser::ascii_alpha) {
3153 return Err(());
3154 }
3155 let bytes = first.as_bytes();
3156 if bytes[1] != b'%' || bytes[2] != b'3' || (bytes[3] != b'a' && bytes[3] != b'A') {
3157 return Err(());
3158 }
3159
3160 string.push_str(&first[0..1]);
3161 string.push(':');
3162 }
3163
3164 _ => return Err(()),
3165 }
3166 };
3167
3168 for segment in segments {
3169 string.push('\\');
3170
3171 match percent_decode_str(segment).decode_utf8() {
3173 Ok(s) => string.push_str(&s),
3174 Err(..) => return Err(()),
3175 }
3176 }
3177 if falsecfg!(test) {
3179 if true {
if !(string.len() <= estimated_capacity) {
{
::core::panicking::panic_fmt(format_args!("len: {0}, capacity: {1}",
string.len(), estimated_capacity));
}
};
};debug_assert!(
3180 string.len() <= estimated_capacity,
3181 "len: {}, capacity: {}",
3182 string.len(),
3183 estimated_capacity
3184 );
3185 }
3186 let path = PathBuf::from(string);
3187 if true {
if !path.is_absolute() {
::core::panicking::panic("to_file_path() failed to produce an absolute Path")
};
};debug_assert!(
3188 path.is_absolute(),
3189 "to_file_path() failed to produce an absolute Path"
3190 );
3191 Ok(path)
3192}
3193
3194)]
3196pub struct UrlQuery<'a> {
3197 url: Option<&'a mut Url>,
3198 fragment: Option<String>,
3199}
3200
3201impl<'a> form_urlencoded::Target for UrlQuery<'a> {
3212 fn as_mut_string(&mut self) -> &mut String {
3213 &mut self.url.as_mut().unwrap().serialization
3214 }
3215
3216 fn finish(mut self) -> &'a mut Url {
3217 let url = self.url.take().unwrap();
3218 url.restore_already_parsed_fragment(self.fragment.take());
3219 url
3220 }
3221
3222 type Finished = &'a mut Url;
3223}
3224
3225impl Drop for UrlQuery<'_> {
3226 fn drop(&mut self) {
3227 if let Some(url) = self.url.take() {
3228 url.restore_already_parsed_fragment(self.fragment.take())
3229 }
3230 }
3231}