RPM build fix (reverted CI changes which will need to be un-reverted or made conditional) and vendor Rust dependencies to make builds much faster in any CI system.
This commit is contained in:
480
zeroidc/vendor/url/src/host.rs
vendored
Normal file
480
zeroidc/vendor/url/src/host.rs
vendored
Normal file
@@ -0,0 +1,480 @@
|
||||
// Copyright 2013-2016 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.
|
||||
|
||||
use std::cmp;
|
||||
use std::fmt::{self, Formatter};
|
||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
|
||||
use percent_encoding::{percent_decode, utf8_percent_encode, CONTROLS};
|
||||
#[cfg(feature = "serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::parser::{ParseError, ParseResult};
|
||||
|
||||
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub(crate) enum HostInternal {
|
||||
None,
|
||||
Domain,
|
||||
Ipv4(Ipv4Addr),
|
||||
Ipv6(Ipv6Addr),
|
||||
}
|
||||
|
||||
impl From<Host<String>> for HostInternal {
|
||||
fn from(host: Host<String>) -> HostInternal {
|
||||
match host {
|
||||
Host::Domain(ref s) if s.is_empty() => HostInternal::None,
|
||||
Host::Domain(_) => HostInternal::Domain,
|
||||
Host::Ipv4(address) => HostInternal::Ipv4(address),
|
||||
Host::Ipv6(address) => HostInternal::Ipv6(address),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The host name of an URL.
|
||||
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
|
||||
#[derive(Clone, Debug, Eq, Ord, PartialOrd, Hash)]
|
||||
pub enum Host<S = String> {
|
||||
/// A DNS domain name, as '.' dot-separated labels.
|
||||
/// Non-ASCII labels are encoded in punycode per IDNA if this is the host of
|
||||
/// a special URL, or percent encoded for non-special URLs. Hosts for
|
||||
/// non-special URLs are also called opaque hosts.
|
||||
Domain(S),
|
||||
|
||||
/// An IPv4 address.
|
||||
/// `Url::host_str` returns the serialization of this address,
|
||||
/// as four decimal integers separated by `.` dots.
|
||||
Ipv4(Ipv4Addr),
|
||||
|
||||
/// An IPv6 address.
|
||||
/// `Url::host_str` returns the serialization of that address between `[` and `]` brackets,
|
||||
/// in the format per [RFC 5952 *A Recommendation
|
||||
/// for IPv6 Address Text Representation*](https://tools.ietf.org/html/rfc5952):
|
||||
/// lowercase hexadecimal with maximal `::` compression.
|
||||
Ipv6(Ipv6Addr),
|
||||
}
|
||||
|
||||
impl<'a> Host<&'a str> {
|
||||
/// Return a copy of `self` that owns an allocated `String` but does not borrow an `&Url`.
|
||||
pub fn to_owned(&self) -> Host<String> {
|
||||
match *self {
|
||||
Host::Domain(domain) => Host::Domain(domain.to_owned()),
|
||||
Host::Ipv4(address) => Host::Ipv4(address),
|
||||
Host::Ipv6(address) => Host::Ipv6(address),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Host<String> {
|
||||
/// Parse a host: either an IPv6 address in [] square brackets, or a domain.
|
||||
///
|
||||
/// <https://url.spec.whatwg.org/#host-parsing>
|
||||
pub fn parse(input: &str) -> Result<Self, ParseError> {
|
||||
if input.starts_with('[') {
|
||||
if !input.ends_with(']') {
|
||||
return Err(ParseError::InvalidIpv6Address);
|
||||
}
|
||||
return parse_ipv6addr(&input[1..input.len() - 1]).map(Host::Ipv6);
|
||||
}
|
||||
let domain = percent_decode(input.as_bytes()).decode_utf8_lossy();
|
||||
let domain = idna::domain_to_ascii(&domain)?;
|
||||
if domain.is_empty() {
|
||||
return Err(ParseError::EmptyHost);
|
||||
}
|
||||
|
||||
let is_invalid_domain_char = |c| {
|
||||
matches!(
|
||||
c,
|
||||
'\0' | '\t'
|
||||
| '\n'
|
||||
| '\r'
|
||||
| ' '
|
||||
| '#'
|
||||
| '%'
|
||||
| '/'
|
||||
| ':'
|
||||
| '<'
|
||||
| '>'
|
||||
| '?'
|
||||
| '@'
|
||||
| '['
|
||||
| '\\'
|
||||
| ']'
|
||||
| '^'
|
||||
)
|
||||
};
|
||||
|
||||
if domain.find(is_invalid_domain_char).is_some() {
|
||||
Err(ParseError::InvalidDomainCharacter)
|
||||
} else if let Some(address) = parse_ipv4addr(&domain)? {
|
||||
Ok(Host::Ipv4(address))
|
||||
} else {
|
||||
Ok(Host::Domain(domain))
|
||||
}
|
||||
}
|
||||
|
||||
// <https://url.spec.whatwg.org/#concept-opaque-host-parser>
|
||||
pub fn parse_opaque(input: &str) -> Result<Self, ParseError> {
|
||||
if input.starts_with('[') {
|
||||
if !input.ends_with(']') {
|
||||
return Err(ParseError::InvalidIpv6Address);
|
||||
}
|
||||
return parse_ipv6addr(&input[1..input.len() - 1]).map(Host::Ipv6);
|
||||
}
|
||||
|
||||
let is_invalid_host_char = |c| {
|
||||
matches!(
|
||||
c,
|
||||
'\0' | '\t'
|
||||
| '\n'
|
||||
| '\r'
|
||||
| ' '
|
||||
| '#'
|
||||
| '/'
|
||||
| ':'
|
||||
| '<'
|
||||
| '>'
|
||||
| '?'
|
||||
| '@'
|
||||
| '['
|
||||
| '\\'
|
||||
| ']'
|
||||
| '^'
|
||||
)
|
||||
};
|
||||
|
||||
if input.find(is_invalid_host_char).is_some() {
|
||||
Err(ParseError::InvalidDomainCharacter)
|
||||
} else {
|
||||
Ok(Host::Domain(
|
||||
utf8_percent_encode(input, CONTROLS).to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: AsRef<str>> fmt::Display for Host<S> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
Host::Domain(ref domain) => domain.as_ref().fmt(f),
|
||||
Host::Ipv4(ref addr) => addr.fmt(f),
|
||||
Host::Ipv6(ref addr) => {
|
||||
f.write_str("[")?;
|
||||
write_ipv6(addr, f)?;
|
||||
f.write_str("]")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, T> PartialEq<Host<T>> for Host<S>
|
||||
where
|
||||
S: PartialEq<T>,
|
||||
{
|
||||
fn eq(&self, other: &Host<T>) -> bool {
|
||||
match (self, other) {
|
||||
(Host::Domain(a), Host::Domain(b)) => a == b,
|
||||
(Host::Ipv4(a), Host::Ipv4(b)) => a == b,
|
||||
(Host::Ipv6(a), Host::Ipv6(b)) => a == b,
|
||||
(_, _) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write_ipv6(addr: &Ipv6Addr, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
let segments = addr.segments();
|
||||
let (compress_start, compress_end) = longest_zero_sequence(&segments);
|
||||
let mut i = 0;
|
||||
while i < 8 {
|
||||
if i == compress_start {
|
||||
f.write_str(":")?;
|
||||
if i == 0 {
|
||||
f.write_str(":")?;
|
||||
}
|
||||
if compress_end < 8 {
|
||||
i = compress_end;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
write!(f, "{:x}", segments[i as usize])?;
|
||||
if i < 7 {
|
||||
f.write_str(":")?;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// https://url.spec.whatwg.org/#concept-ipv6-serializer step 2 and 3
|
||||
fn longest_zero_sequence(pieces: &[u16; 8]) -> (isize, isize) {
|
||||
let mut longest = -1;
|
||||
let mut longest_length = -1;
|
||||
let mut start = -1;
|
||||
macro_rules! finish_sequence(
|
||||
($end: expr) => {
|
||||
if start >= 0 {
|
||||
let length = $end - start;
|
||||
if length > longest_length {
|
||||
longest = start;
|
||||
longest_length = length;
|
||||
}
|
||||
}
|
||||
};
|
||||
);
|
||||
for i in 0..8 {
|
||||
if pieces[i as usize] == 0 {
|
||||
if start < 0 {
|
||||
start = i;
|
||||
}
|
||||
} else {
|
||||
finish_sequence!(i);
|
||||
start = -1;
|
||||
}
|
||||
}
|
||||
finish_sequence!(8);
|
||||
// https://url.spec.whatwg.org/#concept-ipv6-serializer
|
||||
// step 3: ignore lone zeroes
|
||||
if longest_length < 2 {
|
||||
(-1, -2)
|
||||
} else {
|
||||
(longest, longest + longest_length)
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://url.spec.whatwg.org/#ipv4-number-parser>
|
||||
fn parse_ipv4number(mut input: &str) -> Result<Option<u32>, ()> {
|
||||
let mut r = 10;
|
||||
if input.starts_with("0x") || input.starts_with("0X") {
|
||||
input = &input[2..];
|
||||
r = 16;
|
||||
} else if input.len() >= 2 && input.starts_with('0') {
|
||||
input = &input[1..];
|
||||
r = 8;
|
||||
}
|
||||
|
||||
// At the moment we can't know the reason why from_str_radix fails
|
||||
// https://github.com/rust-lang/rust/issues/22639
|
||||
// So instead we check if the input looks like a real number and only return
|
||||
// an error when it's an overflow.
|
||||
let valid_number = match r {
|
||||
8 => input.chars().all(|c| ('0'..='7').contains(&c)),
|
||||
10 => input.chars().all(|c| ('0'..='9').contains(&c)),
|
||||
16 => input.chars().all(|c| {
|
||||
('0'..='9').contains(&c) || ('a'..='f').contains(&c) || ('A'..='F').contains(&c)
|
||||
}),
|
||||
_ => false,
|
||||
};
|
||||
|
||||
if !valid_number {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
if input.is_empty() {
|
||||
return Ok(Some(0));
|
||||
}
|
||||
if input.starts_with('+') {
|
||||
return Ok(None);
|
||||
}
|
||||
match u32::from_str_radix(input, r) {
|
||||
Ok(number) => Ok(Some(number)),
|
||||
Err(_) => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://url.spec.whatwg.org/#concept-ipv4-parser>
|
||||
fn parse_ipv4addr(input: &str) -> ParseResult<Option<Ipv4Addr>> {
|
||||
if input.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
let mut parts: Vec<&str> = input.split('.').collect();
|
||||
if parts.last() == Some(&"") {
|
||||
parts.pop();
|
||||
}
|
||||
if parts.len() > 4 {
|
||||
return Ok(None);
|
||||
}
|
||||
let mut numbers: Vec<u32> = Vec::new();
|
||||
let mut overflow = false;
|
||||
for part in parts {
|
||||
if part.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
match parse_ipv4number(part) {
|
||||
Ok(Some(n)) => numbers.push(n),
|
||||
Ok(None) => return Ok(None),
|
||||
Err(()) => overflow = true,
|
||||
};
|
||||
}
|
||||
if overflow {
|
||||
return Err(ParseError::InvalidIpv4Address);
|
||||
}
|
||||
let mut ipv4 = numbers.pop().expect("a non-empty list of numbers");
|
||||
// Equivalent to: ipv4 >= 256 ** (4 − numbers.len())
|
||||
if ipv4 > u32::max_value() >> (8 * numbers.len() as u32) {
|
||||
return Err(ParseError::InvalidIpv4Address);
|
||||
}
|
||||
if numbers.iter().any(|x| *x > 255) {
|
||||
return Err(ParseError::InvalidIpv4Address);
|
||||
}
|
||||
for (counter, n) in numbers.iter().enumerate() {
|
||||
ipv4 += n << (8 * (3 - counter as u32))
|
||||
}
|
||||
Ok(Some(Ipv4Addr::from(ipv4)))
|
||||
}
|
||||
|
||||
/// <https://url.spec.whatwg.org/#concept-ipv6-parser>
|
||||
fn parse_ipv6addr(input: &str) -> ParseResult<Ipv6Addr> {
|
||||
let input = input.as_bytes();
|
||||
let len = input.len();
|
||||
let mut is_ip_v4 = false;
|
||||
let mut pieces = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
let mut piece_pointer = 0;
|
||||
let mut compress_pointer = None;
|
||||
let mut i = 0;
|
||||
|
||||
if len < 2 {
|
||||
return Err(ParseError::InvalidIpv6Address);
|
||||
}
|
||||
|
||||
if input[0] == b':' {
|
||||
if input[1] != b':' {
|
||||
return Err(ParseError::InvalidIpv6Address);
|
||||
}
|
||||
i = 2;
|
||||
piece_pointer = 1;
|
||||
compress_pointer = Some(1);
|
||||
}
|
||||
|
||||
while i < len {
|
||||
if piece_pointer == 8 {
|
||||
return Err(ParseError::InvalidIpv6Address);
|
||||
}
|
||||
if input[i] == b':' {
|
||||
if compress_pointer.is_some() {
|
||||
return Err(ParseError::InvalidIpv6Address);
|
||||
}
|
||||
i += 1;
|
||||
piece_pointer += 1;
|
||||
compress_pointer = Some(piece_pointer);
|
||||
continue;
|
||||
}
|
||||
let start = i;
|
||||
let end = cmp::min(len, start + 4);
|
||||
let mut value = 0u16;
|
||||
while i < end {
|
||||
match (input[i] as char).to_digit(16) {
|
||||
Some(digit) => {
|
||||
value = value * 0x10 + digit as u16;
|
||||
i += 1;
|
||||
}
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
if i < len {
|
||||
match input[i] {
|
||||
b'.' => {
|
||||
if i == start {
|
||||
return Err(ParseError::InvalidIpv6Address);
|
||||
}
|
||||
i = start;
|
||||
if piece_pointer > 6 {
|
||||
return Err(ParseError::InvalidIpv6Address);
|
||||
}
|
||||
is_ip_v4 = true;
|
||||
}
|
||||
b':' => {
|
||||
i += 1;
|
||||
if i == len {
|
||||
return Err(ParseError::InvalidIpv6Address);
|
||||
}
|
||||
}
|
||||
_ => return Err(ParseError::InvalidIpv6Address),
|
||||
}
|
||||
}
|
||||
if is_ip_v4 {
|
||||
break;
|
||||
}
|
||||
pieces[piece_pointer] = value;
|
||||
piece_pointer += 1;
|
||||
}
|
||||
|
||||
if is_ip_v4 {
|
||||
if piece_pointer > 6 {
|
||||
return Err(ParseError::InvalidIpv6Address);
|
||||
}
|
||||
let mut numbers_seen = 0;
|
||||
while i < len {
|
||||
if numbers_seen > 0 {
|
||||
if numbers_seen < 4 && (i < len && input[i] == b'.') {
|
||||
i += 1
|
||||
} else {
|
||||
return Err(ParseError::InvalidIpv6Address);
|
||||
}
|
||||
}
|
||||
|
||||
let mut ipv4_piece = None;
|
||||
while i < len {
|
||||
let digit = match input[i] {
|
||||
c @ b'0'..=b'9' => c - b'0',
|
||||
_ => break,
|
||||
};
|
||||
match ipv4_piece {
|
||||
None => ipv4_piece = Some(digit as u16),
|
||||
Some(0) => return Err(ParseError::InvalidIpv6Address), // No leading zero
|
||||
Some(ref mut v) => {
|
||||
*v = *v * 10 + digit as u16;
|
||||
if *v > 255 {
|
||||
return Err(ParseError::InvalidIpv6Address);
|
||||
}
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
pieces[piece_pointer] = if let Some(v) = ipv4_piece {
|
||||
pieces[piece_pointer] * 0x100 + v
|
||||
} else {
|
||||
return Err(ParseError::InvalidIpv6Address);
|
||||
};
|
||||
numbers_seen += 1;
|
||||
|
||||
if numbers_seen == 2 || numbers_seen == 4 {
|
||||
piece_pointer += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if numbers_seen != 4 {
|
||||
return Err(ParseError::InvalidIpv6Address);
|
||||
}
|
||||
}
|
||||
|
||||
if i < len {
|
||||
return Err(ParseError::InvalidIpv6Address);
|
||||
}
|
||||
|
||||
match compress_pointer {
|
||||
Some(compress_pointer) => {
|
||||
let mut swaps = piece_pointer - compress_pointer;
|
||||
piece_pointer = 7;
|
||||
while swaps > 0 {
|
||||
pieces.swap(piece_pointer, compress_pointer + swaps - 1);
|
||||
swaps -= 1;
|
||||
piece_pointer -= 1;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if piece_pointer != 8 {
|
||||
return Err(ParseError::InvalidIpv6Address);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Ipv6Addr::new(
|
||||
pieces[0], pieces[1], pieces[2], pieces[3], pieces[4], pieces[5], pieces[6], pieces[7],
|
||||
))
|
||||
}
|
||||
2889
zeroidc/vendor/url/src/lib.rs
vendored
Normal file
2889
zeroidc/vendor/url/src/lib.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
113
zeroidc/vendor/url/src/origin.rs
vendored
Normal file
113
zeroidc/vendor/url/src/origin.rs
vendored
Normal file
@@ -0,0 +1,113 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
use crate::host::Host;
|
||||
use crate::parser::default_port;
|
||||
use crate::Url;
|
||||
use idna::domain_to_unicode;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
pub fn url_origin(url: &Url) -> Origin {
|
||||
let scheme = url.scheme();
|
||||
match scheme {
|
||||
"blob" => {
|
||||
let result = Url::parse(url.path());
|
||||
match result {
|
||||
Ok(ref url) => url_origin(url),
|
||||
Err(_) => Origin::new_opaque(),
|
||||
}
|
||||
}
|
||||
"ftp" | "http" | "https" | "ws" | "wss" => Origin::Tuple(
|
||||
scheme.to_owned(),
|
||||
url.host().unwrap().to_owned(),
|
||||
url.port_or_known_default().unwrap(),
|
||||
),
|
||||
// TODO: Figure out what to do if the scheme is a file
|
||||
"file" => Origin::new_opaque(),
|
||||
_ => Origin::new_opaque(),
|
||||
}
|
||||
}
|
||||
|
||||
/// The origin of an URL
|
||||
///
|
||||
/// Two URLs with the same origin are considered
|
||||
/// to originate from the same entity and can therefore trust
|
||||
/// each other.
|
||||
///
|
||||
/// The origin is determined based on the scheme as follows:
|
||||
///
|
||||
/// - If the scheme is "blob" the origin is the origin of the
|
||||
/// URL contained in the path component. If parsing fails,
|
||||
/// it is an opaque origin.
|
||||
/// - If the scheme is "ftp", "http", "https", "ws", or "wss",
|
||||
/// then the origin is a tuple of the scheme, host, and port.
|
||||
/// - If the scheme is anything else, the origin is opaque, meaning
|
||||
/// the URL does not have the same origin as any other URL.
|
||||
///
|
||||
/// For more information see <https://url.spec.whatwg.org/#origin>
|
||||
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
|
||||
pub enum Origin {
|
||||
/// A globally unique identifier
|
||||
Opaque(OpaqueOrigin),
|
||||
|
||||
/// Consists of the URL's scheme, host and port
|
||||
Tuple(String, Host<String>, u16),
|
||||
}
|
||||
|
||||
impl Origin {
|
||||
/// Creates a new opaque origin that is only equal to itself.
|
||||
pub fn new_opaque() -> Origin {
|
||||
static COUNTER: AtomicUsize = AtomicUsize::new(0);
|
||||
Origin::Opaque(OpaqueOrigin(COUNTER.fetch_add(1, Ordering::SeqCst)))
|
||||
}
|
||||
|
||||
/// Return whether this origin is a (scheme, host, port) tuple
|
||||
/// (as opposed to an opaque origin).
|
||||
pub fn is_tuple(&self) -> bool {
|
||||
matches!(*self, Origin::Tuple(..))
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#ascii-serialisation-of-an-origin>
|
||||
pub fn ascii_serialization(&self) -> String {
|
||||
match *self {
|
||||
Origin::Opaque(_) => "null".to_owned(),
|
||||
Origin::Tuple(ref scheme, ref host, port) => {
|
||||
if default_port(scheme) == Some(port) {
|
||||
format!("{}://{}", scheme, host)
|
||||
} else {
|
||||
format!("{}://{}:{}", scheme, host, port)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#unicode-serialisation-of-an-origin>
|
||||
pub fn unicode_serialization(&self) -> String {
|
||||
match *self {
|
||||
Origin::Opaque(_) => "null".to_owned(),
|
||||
Origin::Tuple(ref scheme, ref host, port) => {
|
||||
let host = match *host {
|
||||
Host::Domain(ref domain) => {
|
||||
let (domain, _errors) = domain_to_unicode(domain);
|
||||
Host::Domain(domain)
|
||||
}
|
||||
_ => host.clone(),
|
||||
};
|
||||
if default_port(scheme) == Some(port) {
|
||||
format!("{}://{}", scheme, host)
|
||||
} else {
|
||||
format!("{}://{}:{}", scheme, host, port)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Opaque identifier for URLs that have file or other schemes
|
||||
#[derive(Eq, PartialEq, Hash, Clone, Debug)]
|
||||
pub struct OpaqueOrigin(usize);
|
||||
1606
zeroidc/vendor/url/src/parser.rs
vendored
Normal file
1606
zeroidc/vendor/url/src/parser.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
246
zeroidc/vendor/url/src/path_segments.rs
vendored
Normal file
246
zeroidc/vendor/url/src/path_segments.rs
vendored
Normal file
@@ -0,0 +1,246 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
use crate::parser::{self, to_u32, SchemeType};
|
||||
use crate::Url;
|
||||
use std::str;
|
||||
|
||||
/// Exposes methods to manipulate the path of an URL that is not cannot-be-base.
|
||||
///
|
||||
/// The path always starts with a `/` slash, and is made of slash-separated segments.
|
||||
/// There is always at least one segment (which may be the empty string).
|
||||
///
|
||||
/// Examples:
|
||||
///
|
||||
/// ```rust
|
||||
/// use url::Url;
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn run() -> Result<(), Box<dyn Error>> {
|
||||
/// let mut url = Url::parse("mailto:me@example.com")?;
|
||||
/// assert!(url.path_segments_mut().is_err());
|
||||
///
|
||||
/// let mut url = Url::parse("http://example.net/foo/index.html")?;
|
||||
/// url.path_segments_mut().map_err(|_| "cannot be base")?
|
||||
/// .pop().push("img").push("2/100%.png");
|
||||
/// assert_eq!(url.as_str(), "http://example.net/foo/img/2%2F100%25.png");
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// # run().unwrap();
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct PathSegmentsMut<'a> {
|
||||
url: &'a mut Url,
|
||||
after_first_slash: usize,
|
||||
after_path: String,
|
||||
old_after_path_position: u32,
|
||||
}
|
||||
|
||||
// Not re-exported outside the crate
|
||||
pub fn new(url: &mut Url) -> PathSegmentsMut<'_> {
|
||||
let after_path = url.take_after_path();
|
||||
let old_after_path_position = to_u32(url.serialization.len()).unwrap();
|
||||
// Special urls always have a non empty path
|
||||
if SchemeType::from(url.scheme()).is_special() {
|
||||
debug_assert!(url.byte_at(url.path_start) == b'/');
|
||||
} else {
|
||||
debug_assert!(
|
||||
url.serialization.len() == url.path_start as usize
|
||||
|| url.byte_at(url.path_start) == b'/'
|
||||
);
|
||||
}
|
||||
PathSegmentsMut {
|
||||
after_first_slash: url.path_start as usize + "/".len(),
|
||||
url,
|
||||
old_after_path_position,
|
||||
after_path,
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for PathSegmentsMut<'a> {
|
||||
fn drop(&mut self) {
|
||||
self.url
|
||||
.restore_after_path(self.old_after_path_position, &self.after_path)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PathSegmentsMut<'a> {
|
||||
/// Remove all segments in the path, leaving the minimal `url.path() == "/"`.
|
||||
///
|
||||
/// Returns `&mut Self` so that method calls can be chained.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```rust
|
||||
/// use url::Url;
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn run() -> Result<(), Box<dyn Error>> {
|
||||
/// let mut url = Url::parse("https://github.com/servo/rust-url/")?;
|
||||
/// url.path_segments_mut().map_err(|_| "cannot be base")?
|
||||
/// .clear().push("logout");
|
||||
/// assert_eq!(url.as_str(), "https://github.com/logout");
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// # run().unwrap();
|
||||
/// ```
|
||||
pub fn clear(&mut self) -> &mut Self {
|
||||
self.url.serialization.truncate(self.after_first_slash);
|
||||
self
|
||||
}
|
||||
|
||||
/// Remove the last segment of this URL’s path if it is empty,
|
||||
/// except if these was only one segment to begin with.
|
||||
///
|
||||
/// In other words, remove one path trailing slash, if any,
|
||||
/// unless it is also the initial slash (so this does nothing if `url.path() == "/")`.
|
||||
///
|
||||
/// Returns `&mut Self` so that method calls can be chained.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```rust
|
||||
/// use url::Url;
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn run() -> Result<(), Box<dyn Error>> {
|
||||
/// let mut url = Url::parse("https://github.com/servo/rust-url/")?;
|
||||
/// url.path_segments_mut().map_err(|_| "cannot be base")?
|
||||
/// .push("pulls");
|
||||
/// assert_eq!(url.as_str(), "https://github.com/servo/rust-url//pulls");
|
||||
///
|
||||
/// let mut url = Url::parse("https://github.com/servo/rust-url/")?;
|
||||
/// url.path_segments_mut().map_err(|_| "cannot be base")?
|
||||
/// .pop_if_empty().push("pulls");
|
||||
/// assert_eq!(url.as_str(), "https://github.com/servo/rust-url/pulls");
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// # run().unwrap();
|
||||
/// ```
|
||||
pub fn pop_if_empty(&mut self) -> &mut Self {
|
||||
if self.after_first_slash >= self.url.serialization.len() {
|
||||
return self;
|
||||
}
|
||||
if self.url.serialization[self.after_first_slash..].ends_with('/') {
|
||||
self.url.serialization.pop();
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Remove the last segment of this URL’s path.
|
||||
///
|
||||
/// If the path only has one segment, make it empty such that `url.path() == "/"`.
|
||||
///
|
||||
/// Returns `&mut Self` so that method calls can be chained.
|
||||
pub fn pop(&mut self) -> &mut Self {
|
||||
if self.after_first_slash >= self.url.serialization.len() {
|
||||
return self;
|
||||
}
|
||||
let last_slash = self.url.serialization[self.after_first_slash..]
|
||||
.rfind('/')
|
||||
.unwrap_or(0);
|
||||
self.url
|
||||
.serialization
|
||||
.truncate(self.after_first_slash + last_slash);
|
||||
self
|
||||
}
|
||||
|
||||
/// Append the given segment at the end of this URL’s path.
|
||||
///
|
||||
/// See the documentation for `.extend()`.
|
||||
///
|
||||
/// Returns `&mut Self` so that method calls can be chained.
|
||||
pub fn push(&mut self, segment: &str) -> &mut Self {
|
||||
self.extend(Some(segment))
|
||||
}
|
||||
|
||||
/// Append each segment from the given iterator at the end of this URL’s path.
|
||||
///
|
||||
/// Each segment is percent-encoded like in `Url::parse` or `Url::join`,
|
||||
/// except that `%` and `/` characters are also encoded (to `%25` and `%2F`).
|
||||
/// This is unlike `Url::parse` where `%` is left as-is in case some of the input
|
||||
/// is already percent-encoded, and `/` denotes a path segment separator.)
|
||||
///
|
||||
/// Note that, in addition to slashes between new segments,
|
||||
/// this always adds a slash between the existing path and the new segments
|
||||
/// *except* if the existing path is `"/"`.
|
||||
/// If the previous last segment was empty (if the path had a trailing slash)
|
||||
/// the path after `.extend()` will contain two consecutive slashes.
|
||||
/// If that is undesired, call `.pop_if_empty()` first.
|
||||
///
|
||||
/// To obtain a behavior similar to `Url::join`, call `.pop()` unconditionally first.
|
||||
///
|
||||
/// Returns `&mut Self` so that method calls can be chained.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```rust
|
||||
/// use url::Url;
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn run() -> Result<(), Box<dyn Error>> {
|
||||
/// let mut url = Url::parse("https://github.com/")?;
|
||||
/// let org = "servo";
|
||||
/// let repo = "rust-url";
|
||||
/// let issue_number = "188";
|
||||
/// url.path_segments_mut().map_err(|_| "cannot be base")?
|
||||
/// .extend(&[org, repo, "issues", issue_number]);
|
||||
/// assert_eq!(url.as_str(), "https://github.com/servo/rust-url/issues/188");
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// # run().unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// In order to make sure that parsing the serialization of an URL gives the same URL,
|
||||
/// a segment is ignored if it is `"."` or `".."`:
|
||||
///
|
||||
/// ```rust
|
||||
/// use url::Url;
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn run() -> Result<(), Box<dyn Error>> {
|
||||
/// let mut url = Url::parse("https://github.com/servo")?;
|
||||
/// url.path_segments_mut().map_err(|_| "cannot be base")?
|
||||
/// .extend(&["..", "rust-url", ".", "pulls"]);
|
||||
/// assert_eq!(url.as_str(), "https://github.com/servo/rust-url/pulls");
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// # run().unwrap();
|
||||
/// ```
|
||||
pub fn extend<I>(&mut self, segments: I) -> &mut Self
|
||||
where
|
||||
I: IntoIterator,
|
||||
I::Item: AsRef<str>,
|
||||
{
|
||||
let scheme_type = SchemeType::from(self.url.scheme());
|
||||
let path_start = self.url.path_start as usize;
|
||||
self.url.mutate(|parser| {
|
||||
parser.context = parser::Context::PathSegmentSetter;
|
||||
for segment in segments {
|
||||
let segment = segment.as_ref();
|
||||
if matches!(segment, "." | "..") {
|
||||
continue;
|
||||
}
|
||||
if parser.serialization.len() > path_start + 1
|
||||
// Non special url's path might still be empty
|
||||
|| parser.serialization.len() == path_start
|
||||
{
|
||||
parser.serialization.push('/');
|
||||
}
|
||||
let mut has_host = true; // FIXME account for this?
|
||||
parser.parse_path(
|
||||
scheme_type,
|
||||
&mut has_host,
|
||||
path_start,
|
||||
parser::Input::new(segment),
|
||||
);
|
||||
}
|
||||
});
|
||||
self
|
||||
}
|
||||
}
|
||||
287
zeroidc/vendor/url/src/quirks.rs
vendored
Normal file
287
zeroidc/vendor/url/src/quirks.rs
vendored
Normal file
@@ -0,0 +1,287 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
//! Getters and setters for URL components implemented per https://url.spec.whatwg.org/#api
|
||||
//!
|
||||
//! Unless you need to be interoperable with web browsers,
|
||||
//! you probably want to use `Url` method instead.
|
||||
|
||||
use crate::parser::{default_port, Context, Input, Parser, SchemeType};
|
||||
use crate::{Host, ParseError, Position, Url};
|
||||
|
||||
/// https://url.spec.whatwg.org/#dom-url-domaintoascii
|
||||
pub fn domain_to_ascii(domain: &str) -> String {
|
||||
match Host::parse(domain) {
|
||||
Ok(Host::Domain(domain)) => domain,
|
||||
_ => String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// https://url.spec.whatwg.org/#dom-url-domaintounicode
|
||||
pub fn domain_to_unicode(domain: &str) -> String {
|
||||
match Host::parse(domain) {
|
||||
Ok(Host::Domain(ref domain)) => {
|
||||
let (unicode, _errors) = idna::domain_to_unicode(domain);
|
||||
unicode
|
||||
}
|
||||
_ => String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Getter for https://url.spec.whatwg.org/#dom-url-href
|
||||
pub fn href(url: &Url) -> &str {
|
||||
url.as_str()
|
||||
}
|
||||
|
||||
/// Setter for https://url.spec.whatwg.org/#dom-url-href
|
||||
pub fn set_href(url: &mut Url, value: &str) -> Result<(), ParseError> {
|
||||
*url = Url::parse(value)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Getter for https://url.spec.whatwg.org/#dom-url-origin
|
||||
pub fn origin(url: &Url) -> String {
|
||||
url.origin().ascii_serialization()
|
||||
}
|
||||
|
||||
/// Getter for https://url.spec.whatwg.org/#dom-url-protocol
|
||||
#[inline]
|
||||
pub fn protocol(url: &Url) -> &str {
|
||||
&url.as_str()[..url.scheme().len() + ":".len()]
|
||||
}
|
||||
|
||||
/// Setter for https://url.spec.whatwg.org/#dom-url-protocol
|
||||
#[allow(clippy::result_unit_err)]
|
||||
pub fn set_protocol(url: &mut Url, mut new_protocol: &str) -> Result<(), ()> {
|
||||
// The scheme state in the spec ignores everything after the first `:`,
|
||||
// but `set_scheme` errors if there is more.
|
||||
if let Some(position) = new_protocol.find(':') {
|
||||
new_protocol = &new_protocol[..position];
|
||||
}
|
||||
url.set_scheme(new_protocol)
|
||||
}
|
||||
|
||||
/// Getter for https://url.spec.whatwg.org/#dom-url-username
|
||||
#[inline]
|
||||
pub fn username(url: &Url) -> &str {
|
||||
url.username()
|
||||
}
|
||||
|
||||
/// Setter for https://url.spec.whatwg.org/#dom-url-username
|
||||
#[allow(clippy::result_unit_err)]
|
||||
pub fn set_username(url: &mut Url, new_username: &str) -> Result<(), ()> {
|
||||
url.set_username(new_username)
|
||||
}
|
||||
|
||||
/// Getter for https://url.spec.whatwg.org/#dom-url-password
|
||||
#[inline]
|
||||
pub fn password(url: &Url) -> &str {
|
||||
url.password().unwrap_or("")
|
||||
}
|
||||
|
||||
/// Setter for https://url.spec.whatwg.org/#dom-url-password
|
||||
#[allow(clippy::result_unit_err)]
|
||||
pub fn set_password(url: &mut Url, new_password: &str) -> Result<(), ()> {
|
||||
url.set_password(if new_password.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(new_password)
|
||||
})
|
||||
}
|
||||
|
||||
/// Getter for https://url.spec.whatwg.org/#dom-url-host
|
||||
#[inline]
|
||||
pub fn host(url: &Url) -> &str {
|
||||
&url[Position::BeforeHost..Position::AfterPort]
|
||||
}
|
||||
|
||||
/// Setter for https://url.spec.whatwg.org/#dom-url-host
|
||||
#[allow(clippy::result_unit_err)]
|
||||
pub fn set_host(url: &mut Url, new_host: &str) -> Result<(), ()> {
|
||||
// If context object’s url’s cannot-be-a-base-URL flag is set, then return.
|
||||
if url.cannot_be_a_base() {
|
||||
return Err(());
|
||||
}
|
||||
// Host parsing rules are strict,
|
||||
// We don't want to trim the input
|
||||
let input = Input::no_trim(new_host);
|
||||
let host;
|
||||
let opt_port;
|
||||
{
|
||||
let scheme = url.scheme();
|
||||
let scheme_type = SchemeType::from(scheme);
|
||||
if scheme_type == SchemeType::File && new_host.is_empty() {
|
||||
url.set_host_internal(Host::Domain(String::new()), None);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Ok((h, remaining)) = Parser::parse_host(input, scheme_type) {
|
||||
host = h;
|
||||
opt_port = if let Some(remaining) = remaining.split_prefix(':') {
|
||||
if remaining.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Parser::parse_port(remaining, || default_port(scheme), Context::Setter)
|
||||
.ok()
|
||||
.map(|(port, _remaining)| port)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
} else {
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
// Make sure we won't set an empty host to a url with a username or a port
|
||||
if host == Host::Domain("".to_string()) {
|
||||
if !username(&url).is_empty() {
|
||||
return Err(());
|
||||
} else if let Some(Some(_)) = opt_port {
|
||||
return Err(());
|
||||
} else if url.port().is_some() {
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
url.set_host_internal(host, opt_port);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Getter for https://url.spec.whatwg.org/#dom-url-hostname
|
||||
#[inline]
|
||||
pub fn hostname(url: &Url) -> &str {
|
||||
url.host_str().unwrap_or("")
|
||||
}
|
||||
|
||||
/// Setter for https://url.spec.whatwg.org/#dom-url-hostname
|
||||
#[allow(clippy::result_unit_err)]
|
||||
pub fn set_hostname(url: &mut Url, new_hostname: &str) -> Result<(), ()> {
|
||||
if url.cannot_be_a_base() {
|
||||
return Err(());
|
||||
}
|
||||
// Host parsing rules are strict we don't want to trim the input
|
||||
let input = Input::no_trim(new_hostname);
|
||||
let scheme_type = SchemeType::from(url.scheme());
|
||||
if scheme_type == SchemeType::File && new_hostname.is_empty() {
|
||||
url.set_host_internal(Host::Domain(String::new()), None);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Ok((host, _remaining)) = Parser::parse_host(input, scheme_type) {
|
||||
if let Host::Domain(h) = &host {
|
||||
if h.is_empty() {
|
||||
// Empty host on special not file url
|
||||
if SchemeType::from(url.scheme()) == SchemeType::SpecialNotFile
|
||||
// Port with an empty host
|
||||
||!port(&url).is_empty()
|
||||
// Empty host that includes credentials
|
||||
|| !url.username().is_empty()
|
||||
|| !url.password().unwrap_or(&"").is_empty()
|
||||
{
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
}
|
||||
url.set_host_internal(host, None);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Getter for https://url.spec.whatwg.org/#dom-url-port
|
||||
#[inline]
|
||||
pub fn port(url: &Url) -> &str {
|
||||
&url[Position::BeforePort..Position::AfterPort]
|
||||
}
|
||||
|
||||
/// Setter for https://url.spec.whatwg.org/#dom-url-port
|
||||
#[allow(clippy::result_unit_err)]
|
||||
pub fn set_port(url: &mut Url, new_port: &str) -> Result<(), ()> {
|
||||
let result;
|
||||
{
|
||||
// has_host implies !cannot_be_a_base
|
||||
let scheme = url.scheme();
|
||||
if !url.has_host() || url.host() == Some(Host::Domain("")) || scheme == "file" {
|
||||
return Err(());
|
||||
}
|
||||
result = Parser::parse_port(
|
||||
Input::new(new_port),
|
||||
|| default_port(scheme),
|
||||
Context::Setter,
|
||||
)
|
||||
}
|
||||
if let Ok((new_port, _remaining)) = result {
|
||||
url.set_port_internal(new_port);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Getter for https://url.spec.whatwg.org/#dom-url-pathname
|
||||
#[inline]
|
||||
pub fn pathname(url: &Url) -> &str {
|
||||
url.path()
|
||||
}
|
||||
|
||||
/// Setter for https://url.spec.whatwg.org/#dom-url-pathname
|
||||
pub fn set_pathname(url: &mut Url, new_pathname: &str) {
|
||||
if url.cannot_be_a_base() {
|
||||
return;
|
||||
}
|
||||
if new_pathname.starts_with('/')
|
||||
|| (SchemeType::from(url.scheme()).is_special()
|
||||
// \ is a segment delimiter for 'special' URLs"
|
||||
&& new_pathname.starts_with('\\'))
|
||||
{
|
||||
url.set_path(new_pathname)
|
||||
} else {
|
||||
let mut path_to_set = String::from("/");
|
||||
path_to_set.push_str(new_pathname);
|
||||
url.set_path(&path_to_set)
|
||||
}
|
||||
}
|
||||
|
||||
/// Getter for https://url.spec.whatwg.org/#dom-url-search
|
||||
pub fn search(url: &Url) -> &str {
|
||||
trim(&url[Position::AfterPath..Position::AfterQuery])
|
||||
}
|
||||
|
||||
/// Setter for https://url.spec.whatwg.org/#dom-url-search
|
||||
pub fn set_search(url: &mut Url, new_search: &str) {
|
||||
url.set_query(match new_search {
|
||||
"" => None,
|
||||
_ if new_search.starts_with('?') => Some(&new_search[1..]),
|
||||
_ => Some(new_search),
|
||||
})
|
||||
}
|
||||
|
||||
/// Getter for https://url.spec.whatwg.org/#dom-url-hash
|
||||
pub fn hash(url: &Url) -> &str {
|
||||
trim(&url[Position::AfterQuery..])
|
||||
}
|
||||
|
||||
/// Setter for https://url.spec.whatwg.org/#dom-url-hash
|
||||
pub fn set_hash(url: &mut Url, new_hash: &str) {
|
||||
url.set_fragment(match new_hash {
|
||||
// If the given value is the empty string,
|
||||
// then set context object’s url’s fragment to null and return.
|
||||
"" => None,
|
||||
// Let input be the given value with a single leading U+0023 (#) removed, if any.
|
||||
_ if new_hash.starts_with('#') => Some(&new_hash[1..]),
|
||||
_ => Some(new_hash),
|
||||
})
|
||||
}
|
||||
|
||||
fn trim(s: &str) -> &str {
|
||||
if s.len() == 1 {
|
||||
""
|
||||
} else {
|
||||
s
|
||||
}
|
||||
}
|
||||
187
zeroidc/vendor/url/src/slicing.rs
vendored
Normal file
187
zeroidc/vendor/url/src/slicing.rs
vendored
Normal file
@@ -0,0 +1,187 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
use crate::Url;
|
||||
use std::ops::{Index, Range, RangeFrom, RangeFull, RangeTo};
|
||||
|
||||
impl Index<RangeFull> for Url {
|
||||
type Output = str;
|
||||
fn index(&self, _: RangeFull) -> &str {
|
||||
&self.serialization
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<RangeFrom<Position>> for Url {
|
||||
type Output = str;
|
||||
fn index(&self, range: RangeFrom<Position>) -> &str {
|
||||
&self.serialization[self.index(range.start)..]
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<RangeTo<Position>> for Url {
|
||||
type Output = str;
|
||||
fn index(&self, range: RangeTo<Position>) -> &str {
|
||||
&self.serialization[..self.index(range.end)]
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<Range<Position>> for Url {
|
||||
type Output = str;
|
||||
fn index(&self, range: Range<Position>) -> &str {
|
||||
&self.serialization[self.index(range.start)..self.index(range.end)]
|
||||
}
|
||||
}
|
||||
|
||||
/// Indicates a position within a URL based on its components.
|
||||
///
|
||||
/// A range of positions can be used for slicing `Url`:
|
||||
///
|
||||
/// ```rust
|
||||
/// # use url::{Url, Position};
|
||||
/// # fn something(some_url: Url) {
|
||||
/// let serialization: &str = &some_url[..];
|
||||
/// let serialization_without_fragment: &str = &some_url[..Position::AfterQuery];
|
||||
/// let authority: &str = &some_url[Position::BeforeUsername..Position::AfterPort];
|
||||
/// let data_url_payload: &str = &some_url[Position::BeforePath..Position::AfterQuery];
|
||||
/// let scheme_relative: &str = &some_url[Position::BeforeUsername..];
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// In a pseudo-grammar (where `[`…`]?` makes a sub-sequence optional),
|
||||
/// URL components and delimiters that separate them are:
|
||||
///
|
||||
/// ```notrust
|
||||
/// url =
|
||||
/// scheme ":"
|
||||
/// [ "//" [ username [ ":" password ]? "@" ]? host [ ":" port ]? ]?
|
||||
/// path [ "?" query ]? [ "#" fragment ]?
|
||||
/// ```
|
||||
///
|
||||
/// When a given component is not present,
|
||||
/// its "before" and "after" position are the same
|
||||
/// (so that `&some_url[BeforeFoo..AfterFoo]` is the empty string)
|
||||
/// and component ordering is preserved
|
||||
/// (so that a missing query "is between" a path and a fragment).
|
||||
///
|
||||
/// The end of a component and the start of the next are either the same or separate
|
||||
/// by a delimiter.
|
||||
/// (Not that the initial `/` of a path is considered part of the path here, not a delimiter.)
|
||||
/// For example, `&url[..BeforeFragment]` would include a `#` delimiter (if present in `url`),
|
||||
/// so `&url[..AfterQuery]` might be desired instead.
|
||||
///
|
||||
/// `BeforeScheme` and `AfterFragment` are always the start and end of the entire URL,
|
||||
/// so `&url[BeforeScheme..X]` is the same as `&url[..X]`
|
||||
/// and `&url[X..AfterFragment]` is the same as `&url[X..]`.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum Position {
|
||||
BeforeScheme,
|
||||
AfterScheme,
|
||||
BeforeUsername,
|
||||
AfterUsername,
|
||||
BeforePassword,
|
||||
AfterPassword,
|
||||
BeforeHost,
|
||||
AfterHost,
|
||||
BeforePort,
|
||||
AfterPort,
|
||||
BeforePath,
|
||||
AfterPath,
|
||||
BeforeQuery,
|
||||
AfterQuery,
|
||||
BeforeFragment,
|
||||
AfterFragment,
|
||||
}
|
||||
|
||||
impl Url {
|
||||
#[inline]
|
||||
fn index(&self, position: Position) -> usize {
|
||||
match position {
|
||||
Position::BeforeScheme => 0,
|
||||
|
||||
Position::AfterScheme => self.scheme_end as usize,
|
||||
|
||||
Position::BeforeUsername => {
|
||||
if self.has_authority() {
|
||||
self.scheme_end as usize + "://".len()
|
||||
} else {
|
||||
debug_assert!(self.byte_at(self.scheme_end) == b':');
|
||||
debug_assert!(self.scheme_end + ":".len() as u32 == self.username_end);
|
||||
self.scheme_end as usize + ":".len()
|
||||
}
|
||||
}
|
||||
|
||||
Position::AfterUsername => self.username_end as usize,
|
||||
|
||||
Position::BeforePassword => {
|
||||
if self.has_authority() && self.byte_at(self.username_end) == b':' {
|
||||
self.username_end as usize + ":".len()
|
||||
} else {
|
||||
debug_assert!(self.username_end == self.host_start);
|
||||
self.username_end as usize
|
||||
}
|
||||
}
|
||||
|
||||
Position::AfterPassword => {
|
||||
if self.has_authority() && self.byte_at(self.username_end) == b':' {
|
||||
debug_assert!(self.byte_at(self.host_start - "@".len() as u32) == b'@');
|
||||
self.host_start as usize - "@".len()
|
||||
} else {
|
||||
debug_assert!(self.username_end == self.host_start);
|
||||
self.host_start as usize
|
||||
}
|
||||
}
|
||||
|
||||
Position::BeforeHost => self.host_start as usize,
|
||||
|
||||
Position::AfterHost => self.host_end as usize,
|
||||
|
||||
Position::BeforePort => {
|
||||
if self.port.is_some() {
|
||||
debug_assert!(self.byte_at(self.host_end) == b':');
|
||||
self.host_end as usize + ":".len()
|
||||
} else {
|
||||
self.host_end as usize
|
||||
}
|
||||
}
|
||||
|
||||
Position::AfterPort => self.path_start as usize,
|
||||
|
||||
Position::BeforePath => self.path_start as usize,
|
||||
|
||||
Position::AfterPath => match (self.query_start, self.fragment_start) {
|
||||
(Some(q), _) => q as usize,
|
||||
(None, Some(f)) => f as usize,
|
||||
(None, None) => self.serialization.len(),
|
||||
},
|
||||
|
||||
Position::BeforeQuery => match (self.query_start, self.fragment_start) {
|
||||
(Some(q), _) => {
|
||||
debug_assert!(self.byte_at(q) == b'?');
|
||||
q as usize + "?".len()
|
||||
}
|
||||
(None, Some(f)) => f as usize,
|
||||
(None, None) => self.serialization.len(),
|
||||
},
|
||||
|
||||
Position::AfterQuery => match self.fragment_start {
|
||||
None => self.serialization.len(),
|
||||
Some(f) => f as usize,
|
||||
},
|
||||
|
||||
Position::BeforeFragment => match self.fragment_start {
|
||||
Some(f) => {
|
||||
debug_assert!(self.byte_at(f) == b'#');
|
||||
f as usize + "#".len()
|
||||
}
|
||||
None => self.serialization.len(),
|
||||
},
|
||||
|
||||
Position::AfterFragment => self.serialization.len(),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user