// Copyright 2013-2015 The rust-url developers. // // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms.
Ifyouenablethe`serde`feature,[`Url`](struct.Url.html)willimplement [`serde::Serialize`](https://docs.rs/serde/1/serde/trait.Serialize.html) and [`serde::Deserialize`](https://docs.rs/serde/1/serde/trait.Deserialize.html). See[serdedocumentation](https://serde.rs) for more information.
```toml url={version="2",features=["serde"]} ```
#Feature:`debugger_visualizer`
Ifyouenablethe`debugger_visualizer`feature,the`url`cratewillinclude a[natvisfile](https://docs.microsoft.com/en-us/visualstudio/debugger/create-custom-views-of-native-objects) for[VisualStudio](https://www.visualstudio.com/) that allows you to view [`Url`](struct.Url.html)objectsinthedebugger.
// For forwards compatibility #[cfg(feature = "std")] externcrate std;
#[macro_use] externcrate alloc;
#[cfg(feature = "serde")] externcrate serde;
usecrate::host::HostInternal;
usecrate::net::IpAddr; #[cfg(feature = "std")] #[cfg(any(
unix,
windows,
target_os = "redox",
target_os = "wasi",
target_os = "hermit"
))] usecrate::net::{SocketAddr, ToSocketAddrs}; usecrate::parser::{to_u32, Context, Parser, SchemeType, USERINFO}; use alloc::borrow::ToOwned; use alloc::str; use alloc::string::{String, ToString}; use core::borrow::Borrow; use core::convert::TryFrom; use core::fmt::Write; use core::ops::{Range, RangeFrom, RangeTo}; use core::{cmp, fmt, hash, mem}; use percent_encoding::utf8_percent_encode; #[cfg(feature = "std")] #[cfg(any(
unix,
windows,
target_os = "redox",
target_os = "wasi",
target_os = "hermit"
))] use std::io; #[cfg(feature = "std")] use std::path::{Path, PathBuf};
/// `std` version of `net` #[cfg(feature = "std")] pub(crate) mod net { pubuse std::net::*;
} /// `no_std` nightly version of `net` #[cfg(not(feature = "std"))] pub(crate) mod net { pubuse core::net::*;
}
// Components
scheme_end: u32, // Before ':'
username_end: u32, // Before ':' (if a password is given) or '@' (if not)
host_start: u32,
host_end: u32,
host: HostInternal,
port: Option<u16>,
path_start: u32, // Before initial '/', if any
query_start: Option<u32>, // Before '?', unlike Position::QueryStart
fragment_start: Option<u32>, // Before '#', unlike Position::FragmentStart
}
/// Full configuration for the URL parser. #[derive(Copy, Clone)] #[must_use] pubstruct ParseOptions<'a> {
base_url: Option<&'a Url>,
encoding_override: EncodingOverride<'a>,
violation_fn: Option<&'a dyn Fn(SyntaxViolation)>,
}
impl<'a> ParseOptions<'a> { /// Change the base URL /// /// See the notes of [`Url::join`] for more details about how this base is considered /// when parsing. pubfn base_url(mutself, new: Option<&'a Url>) -> Self { self.base_url = new; self
}
/// Override the character encoding of query strings. /// This is a legacy concept only relevant for HTML. pubfn encoding_override(mutself, new: EncodingOverride<'a>) -> Self { self.encoding_override = new; self
}
/// Call the provided function or closure for a non-fatal `SyntaxViolation` /// when it occurs during parsing. Note that since the provided function is /// `Fn`, the caller might need to utilize _interior mutability_, such as with /// a `RefCell`, to collect the violations. /// /// ## Example /// ``` /// use std::cell::RefCell; /// use url::{Url, SyntaxViolation}; /// # use url::ParseError; /// # fn run() -> Result<(), url::ParseError> { /// let violations = RefCell::new(Vec::new()); /// let url = Url::options() /// .syntax_violation_callback(Some(&|v| violations.borrow_mut().push(v))) /// .parse("https:////example.com")?; /// assert_eq!(url.as_str(), "https://example.com/"); /// assert_eq!(violations.into_inner(), /// vec!(SyntaxViolation::ExpectedDoubleSlash)); /// # Ok(()) /// # } /// # run().unwrap(); /// ``` pubfn syntax_violation_callback(mutself, new: Option<&'a dyn Fn(SyntaxViolation)>) -> Self { self.violation_fn = new; self
}
/// Parse an URL string with the configuration so far. pubfn parse(self, input: &str) -> Result<Url, crate::ParseError> {
Parser {
serialization: String::with_capacity(input.len()),
base_url: self.base_url,
query_encoding_override: self.encoding_override,
violation_fn: self.violation_fn,
context: Context::UrlParser,
}
.parse_url(input)
}
}
impl Url { /// Parse an absolute URL from a string. /// /// # Examples /// /// ```rust /// use url::Url; /// # use url::ParseError; /// /// # fn run() -> Result<(), ParseError> { /// let url = Url::parse("https://example.net")?; /// # Ok(()) /// # } /// # run().unwrap(); /// ``` /// /// # Errors /// /// If the function can not parse an absolute URL from the given string, /// a [`ParseError`] variant will be returned. /// /// [`ParseError`]: enum.ParseError.html #[inline] pubfn parse(input: &str) -> Result<Url, crate::ParseError> {
Url::options().parse(input)
}
/// Parse an absolute URL from a string and add params to its query string. /// /// Existing params are not removed. /// /// # Examples /// /// ```rust /// use url::Url; /// # use url::ParseError; /// /// # fn run() -> Result<(), ParseError> { /// let url = Url::parse_with_params("https://example.net?dont=clobberme", /// &[("lang", "rust"), ("browser", "servo")])?; /// assert_eq!("https://example.net/?dont=clobberme&lang=rust&browser=servo", url.as_str()); /// # Ok(()) /// # } /// # run().unwrap(); /// ``` /// /// # Errors /// /// If the function can not parse an absolute URL from the given string, /// a [`ParseError`] variant will be returned. /// /// [`ParseError`]: enum.ParseError.html #[inline] pubfn parse_with_params<I, K, V>(input: &str, iter: I) -> Result<Url, crate::ParseError> where
I: IntoIterator,
I::Item: Borrow<(K, V)>,
K: AsRef<str>,
V: AsRef<str>,
{ letmut url = Url::options().parse(input);
let start = self.serialization.len() - trailing_space_count;
self.serialization.truncate(start);
}
/// Parse a string as an URL, with this URL as the base URL. /// /// The inverse of this is [`make_relative`]. /// /// # Notes /// /// - A trailing slash is significant. /// Without it, the last path component is considered to be a “file” name /// to be removed to get at the “directory” that is used as the base. /// - A [scheme relative special URL](https://url.spec.whatwg.org/#scheme-relative-special-url-string) /// as input replaces everything in the base URL after the scheme. /// - An absolute URL (with a scheme) as input replaces the whole base URL (even the scheme). /// /// # Examples /// /// ```rust /// use url::Url; /// # use url::ParseError; /// /// // Base without a trailing slash /// # fn run() -> Result<(), ParseError> { /// let base = Url::parse("https://example.net/a/b.html")?; /// let url = base.join("c.png")?; /// assert_eq!(url.as_str(), "https://example.net/a/c.png"); // Not /a/b.html/c.png /// /// // Base with a trailing slash /// let base = Url::parse("https://example.net/a/b/")?; /// let url = base.join("c.png")?; /// assert_eq!(url.as_str(), "https://example.net/a/b/c.png"); /// /// // Input as scheme relative special URL /// let base = Url::parse("https://alice.com/a")?; /// let url = base.join("//eve.com/b")?; /// assert_eq!(url.as_str(), "https://eve.com/b"); /// /// // Input as absolute URL /// let base = Url::parse("https://alice.com/a")?; /// let url = base.join("http://eve.com/b")?; /// assert_eq!(url.as_str(), "http://eve.com/b"); // http instead of https
/// # Ok(()) /// # } /// # run().unwrap(); /// ``` /// /// # Errors /// /// If the function can not parse an URL from the given string /// with this URL as the base URL, a [`ParseError`] variant will be returned. /// /// [`ParseError`]: enum.ParseError.html /// [`make_relative`]: #method.make_relative #[inline] pubfn join(&self, input: &str) -> Result<Url, crate::ParseError> {
Url::options().base_url(Some(self)).parse(input)
}
/// Creates a relative URL if possible, with this URL as the base URL. /// /// This is the inverse of [`join`]. /// /// # Examples /// /// ```rust /// use url::Url; /// # use url::ParseError; /// /// # fn run() -> Result<(), ParseError> { /// let base = Url::parse("https://example.net/a/b.html")?; /// let url = Url::parse("https://example.net/a/c.png")?; /// let relative = base.make_relative(&url); /// assert_eq!(relative.as_ref().map(|s| s.as_str()), Some("c.png")); /// /// let base = Url::parse("https://example.net/a/b/")?; /// let url = Url::parse("https://example.net/a/b/c.png")?; /// let relative = base.make_relative(&url); /// assert_eq!(relative.as_ref().map(|s| s.as_str()), Some("c.png")); /// /// let base = Url::parse("https://example.net/a/b/")?; /// let url = Url::parse("https://example.net/a/d/c.png")?; /// let relative = base.make_relative(&url); /// assert_eq!(relative.as_ref().map(|s| s.as_str()), Some("../d/c.png")); /// /// let base = Url::parse("https://example.net/a/b.html?c=d")?; /// let url = Url::parse("https://example.net/a/b.html?e=f")?; /// let relative = base.make_relative(&url); /// assert_eq!(relative.as_ref().map(|s| s.as_str()), Some("?e=f")); /// # Ok(()) /// # } /// # run().unwrap(); /// ``` /// /// # Errors /// /// If this URL can't be a base for the given URL, `None` is returned. /// This is for example the case if the scheme, host or port are not the same. /// /// [`join`]: #method.join pubfn make_relative(&self, url: &Url) -> Option<String> { ifself.cannot_be_a_base() { return None;
}
// Scheme, host and port need to be the same ifself.scheme() != url.scheme() || self.host() != url.host() || self.port() != url.port() { return None;
}
// We ignore username/password at this point
// The path has to be transformed letmut relative = String::new();
// Extract the filename of both URIs, these need to be handled separately fn extract_path_filename(s: &str) -> (&str, &str) { let last_slash_idx = s.rfind('/').unwrap_or(0); let (path, filename) = s.split_at(last_slash_idx); if filename.is_empty() {
(path, "")
} else {
(path, &filename[1..])
}
}
let (base_path, base_filename) = extract_path_filename(self.path()); let (url_path, url_filename) = extract_path_filename(url.path());
// Skip over the common prefix while base_path.peek().is_some() && base_path.peek() == url_path.peek() {
base_path.next();
url_path.next();
}
// Add `..` segments for the remainder of the base path for base_path_segment in base_path { // Skip empty last segments if base_path_segment.is_empty() { break;
}
if !relative.is_empty() {
relative.push('/');
}
relative.push_str("..");
}
// Append the remainder of the other URI for url_path_segment in url_path { if !relative.is_empty() {
relative.push('/');
}
relative.push_str(url_path_segment);
}
// Add the filename if they are not the same if !relative.is_empty() || base_filename != url_filename { // If the URIs filename is empty this means that it was a directory // so we'll have to append a '/'. // // Otherwise append it directly as the new filename. if url_filename.is_empty() {
relative.push('/');
} else { if !relative.is_empty() {
relative.push('/');
}
relative.push_str(url_filename);
}
}
// Query and fragment are only taken from the other URI iflet Some(query) = url.query() {
relative.push('?');
relative.push_str(query);
}
/// Return a default `ParseOptions` that can fully configure the URL parser. /// /// # Examples /// /// Get default `ParseOptions`, then change base url /// /// ```rust /// use url::Url; /// # use url::ParseError; /// # fn run() -> Result<(), ParseError> { /// let options = Url::options(); /// let api = Url::parse("https://api.example.com")?; /// let base_url = options.base_url(Some(&api)); /// let version_url = base_url.parse("version.json")?; /// assert_eq!(version_url.as_str(), "https://api.example.com/version.json"); /// # Ok(()) /// # } /// # run().unwrap(); /// ``` pubfn options<'a>() -> ParseOptions<'a> {
ParseOptions {
base_url: None,
encoding_override: None,
violation_fn: None,
}
}
/// Return the serialization of this URL. /// /// This is fast since that serialization is already stored in the `Url` struct. /// /// # Examples /// /// ```rust /// use url::Url; /// # use url::ParseError; /// /// # fn run() -> Result<(), ParseError> { /// let url_str = "https://example.net/"; /// let url = Url::parse(url_str)?; /// assert_eq!(url.as_str(), url_str); /// # Ok(()) /// # } /// # run().unwrap(); /// ``` #[inline] pubfn as_str(&self) -> &str {
&self.serialization
}
/// Return the serialization of this URL. /// /// This consumes the `Url` and takes ownership of the `String` stored in it. /// /// # Examples /// /// ```rust /// use url::Url; /// # use url::ParseError; /// /// # fn run() -> Result<(), ParseError> { /// let url_str = "https://example.net/"; /// let url = Url::parse(url_str)?; /// assert_eq!(String::from(url), url_str); /// # Ok(()) /// # } /// # run().unwrap(); /// ``` #[inline] #[deprecated(since = "2.3.0", note = "use Into<String>")] pubfn into_string(self) -> String { self.into()
}
/// For internal testing, not part of the public API. /// /// Methods of the `Url` struct assume a number of invariants. /// This checks each of these invariants and panic if one is not met. /// This is for testing rust-url itself. #[doc(hidden)] pubfn check_invariants(&self) -> Result<(), String> {
macro_rules! assert {
($x: expr) => { if !$x { return Err(format!( "!( {} ) for URL {:?}",
stringify!($x), self.serialization
));
}
};
}
macro_rules! assert_eq {
($a: expr, $b: expr) => {
{ let a = $a; let b = $b; if a != b { return Err(format!("{:?} != {:?} ({} != {}) for URL {:?}",
a, b, stringify!($a), stringify!($b), self.serialization))
}
}
}
}
let other = Url::parse(self.as_str()).expect("Failed to parse myself?");
assert_eq!(&self.serialization, &other.serialization);
assert_eq!(self.scheme_end, other.scheme_end);
assert_eq!(self.username_end, other.username_end);
assert_eq!(self.host_start, other.host_start);
assert_eq!(self.host_end, other.host_end);
assert!( self.host == other.host || // XXX No host round-trips to empty host. // See https://github.com/whatwg/url/issues/79
(self.host_str(), other.host_str()) == (None, Some(""))
);
assert_eq!(self.port, other.port);
assert_eq!(self.path_start, other.path_start);
assert_eq!(self.query_start, other.query_start);
assert_eq!(self.fragment_start, other.fragment_start);
Ok(())
}
/// Return the origin of this URL (<https://url.spec.whatwg.org/#origin>) /// /// Note: this returns an opaque origin for `file:` URLs, which causes /// `url.origin() != url.origin()`. /// /// # Examples /// /// URL with `ftp` scheme: /// /// ```rust /// use url::{Host, Origin, Url}; /// # use url::ParseError; /// /// # fn run() -> Result<(), ParseError> { /// let url = Url::parse("ftp://example.com/foo")?; /// assert_eq!(url.origin(), /// Origin::Tuple("ftp".into(), /// Host::Domain("example.com".into()), /// 21)); /// # Ok(()) /// # } /// # run().unwrap(); /// ``` /// /// URL with `blob` scheme: /// /// ```rust /// use url::{Host, Origin, Url}; /// # use url::ParseError; /// /// # fn run() -> Result<(), ParseError> { /// let url = Url::parse("blob:https://example.com/foo")?; /// assert_eq!(url.origin(), /// Origin::Tuple("https".into(), /// Host::Domain("example.com".into()), /// 443)); /// # Ok(()) /// # } /// # run().unwrap(); /// ``` /// /// URL with `file` scheme: /// /// ```rust /// use url::{Host, Origin, Url}; /// # use url::ParseError; /// /// # fn run() -> Result<(), ParseError> { /// let url = Url::parse("file:///tmp/foo")?; /// assert!(!url.origin().is_tuple()); /// /// let other_url = Url::parse("file:///tmp/foo")?; /// assert!(url.origin() != other_url.origin()); /// # Ok(()) /// # } /// # run().unwrap(); /// ``` /// /// URL with other scheme: /// /// ```rust /// use url::{Host, Origin, Url}; /// # use url::ParseError; /// /// # fn run() -> Result<(), ParseError> { /// let url = Url::parse("foo:bar")?; /// assert!(!url.origin().is_tuple()); /// # Ok(()) /// # } /// # run().unwrap(); /// ``` #[inline] pubfn origin(&self) -> Origin {
origin::url_origin(self)
}
/// Return the scheme of this URL, lower-cased, as an ASCII string without the ':' delimiter. /// /// # Examples /// /// ``` /// use url::Url; /// # use url::ParseError; /// /// # fn run() -> Result<(), ParseError> { /// let url = Url::parse("file:///tmp/foo")?; /// assert_eq!(url.scheme(), "file"); /// # Ok(()) /// # } /// # run().unwrap(); /// ``` #[inline] pubfn scheme(&self) -> &str { self.slice(..self.scheme_end)
}
/// Return whether the URL is special (has a special scheme) /// /// # Examples /// /// ``` /// use url::Url; /// # use url::ParseError; /// /// # fn run() -> Result<(), ParseError> { /// assert!(Url::parse("http:///tmp/foo")?.is_special()); /// assert!(Url::parse("file:///tmp/foo")?.is_special()); /// assert!(!Url::parse("moz:///tmp/foo")?.is_special()); /// # Ok(()) /// # } /// # run().unwrap(); /// ``` pubfn is_special(&self) -> bool { let scheme_type = SchemeType::from(self.scheme());
scheme_type.is_special()
}
/// Return whether the URL has an 'authority', /// which can contain a username, password, host, and port number. /// /// URLs that do *not* are either path-only like `unix:/run/foo.socket` /// or cannot-be-a-base like `data:text/plain,Stuff`. /// /// See also the `authority` method. /// /// # Examples /// /// ``` /// use url::Url; /// # use url::ParseError; /// /// # fn run() -> Result<(), ParseError> { /// let url = Url::parse("ftp://rms@example.com")?; /// assert!(url.has_authority()); /// /// let url = Url::parse("unix:/run/foo.socket")?; /// assert!(!url.has_authority()); /// /// let url = Url::parse("data:text/plain,Stuff")?; /// assert!(!url.has_authority()); /// # Ok(()) /// # } /// # run().unwrap(); /// ``` #[inline] pubfn has_authority(&self) -> bool {
debug_assert!(self.byte_at(self.scheme_end) == b':'); self.slice(self.scheme_end..).starts_with("://")
}
/// Return the authority of this URL as an ASCII string. /// /// Non-ASCII domains are punycode-encoded per IDNA if this is the host /// of a special URL, or percent encoded for non-special URLs. /// IPv6 addresses are given between `[` and `]` brackets. /// Ports are omitted if they match the well known port of a special URL. /// /// Username and password are percent-encoded. /// /// See also the `has_authority` method. /// /// # Examples /// /// ``` /// use url::Url; /// # use url::ParseError; /// /// # fn run() -> Result<(), ParseError> { /// let url = Url::parse("unix:/run/foo.socket")?; /// assert_eq!(url.authority(), ""); /// let url = Url::parse("file:///tmp/foo")?; /// assert_eq!(url.authority(), ""); /// let url = Url::parse("https://user:password@example.com/tmp/foo")?; /// assert_eq!(url.authority(), "user:password@example.com"); /// let url = Url::parse("irc://àlex.рф.example.com:6667/foo")?; /// assert_eq!(url.authority(), "%C3%A0lex.%D1%80%D1%84.example.com:6667"); /// let url = Url::parse("http://àlex.рф.example.com:80/foo")?; /// assert_eq!(url.authority(), "xn--lex-8ka.xn--p1ai.example.com"); /// # Ok(()) /// # } /// # run().unwrap(); /// ``` pubfn authority(&self) -> &str { let scheme_separator_len = "://".len() as u32; ifself.has_authority() && self.path_start > self.scheme_end + scheme_separator_len { self.slice(self.scheme_end + scheme_separator_len..self.path_start)
} else { ""
}
}
/// Return whether this URL is a cannot-be-a-base URL, /// meaning that parsing a relative URL string with this URL as the base will return an error. /// /// This is the case if the scheme and `:` delimiter are not followed by a `/` slash, /// as is typically the case of `data:` and `mailto:` URLs. /// /// # Examples /// /// ``` /// use url::Url; /// # use url::ParseError; /// /// # fn run() -> Result<(), ParseError> { /// let url = Url::parse("ftp://rms@example.com")?; /// assert!(!url.cannot_be_a_base()); /// /// let url = Url::parse("unix:/run/foo.socket")?; /// assert!(!url.cannot_be_a_base()); /// /// let url = Url::parse("data:text/plain,Stuff")?; /// assert!(url.cannot_be_a_base()); /// # Ok(()) /// # } /// # run().unwrap(); /// ``` #[inline] pubfn cannot_be_a_base(&self) -> bool {
!self.slice(self.scheme_end + 1..).starts_with('/')
}
/// Return the username for this URL (typically the empty string) /// as a percent-encoded ASCII string. /// /// # Examples /// /// ``` /// use url::Url; /// # use url::ParseError; /// /// # fn run() -> Result<(), ParseError> { /// let url = Url::parse("ftp://rms@example.com")?; /// assert_eq!(url.username(), "rms"); /// /// let url = Url::parse("ftp://:secret123@example.com")?; /// assert_eq!(url.username(), ""); /// /// let url = Url::parse("https://example.com")?; /// assert_eq!(url.username(), ""); /// # Ok(()) /// # } /// # run().unwrap(); /// ``` pubfn username(&self) -> &str { let scheme_separator_len = "://".len() as u32; ifself.has_authority() && self.username_end > self.scheme_end + scheme_separator_len { self.slice(self.scheme_end + scheme_separator_len..self.username_end)
} else { ""
}
}
/// Return the password for this URL, if any, as a percent-encoded ASCII string. /// /// # Examples /// /// ``` /// use url::Url; /// # use url::ParseError; /// /// # fn run() -> Result<(), ParseError> { /// let url = Url::parse("ftp://rms:secret123@example.com")?; /// assert_eq!(url.password(), Some("secret123")); /// /// let url = Url::parse("ftp://:secret123@example.com")?; /// assert_eq!(url.password(), Some("secret123")); /// /// let url = Url::parse("ftp://rms@example.com")?; /// assert_eq!(url.password(), None); /// /// let url = Url::parse("https://example.com")?; /// assert_eq!(url.password(), None); /// # Ok(()) /// # } /// # run().unwrap(); /// ``` pubfn password(&self) -> Option<&str> { // This ':' is not the one marking a port number since a host can not be empty. // (Except for file: URLs, which do not have port numbers.) ifself.has_authority()
&& self.username_end != self.serialization.len() as u32
&& self.byte_at(self.username_end) == b':'
{
debug_assert!(self.byte_at(self.host_start - 1) == b'@');
Some(self.slice(self.username_end + 1..self.host_start - 1))
} else {
None
}
}
/// Return the string representation of the host (domain or IP address) for this URL, if any. /// /// Non-ASCII domains are punycode-encoded per IDNA if this is the host /// of a special URL, or percent encoded for non-special URLs. /// IPv6 addresses are given between `[` and `]` brackets. /// /// Cannot-be-a-base URLs (typical of `data:` and `mailto:`) and some `file:` URLs /// don’t have a host. /// /// See also the `host` method. /// /// # Examples /// /// ``` /// use url::Url; /// # use url::ParseError; /// /// # fn run() -> Result<(), ParseError> { /// let url = Url::parse("https://127.0.0.1/index.html")?; /// assert_eq!(url.host_str(), Some("127.0.0.1")); /// /// let url = Url::parse("ftp://rms@example.com")?; /// assert_eq!(url.host_str(), Some("example.com")); /// /// let url = Url::parse("unix:/run/foo.socket")?; /// assert_eq!(url.host_str(), None); /// /// let url = Url::parse("data:text/plain,Stuff")?; /// assert_eq!(url.host_str(), None); /// # Ok(()) /// # } /// # run().unwrap(); /// ``` pubfn host_str(&self) -> Option<&str> { ifself.has_host() {
Some(self.slice(self.host_start..self.host_end))
} else {
None
}
}
/// Return the parsed representation of the host for this URL. /// Non-ASCII domain labels are punycode-encoded per IDNA if this is the host /// of a special URL, or percent encoded for non-special URLs. /// /// Cannot-be-a-base URLs (typical of `data:` and `mailto:`) and some `file:` URLs /// don’t have a host. /// /// See also the `host_str` method. /// /// # Examples /// /// ``` /// use url::Url; /// # use url::ParseError; /// /// # fn run() -> Result<(), ParseError> { /// let url = Url::parse("https://127.0.0.1/index.html")?; /// assert!(url.host().is_some()); /// /// let url = Url::parse("ftp://rms@example.com")?; /// assert!(url.host().is_some()); /// /// let url = Url::parse("unix:/run/foo.socket")?; /// assert!(url.host().is_none()); /// /// let url = Url::parse("data:text/plain,Stuff")?; /// assert!(url.host().is_none()); /// # Ok(()) /// # } /// # run().unwrap(); /// ``` pubfn host(&self) -> Option<Host<&str>> { matchself.host {
HostInternal::None => None,
HostInternal::Domain => Some(Host::Domain(self.slice(self.host_start..self.host_end))),
HostInternal::Ipv4(address) => Some(Host::Ipv4(address)),
HostInternal::Ipv6(address) => Some(Host::Ipv6(address)),
}
}
/// If this URL has a host and it is a domain name (not an IP address), return it. /// Non-ASCII domains are punycode-encoded per IDNA if this is the host /// of a special URL, or percent encoded for non-special URLs. /// /// # Examples /// /// ``` /// use url::Url; /// # use url::ParseError; /// /// # fn run() -> Result<(), ParseError> { /// let url = Url::parse("https://127.0.0.1/")?; /// assert_eq!(url.domain(), None); /// /// let url = Url::parse("mailto:rms@example.net")?; /// assert_eq!(url.domain(), None); /// /// let url = Url::parse("https://example.com/")?; /// assert_eq!(url.domain(), Some("example.com")); /// # Ok(()) /// # } /// # run().unwrap(); /// ``` pubfn domain(&self) -> Option<&str> { matchself.host {
HostInternal::Domain => Some(self.slice(self.host_start..self.host_end)),
_ => None,
}
}
/// Return the port number for this URL, if any. /// /// Note that default port numbers are never reflected by the serialization, /// use the `port_or_known_default()` method if you want a default port number returned. /// /// # Examples /// /// ``` /// use url::Url; /// # use url::ParseError; /// /// # fn run() -> Result<(), ParseError> { /// let url = Url::parse("https://example.com")?; /// assert_eq!(url.port(), None); /// /// let url = Url::parse("https://example.com:443/")?; /// assert_eq!(url.port(), None); /// /// let url = Url::parse("ssh://example.com:22")?; /// assert_eq!(url.port(), Some(22)); /// # Ok(()) /// # } /// # run().unwrap(); /// ``` #[inline] pubfn port(&self) -> Option<u16> { self.port
}
/// Return the port number for this URL, or the default port number if it is known. /// /// This method only knows the default port number /// of the `http`, `https`, `ws`, `wss` and `ftp` schemes. /// /// For URLs in these schemes, this method always returns `Some(_)`. /// For other schemes, it is the same as `Url::port()`. /// /// # Examples /// /// ``` /// use url::Url; /// # use url::ParseError; /// /// # fn run() -> Result<(), ParseError> { /// let url = Url::parse("foo://example.com")?; /// assert_eq!(url.port_or_known_default(), None); /// /// let url = Url::parse("foo://example.com:1456")?; /// assert_eq!(url.port_or_known_default(), Some(1456)); /// /// let url = Url::parse("https://example.com")?; /// assert_eq!(url.port_or_known_default(), Some(443)); /// # Ok(()) /// # } /// # run().unwrap(); /// ``` #[inline] pubfn port_or_known_default(&self) -> Option<u16> { self.port.or_else(|| parser::default_port(self.scheme()))
}
/// Resolve a URL’s host and port number to `SocketAddr`. /// /// If the URL has the default port number of a scheme that is unknown to this library, /// `default_port_number` provides an opportunity to provide the actual port number. /// In non-example code this should be implemented either simply as `|| None`, /// or by matching on the URL’s `.scheme()`. /// /// If the host is a domain, it is resolved using the standard library’s DNS support. /// /// # Examples /// /// ```no_run /// let url = url::Url::parse("https://example.net/").unwrap(); /// let addrs = url.socket_addrs(|| None).unwrap(); /// std::net::TcpStream::connect(&*addrs) /// # ; /// ``` /// /// ``` /// /// With application-specific known default port numbers /// fn socket_addrs(url: url::Url) -> std::io::Result<Vec<std::net::SocketAddr>> { /// url.socket_addrs(|| match url.scheme() { /// "socks5" | "socks5h" => Some(1080), /// _ => None, /// }) /// } /// ``` #[cfg(feature = "std")] #[cfg(any(
unix,
windows,
target_os = "redox",
target_os = "wasi",
target_os = "hermit"
))] pubfn socket_addrs(
&self,
default_port_number: implFn() -> Option<u16>,
) -> io::Result<alloc::vec::Vec<SocketAddr>> { // Note: trying to avoid the Vec allocation by returning `impl AsRef<[SocketAddr]>` // causes borrowck issues because the return value borrows `default_port_number`: // // https://github.com/rust-lang/rfcs/blob/master/text/1951-expand-impl-trait.md#scoping-for-type-and-lifetime-parameters // // > This RFC proposes that *all* type parameters are considered in scope // > for `impl Trait` in return position
let host = io_result(self.host(), "No host name in the URL")?; let port = io_result( self.port_or_known_default().or_else(default_port_number), "No port number in the URL",
)?;
Ok(match host {
Host::Domain(domain) => (domain, port).to_socket_addrs()?.collect(),
Host::Ipv4(ip) => vec![(ip, port).into()],
Host::Ipv6(ip) => vec![(ip, port).into()],
})
}
/// Return the path for this URL, as a percent-encoded ASCII string. /// For cannot-be-a-base URLs, this is an arbitrary string that doesn’t start with '/'. /// For other URLs, this starts with a '/' slash /// and continues with slash-separated path segments. /// /// # Examples /// /// ```rust /// use url::{Url, ParseError}; /// /// # fn run() -> Result<(), ParseError> { /// let url = Url::parse("https://example.com/api/versions?page=2")?; /// assert_eq!(url.path(), "/api/versions"); /// /// let url = Url::parse("https://example.com")?; /// assert_eq!(url.path(), "/"); /// /// let url = Url::parse("https://example.com/countries/việt nam")?; /// assert_eq!(url.path(), "/countries/vi%E1%BB%87t%20nam"); /// # Ok(()) /// # } /// # run().unwrap(); /// ``` pubfn path(&self) -> &str { match (self.query_start, self.fragment_start) {
(None, None) => self.slice(self.path_start..),
(Some(next_component_start), _) | (None, Some(next_component_start)) => { self.slice(self.path_start..next_component_start)
}
}
}
/// Unless this URL is cannot-be-a-base, /// return an iterator of '/' slash-separated path segments, /// each as a percent-encoded ASCII string. /// /// Return `None` for cannot-be-a-base URLs. /// /// When `Some` is returned, the iterator always contains at least one string /// (which may be empty). /// /// # Examples /// /// ``` /// use url::Url; /// /// # #[cfg(feature = "std")] /// # use std::error::Error; /// # #[cfg(not(feature = "std"))] /// # use core::error::Error; /// /// # fn run() -> Result<(), Box<dyn Error>> { /// let url = Url::parse("https://example.com/foo/bar")?; /// let mut path_segments = url.path_segments().ok_or_else(|| "cannot be base")?; /// assert_eq!(path_segments.next(), Some("foo")); /// assert_eq!(path_segments.next(), Some("bar")); /// assert_eq!(path_segments.next(), None); /// /// let url = Url::parse("https://example.com")?; /// let mut path_segments = url.path_segments().ok_or_else(|| "cannot be base")?; /// assert_eq!(path_segments.next(), Some("")); /// assert_eq!(path_segments.next(), None); /// /// let url = Url::parse("data:text/plain,HelloWorld")?; /// assert!(url.path_segments().is_none()); /// /// let url = Url::parse("https://example.com/countries/việt nam")?; /// let mut path_segments = url.path_segments().ok_or_else(|| "cannot be base")?; /// assert_eq!(path_segments.next(), Some("countries")); /// assert_eq!(path_segments.next(), Some("vi%E1%BB%87t%20nam")); /// # Ok(()) /// # } /// # run().unwrap(); /// ``` pubfn path_segments(&self) -> Option<str::Split<'_, char>> { let path = self.path();
path.strip_prefix('/').map(|remainder| remainder.split('/'))
}
/// Return this URL’s query string, if any, as a percent-encoded ASCII string. /// /// # Examples /// /// ```rust /// use url::Url; /// # use url::ParseError; /// /// fn run() -> Result<(), ParseError> { /// let url = Url::parse("https://example.com/products?page=2")?; /// let query = url.query(); /// assert_eq!(query, Some("page=2")); /// /// let url = Url::parse("https://example.com/products")?; /// let query = url.query(); /// assert!(query.is_none()); /// /// let url = Url::parse("https://example.com/?country=español")?; /// let query = url.query(); /// assert_eq!(query, Some("country=espa%C3%B1ol")); /// # Ok(()) /// # } /// # run().unwrap(); /// ``` pubfn query(&self) -> Option<&str> { match (self.query_start, self.fragment_start) {
(None, _) => None,
(Some(query_start), None) => {
debug_assert!(self.byte_at(query_start) == b'?');
Some(self.slice(query_start + 1..))
}
(Some(query_start), Some(fragment_start)) => {
debug_assert!(self.byte_at(query_start) == b'?');
Some(self.slice(query_start + 1..fragment_start))
}
}
}
/// Parse the URL’s query string, if any, as `application/x-www-form-urlencoded` /// and return an iterator of (key, value) pairs. /// /// # Examples /// /// ```rust /// use std::borrow::Cow; /// /// use url::Url; /// # use url::ParseError; /// /// # fn run() -> Result<(), ParseError> { /// let url = Url::parse("https://example.com/products?page=2&sort=desc")?; /// let mut pairs = url.query_pairs(); /// /// assert_eq!(pairs.count(), 2); /// /// assert_eq!(pairs.next(), Some((Cow::Borrowed("page"), Cow::Borrowed("2")))); /// assert_eq!(pairs.next(), Some((Cow::Borrowed("sort"), Cow::Borrowed("desc")))); /// # Ok(()) /// # } /// # run().unwrap(); /// ```
/// Return this URL’s fragment identifier, if any. /// /// A fragment is the part of the URL after the `#` symbol. /// The fragment is optional and, if present, contains a fragment identifier /// that identifies a secondary resource, such as a section heading /// of a document. /// /// In HTML, the fragment identifier is usually the id attribute of a an element /// that is scrolled to on load. Browsers typically will not send the fragment portion /// of a URL to the server. /// /// **Note:** the parser did *not* percent-encode this component, /// but the input may have been percent-encoded already. /// /// # Examples /// /// ```rust /// use url::Url; /// # use url::ParseError; /// /// # fn run() -> Result<(), ParseError> { /// let url = Url::parse("https://example.com/data.csv#row=4")?; /// /// assert_eq!(url.fragment(), Some("row=4")); /// /// let url = Url::parse("https://example.com/data.csv#cell=4,1-6,2")?; /// /// assert_eq!(url.fragment(), Some("cell=4,1-6,2")); /// # Ok(()) /// # } /// # run().unwrap(); /// ``` pubfn fragment(&self) -> Option<&str> { self.fragment_start.map(|start| {
debug_assert!(self.byte_at(start) == b'#'); self.slice(start + 1..)
})
}
fn mutate<F: FnOnce(&mut Parser<'_>) -> R, R>(&mut self, f: F) -> R { letmut parser = Parser::for_setter(mem::take(&mutself.serialization)); let result = f(&mut parser); self.serialization = parser.serialization;
result
}
/// Change this URL’s fragment identifier. /// /// # Examples /// /// ```rust /// use url::Url; /// # use url::ParseError; /// /// # fn run() -> Result<(), ParseError> { /// let mut url = Url::parse("https://example.com/data.csv")?; /// assert_eq!(url.as_str(), "https://example.com/data.csv");
self.serialization.push_str(&host_and_after);
} elseifself.byte_at(self.username_end) == b':' { // If there is a password to remove let has_username_or_password = self.byte_at(self.host_start - 1) == b'@';
debug_assert!(has_username_or_password); let username_start = self.scheme_end + 3; let empty_username = username_start == self.username_end; let start = self.username_end; // Remove the ':' let end = if empty_username { self.host_start // Remove the '@' as well
} else { self.host_start - 1// Keep the '@' to separate the username from the host
}; self.serialization.drain(start as usize..end as usize); let offset = end - start; self.host_start -= offset; self.host_end -= offset; self.path_start -= offset; iflet Some(refmut index) = self.query_start {
*index -= offset
} iflet Some(refmut index) = self.fragment_start {
*index -= offset
}
}
Ok(())
}
/// Change this URL’s username. /// /// If this URL is cannot-be-a-base or does not have a host, do nothing and return `Err`. /// # Examples /// /// Cannot setup username from mailto(cannot-be-base) /// /// ```rust /// use url::{Url, ParseError}; /// /// # fn run() -> Result<(), ParseError> { /// let mut url = Url::parse("mailto:rmz@example.com")?; /// let result = url.set_username("user1"); /// assert_eq!(url.as_str(), "mailto:rmz@example.com"); /// assert!(result.is_err()); /// # Ok(()) /// # } /// # run().unwrap(); /// ``` /// /// Setup username to user1 /// /// ```rust /// use url::{Url, ParseError}; /// /// # fn run() -> Result<(), ParseError> { /// let mut url = Url::parse("ftp://:secre1@example.com/")?; /// let result = url.set_username("user1"); /// assert!(result.is_ok()); /// assert_eq!(url.username(), "user1"); /// assert_eq!(url.as_str(), "ftp://user1:secre1@example.com/"); /// # Ok(()) /// # } /// # run().unwrap(); /// ``` #[allow(clippy::result_unit_err)] pubfn set_username(&mutself, username: &str) -> Result<(), ()> { // has_host implies !cannot_be_a_base if !self.has_host() || self.host() == Some(Host::Domain("")) || self.scheme() == "file" { return Err(());
} let username_start = self.scheme_end + 3;
debug_assert!(self.slice(self.scheme_end..username_start) == "://"); ifself.slice(username_start..self.username_end) == username { return Ok(());
} let after_username = self.slice(self.username_end..).to_owned(); self.serialization.truncate(username_start as usize); self.serialization
.extend(utf8_percent_encode(username, USERINFO));
/// Change this URL’s scheme. /// /// Do nothing and return `Err` under the following circumstances: /// /// * If the new scheme is not in `[a-zA-Z][a-zA-Z0-9+.-]+` /// * If this URL is cannot-be-a-base and the new scheme is one of /// `http`, `https`, `ws`, `wss` or `ftp` /// * If either the old or new scheme is `http`, `https`, `ws`, /// `wss` or `ftp` and the other is not one of these /// * If the new scheme is `file` and this URL includes credentials /// or has a non-null port /// * If this URL's scheme is `file` and its host is empty or null /// /// See also [the URL specification's section on legal scheme state /// overrides](https://url.spec.whatwg.org/#scheme-state). /// /// # Examples /// /// Change the URL’s scheme from `https` to `http`: /// /// ``` /// use url::Url; /// # use url::ParseError; /// /// # fn run() -> Result<(), ParseError> { /// let mut url = Url::parse("https://example.net")?; /// let result = url.set_scheme("http"); /// assert_eq!(url.as_str(), "http://example.net/"); /// assert!(result.is_ok()); /// # Ok(()) /// # } /// # run().unwrap(); /// ``` /// Change the URL’s scheme from `foo` to `bar`: /// /// ``` /// use url::Url; /// # use url::ParseError; /// /// # fn run() -> Result<(), ParseError> { /// let mut url = Url::parse("foo://example.net")?; /// let result = url.set_scheme("bar"); /// assert_eq!(url.as_str(), "bar://example.net"); /// assert!(result.is_ok()); /// # Ok(()) /// # } /// # run().unwrap(); /// ``` /// /// Cannot change URL’s scheme from `https` to `foõ`: /// /// ``` /// use url::Url; /// # use url::ParseError; /// /// # fn run() -> Result<(), ParseError> { /// let mut url = Url::parse("https://example.net")?; /// let result = url.set_scheme("foõ"); /// assert_eq!(url.as_str(), "https://example.net/"); /// assert!(result.is_err()); /// # Ok(()) /// # } /// # run().unwrap(); /// ``` /// /// Cannot change URL’s scheme from `mailto` (cannot-be-a-base) to `https`: /// /// ``` /// use url::Url; /// # use url::ParseError; /// /// # fn run() -> Result<(), ParseError> { /// let mut url = Url::parse("mailto:rms@example.net")?; /// let result = url.set_scheme("https"); /// assert_eq!(url.as_str(), "mailto:rms@example.net"); /// assert!(result.is_err()); /// # Ok(()) /// # } /// # run().unwrap(); /// ``` /// Cannot change the URL’s scheme from `foo` to `https`: /// /// ``` /// use url::Url; /// # use url::ParseError; /// /// # fn run() -> Result<(), ParseError> { /// let mut url = Url::parse("foo://example.net")?; /// let result = url.set_scheme("https"); /// assert_eq!(url.as_str(), "foo://example.net"); /// assert!(result.is_err()); /// # Ok(()) /// # } /// # run().unwrap(); /// ``` /// Cannot change the URL’s scheme from `http` to `foo`: /// /// ``` /// use url::Url; /// # use url::ParseError; /// /// # fn run() -> Result<(), ParseError> { /// let mut url = Url::parse("http://example.net")?; /// let result = url.set_scheme("foo"); /// assert_eq!(url.as_str(), "http://example.net/"); /// assert!(result.is_err()); /// # Ok(()) /// # } /// # run().unwrap(); /// ``` #[allow(clippy::result_unit_err, clippy::suspicious_operation_groupings)] pubfn set_scheme(&mutself, scheme: &str) -> Result<(), ()> { letmut parser = Parser::for_setter(String::new()); let remaining = parser.parse_scheme(parser::Input::new_no_trim(scheme))?; let new_scheme_type = SchemeType::from(&parser.serialization); let old_scheme_type = SchemeType::from(self.scheme()); // If url’s scheme is a special scheme and buffer is not a special scheme, then return. if (new_scheme_type.is_special() && !old_scheme_type.is_special()) || // If url’s scheme is not a special scheme and buffer is a special scheme, then return.
(!new_scheme_type.is_special() && old_scheme_type.is_special()) || // If url includes credentials or has a non-null port, and buffer is "file", then return. // If url’s scheme is "file" and its host is an empty host or null, then return.
(new_scheme_type.is_file() && self.has_authority())
{ return Err(());
}
if !remaining.is_empty() || (!self.has_host() && new_scheme_type.is_special()) { return Err(());
} let old_scheme_end = self.scheme_end; let new_scheme_end = to_u32(parser.serialization.len()).unwrap(); let adjust = |index: &mut u32| {
*index -= old_scheme_end;
*index += new_scheme_end;
};
// Update the port so it can be removed // If it is the scheme's default // we don't mind it silently failing // if there was no port in the first place let previous_port = self.port(); let _ = self.set_port(previous_port);
Ok(())
}
/// Convert a file name as `std::path::Path` into an URL in the `file` scheme. /// /// This returns `Err` if the given path is not absolute or, /// on Windows, if the prefix is not a disk prefix (e.g. `C:`) or a UNC prefix (`\\`). /// /// # Examples /// /// On Unix-like platforms: /// /// ``` /// # if cfg!(unix) { /// use url::Url; /// /// # fn run() -> Result<(), ()> { /// let url = Url::from_file_path("/tmp/foo.txt")?; /// assert_eq!(url.as_str(), "file:///tmp/foo.txt"); /// /// let url = Url::from_file_path("../foo.txt"); /// assert!(url.is_err()); /// /// let url = Url::from_file_path("https://google.com/"); /// assert!(url.is_err()); /// # Ok(()) /// # } /// # run().unwrap(); /// # } /// ``` /// /// This method is only available if the `std` Cargo feature is enabled. #[cfg(all(
feature = "std",
any(
unix,
windows,
target_os = "redox",
target_os = "wasi",
target_os = "hermit"
)
))] #[allow(clippy::result_unit_err)] pubfn from_file_path<P: AsRef<std::path::Path>>(path: P) -> Result<Url, ()> { letmut serialization = "file://".to_owned(); let host_start = serialization.len() as u32; let (host_end, host) = path_to_file_url_segments(path.as_ref(), &mut serialization)?;
Ok(Url {
serialization,
scheme_end: "file".len() as u32,
username_end: host_start,
host_start,
host_end,
host,
port: None,
path_start: host_end,
query_start: None,
fragment_start: None,
})
}
/// Convert a directory name as `std::path::Path` into an URL in the `file` scheme. /// /// This returns `Err` if the given path is not absolute or, /// on Windows, if the prefix is not a disk prefix (e.g. `C:`) or a UNC prefix (`\\`). /// /// Compared to `from_file_path`, this ensure that URL’s the path has a trailing slash /// so that the entire path is considered when using this URL as a base URL. /// /// For example: /// /// * `"index.html"` parsed with `Url::from_directory_path(Path::new("/var/www"))` /// as the base URL is `file:///var/www/index.html` /// * `"index.html"` parsed with `Url::from_file_path(Path::new("/var/www"))` /// as the base URL is `file:///var/index.html`, which might not be what was intended. /// /// Note that `std::path` does not consider trailing slashes significant /// and usually does not include them (e.g. in `Path::parent()`). /// /// This method is only available if the `std` Cargo feature is enabled. #[cfg(all(
feature = "std",
any(
unix,
windows,
target_os = "redox",
target_os = "wasi",
target_os = "hermit"
)
))] #[allow(clippy::result_unit_err)] pubfn from_directory_path<P: AsRef<std::path::Path>>(path: P) -> Result<Url, ()> { letmut url = Url::from_file_path(path)?; if !url.serialization.ends_with('/') {
url.serialization.push('/')
}
Ok(url)
}
/// Serialize with Serde using the internal representation of the `Url` struct. /// /// The corresponding `deserialize_internal` method sacrifices some invariant-checking /// for speed, compared to the `Deserialize` trait impl. /// /// This method is only available if the `serde` Cargo feature is enabled. #[cfg(feature = "serde")] #[deny(unused)] pubfn serialize_internal<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where
S: serde::Serializer,
{ use serde::Serialize; // Destructuring first lets us ensure that adding or removing fields forces this method // to be updated let Url { ref serialization, ref scheme_end, ref username_end, ref host_start, ref host_end, ref host, ref port, ref path_start, ref query_start, ref fragment_start,
} = *self;
(
serialization,
scheme_end,
username_end,
host_start,
host_end,
host,
port,
path_start,
query_start,
fragment_start,
)
.serialize(serializer)
}
/// Serialize with Serde using the internal representation of the `Url` struct. /// /// The corresponding `deserialize_internal` method sacrifices some invariant-checking /// for speed, compared to the `Deserialize` trait impl. /// /// This method is only available if the `serde` Cargo feature is enabled. #[cfg(feature = "serde")] #[deny(unused)] pubfn deserialize_internal<'de, D>(deserializer: D) -> Result<Self, D::Error> where
D: serde::Deserializer<'de>,
{ use serde::de::{Deserialize, Error}; let (
serialization,
scheme_end,
username_end,
host_start,
host_end,
host,
port,
path_start,
query_start,
fragment_start,
) = Deserialize::deserialize(deserializer)?; let url = Url {
serialization,
scheme_end,
username_end,
host_start,
host_end,
host,
port,
path_start,
query_start,
fragment_start,
}; if cfg!(debug_assertions) {
url.check_invariants()
.map_err(|reason| Error::custom(reason))?
}
Ok(url)
}
/// Assuming the URL is in the `file` scheme or similar, /// convert its path to an absolute `std::path::Path`. /// /// **Note:** This does not actually check the URL’s `scheme`, /// and may give nonsensical results for other schemes. /// It is the user’s responsibility to check the URL’s scheme before calling this. /// /// ``` /// # use url::Url; /// # let url = Url::parse("file:///etc/passwd").unwrap(); /// let path = url.to_file_path(); /// ``` /// /// Returns `Err` if the host is neither empty nor `"localhost"` (except on Windows, where /// `file:` URLs may have a non-local host), /// or if `Path::new_opt()` returns `None`. /// (That is, if the percent-decoded path contains a NUL byte or, /// for a Windows path, is not UTF-8.) /// /// This method is only available if the `std` Cargo feature is enabled. #[inline] #[cfg(all(
feature = "std",
any(
unix,
windows,
target_os = "redox",
target_os = "wasi",
target_os = "hermit"
)
))] #[allow(clippy::result_unit_err)] pubfn to_file_path(&self) -> Result<PathBuf, ()> { iflet Some(segments) = self.path_segments() { let host = matchself.host() {
None | Some(Host::Domain("localhost")) => None,
Some(_) if cfg!(windows) && self.scheme() == "file" => {
Some(&self.serialization[self.host_start as usize..self.host_end as usize])
}
_ => return Err(()),
};
impl RangeArg for Range<u32> { #[inline] fn slice_of<'a>(&self, s: &'a str) -> &'a str {
&s[self.start as usize..self.end as usize]
}
}
impl RangeArg for RangeFrom<u32> { #[inline] fn slice_of<'a>(&self, s: &'a str) -> &'a str {
&s[self.start as usize..]
}
}
impl RangeArg for RangeTo<u32> { #[inline] fn slice_of<'a>(&self, s: &'a str) -> &'a str {
&s[..self.end as usize]
}
}
/// Serializes this URL into a `serde` stream. /// /// This implementation is only available if the `serde` Cargo feature is enabled. #[cfg(feature = "serde")] impl serde::Serialize for Url { fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where
S: serde::Serializer,
{
serializer.serialize_str(self.as_str())
}
}
/// Deserializes this URL from a `serde` stream. /// /// This implementation is only available if the `serde` Cargo feature is enabled. #[cfg(feature = "serde")] impl<'de> serde::Deserialize<'de> for Url { fn deserialize<D>(deserializer: D) -> Result<Url, D::Error> where
D: serde::Deserializer<'de>,
{ use serde::de::{Error, Visitor};
struct UrlVisitor;
impl<'de> Visitor<'de> for UrlVisitor { type Value = Url;
// A windows drive letter must end with a slash. if serialization.len() > host_start
&& parser::is_windows_drive_letter(&serialization[host_start..])
&& path_only_has_prefix
{
serialization.push('/');
}
Ok((host_end, host_internal))
}
#[cfg(all(
feature = "std",
any(unix, target_os = "redox", target_os = "wasi", target_os = "hermit")
))] fn file_url_segments_to_pathbuf(
host: Option<&str>,
segments: str::Split<'_, char>,
) -> Result<PathBuf, ()> { use alloc::vec::Vec; use percent_encoding::percent_decode; #[cfg(not(target_os = "wasi"))] use std::ffi::OsStr; #[cfg(target_os = "hermit")] use std::os::hermit::ffi::OsStrExt; #[cfg(any(unix, target_os = "redox"))] use std::os::unix::prelude::OsStrExt; use std::path::PathBuf;
for segment in segments {
bytes.push(b'/');
bytes.extend(percent_decode(segment.as_bytes()));
}
// A windows drive letter must end with a slash. if bytes.len() > 2
&& bytes[bytes.len() - 2].is_ascii_alphabetic()
&& matches!(bytes[bytes.len() - 1], b':' | b'|')
{
bytes.push(b'/');
}
#[cfg(not(target_os = "wasi"))] let path = PathBuf::from(OsStr::from_bytes(&bytes)); #[cfg(target_os = "wasi")] let path = String::from_utf8(bytes)
.map(|path| PathBuf::from(path))
.map_err(|_| ())?;
debug_assert!(
path.is_absolute(), "to_file_path() failed to produce an absolute Path"
);
// Currently non-unicode windows paths cannot be represented match String::from_utf8(percent_decode(segment.as_bytes()).collect()) {
Ok(s) => string.push_str(&s),
Err(..) => return Err(()),
}
} let path = PathBuf::from(string);
debug_assert!(
path.is_absolute(), "to_file_path() failed to produce an absolute Path"
);
Ok(path)
}
/// Implementation detail of `Url::query_pairs_mut`. Typically not used directly. #[derive(Debug)] pubstruct UrlQuery<'a> {
url: Option<&'a mut Url>,
fragment: Option<String>,
}
// `as_mut_string` string here exposes the internal serialization of an `Url`, // which should not be exposed to users. // We achieve that by not giving users direct access to `UrlQuery`: // * Its fields are private // (and so can not be constructed with struct literal syntax outside of this crate), // * It has no constructor // * It is only visible (on the type level) to users in the return type of // `Url::query_pairs_mut` which is `Serializer<UrlQuery>` // * `Serializer` keeps its target in a private field // * Unlike in other `Target` impls, `UrlQuery::finished` does not return `Self`. impl<'a> form_urlencoded::Target for UrlQuery<'a> { fn as_mut_string(&mutself) -> &mut String {
&mutself.url.as_mut().unwrap().serialization
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.