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:
Adam Ierymenko
2022-06-08 07:32:16 -04:00
parent 373ca30269
commit d5ca4e5f52
12611 changed files with 2898014 additions and 284 deletions

480
zeroidc/vendor/url/src/host.rs vendored Normal file
View 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

File diff suppressed because it is too large Load Diff

113
zeroidc/vendor/url/src/origin.rs vendored Normal file
View 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

File diff suppressed because it is too large Load Diff

246
zeroidc/vendor/url/src/path_segments.rs vendored Normal file
View 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 URLs 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 URLs 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 URLs 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 URLs 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
View 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 objects urls 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 objects urls 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
View 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(),
}
}
}