#![doc(html_root_url = "https://docs.rs/url/1.7.0")]
#[cfg(feature="rustc-serialize")] extern crate rustc_serialize;
#[macro_use] extern crate matches;
#[cfg(feature="serde")] extern crate serde;
#[cfg(feature="heapsize")] #[macro_use] extern crate heapsize;
pub extern crate idna;
#[macro_use]
pub extern crate percent_encoding;
use encoding::EncodingOverride;
#[cfg(feature = "heapsize")] use heapsize::HeapSizeOf;
use host::HostInternal;
use parser::{Parser, Context, SchemeType, to_u32, ViolationFn};
use percent_encoding::{PATH_SEGMENT_ENCODE_SET, USERINFO_ENCODE_SET,
percent_encode, percent_decode, utf8_percent_encode};
use std::borrow::Borrow;
use std::cmp;
#[cfg(feature = "serde")] use std::error::Error;
use std::fmt::{self, Write, Debug, Formatter};
use std::hash;
use std::io;
use std::mem;
use std::net::{ToSocketAddrs, IpAddr};
use std::ops::{Range, RangeFrom, RangeTo};
use std::path::{Path, PathBuf};
use std::str;
pub use origin::{Origin, OpaqueOrigin};
pub use host::{Host, HostAndPort, SocketAddrs};
pub use path_segments::PathSegmentsMut;
pub use parser::{ParseError, SyntaxViolation};
pub use slicing::Position;
mod encoding;
mod host;
mod origin;
mod path_segments;
mod parser;
mod slicing;
pub mod form_urlencoded;
#[doc(hidden)] pub mod quirks;
#[derive(Clone)]
pub struct Url {
serialization: String,
scheme_end: u32, username_end: u32, host_start: u32,
host_end: u32,
host: HostInternal,
port: Option<u16>,
path_start: u32, query_start: Option<u32>, fragment_start: Option<u32>, }
#[cfg(feature = "heapsize")]
impl HeapSizeOf for Url {
fn heap_size_of_children(&self) -> usize {
self.serialization.heap_size_of_children()
}
}
#[derive(Copy, Clone)]
pub struct ParseOptions<'a> {
base_url: Option<&'a Url>,
encoding_override: encoding::EncodingOverride,
violation_fn: ViolationFn<'a>,
}
impl<'a> ParseOptions<'a> {
pub fn base_url(mut self, new: Option<&'a Url>) -> Self {
self.base_url = new;
self
}
#[cfg(feature = "query_encoding")]
pub fn encoding_override(mut self, new: Option<encoding::EncodingRef>) -> Self {
self.encoding_override = EncodingOverride::from_opt_encoding(new).to_output_encoding();
self
}
#[deprecated]
pub fn log_syntax_violation(mut self, new: Option<&'a Fn(&'static str)>) -> Self {
self.violation_fn = match new {
Some(f) => ViolationFn::OldFn(f),
None => ViolationFn::NoOp
};
self
}
pub fn syntax_violation_callback(mut self, new: Option<&'a Fn(SyntaxViolation)>) -> Self {
self.violation_fn = match new {
Some(f) => ViolationFn::NewFn(f),
None => ViolationFn::NoOp
};
self
}
pub fn parse(self, input: &str) -> Result<Url, ::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<'a> Debug for ParseOptions<'a> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f,
"ParseOptions {{ base_url: {:?}, encoding_override: {:?}, \
violation_fn: {:?} }}",
self.base_url,
self.encoding_override,
self.violation_fn)
}
}
impl Url {
#[inline]
pub fn parse(input: &str) -> Result<Url, ::ParseError> {
Url::options().parse(input)
}
#[inline]
pub fn parse_with_params<I, K, V>(input: &str, iter: I) -> Result<Url, ::ParseError>
where I: IntoIterator,
I::Item: Borrow<(K, V)>,
K: AsRef<str>,
V: AsRef<str>
{
let mut url = Url::options().parse(input);
if let Ok(ref mut url) = url {
url.query_pairs_mut().extend_pairs(iter);
}
url
}
#[inline]
pub fn join(&self, input: &str) -> Result<Url, ::ParseError> {
Url::options().base_url(Some(self)).parse(input)
}
pub fn options<'a>() -> ParseOptions<'a> {
ParseOptions {
base_url: None,
encoding_override: EncodingOverride::utf8(),
violation_fn: ViolationFn::NoOp,
}
}
#[inline]
pub fn as_str(&self) -> &str {
&self.serialization
}
#[inline]
pub fn into_string(self) -> String {
self.serialization
}
#[doc(hidden)]
pub fn 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))
}
}
}
}
assert!(self.scheme_end >= 1);
assert!(matches!(self.byte_at(0), b'a'...b'z' | b'A'...b'Z'));
assert!(self.slice(1..self.scheme_end).chars()
.all(|c| matches!(c, 'a'...'z' | 'A'...'Z' | '0'...'9' | '+' | '-' | '.')));
assert_eq!(self.byte_at(self.scheme_end), b':');
if self.slice(self.scheme_end + 1 ..).starts_with("//") {
match self.byte_at(self.username_end) {
b':' => {
assert!(self.host_start >= self.username_end + 2);
assert_eq!(self.byte_at(self.host_start - 1), b'@');
}
b'@' => assert!(self.host_start == self.username_end + 1),
_ => assert_eq!(self.username_end, self.scheme_end + 3),
}
assert!(self.host_start >= self.username_end);
assert!(self.host_end >= self.host_start);
let host_str = self.slice(self.host_start..self.host_end);
match self.host {
HostInternal::None => assert_eq!(host_str, ""),
HostInternal::Ipv4(address) => assert_eq!(host_str, address.to_string()),
HostInternal::Ipv6(address) => {
let h: Host<String> = Host::Ipv6(address);
assert_eq!(host_str, h.to_string())
}
HostInternal::Domain => {
if SchemeType::from(self.scheme()).is_special() {
assert!(!host_str.is_empty())
}
}
}
if self.path_start == self.host_end {
assert_eq!(self.port, None);
} else {
assert_eq!(self.byte_at(self.host_end), b':');
let port_str = self.slice(self.host_end + 1..self.path_start);
assert_eq!(self.port, Some(port_str.parse::<u16>().expect("Couldn't parse port?")));
}
assert_eq!(self.byte_at(self.path_start), b'/');
} else {
assert_eq!(self.username_end, self.scheme_end + 1);
assert_eq!(self.host_start, self.scheme_end + 1);
assert_eq!(self.host_end, self.scheme_end + 1);
assert_eq!(self.host, HostInternal::None);
assert_eq!(self.port, None);
assert_eq!(self.path_start, self.scheme_end + 1);
}
if let Some(start) = self.query_start {
assert!(start > self.path_start);
assert_eq!(self.byte_at(start), b'?');
}
if let Some(start) = self.fragment_start {
assert!(start > self.path_start);
assert_eq!(self.byte_at(start), b'#');
}
if let (Some(query_start), Some(fragment_start)) = (self.query_start, self.fragment_start) {
assert!(fragment_start > query_start);
}
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 ||
(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(())
}
#[inline]
pub fn origin(&self) -> Origin {
origin::url_origin(self)
}
#[inline]
pub fn scheme(&self) -> &str {
self.slice(..self.scheme_end)
}
#[inline]
pub fn has_authority(&self) -> bool {
debug_assert!(self.byte_at(self.scheme_end) == b':');
self.slice(self.scheme_end..).starts_with("://")
}
#[inline]
pub fn cannot_be_a_base(&self) -> bool {
!self.slice(self.path_start..).starts_with('/')
}
pub fn username(&self) -> &str {
if self.has_authority() {
self.slice(self.scheme_end + ("://".len() as u32)..self.username_end)
} else {
""
}
}
pub fn password(&self) -> Option<&str> {
if self.has_authority() && 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
}
}
pub fn has_host(&self) -> bool {
!matches!(self.host, HostInternal::None)
}
pub fn host_str(&self) -> Option<&str> {
if self.has_host() {
Some(self.slice(self.host_start..self.host_end))
} else {
None
}
}
pub fn host(&self) -> Option<Host<&str>> {
match self.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)),
}
}
pub fn domain(&self) -> Option<&str> {
match self.host {
HostInternal::Domain => Some(self.slice(self.host_start..self.host_end)),
_ => None,
}
}
#[inline]
pub fn port(&self) -> Option<u16> {
self.port
}
#[inline]
pub fn port_or_known_default(&self) -> Option<u16> {
self.port.or_else(|| parser::default_port(self.scheme()))
}
pub fn with_default_port<F>(&self, f: F) -> io::Result<HostAndPort<&str>>
where F: FnOnce(&Url) -> Result<u16, ()> {
Ok(HostAndPort {
host: self.host()
.ok_or(())
.or_else(|()| io_error("URL has no host"))?,
port: self.port_or_known_default()
.ok_or(())
.or_else(|()| f(self))
.or_else(|()| io_error("URL has no port number"))?
})
}
pub fn 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)
}
}
}
pub fn path_segments(&self) -> Option<str::Split<char>> {
let path = self.path();
if path.starts_with('/') {
Some(path[1..].split('/'))
} else {
None
}
}
pub fn 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))
}
}
}
#[inline]
pub fn query_pairs(&self) -> form_urlencoded::Parse {
form_urlencoded::parse(self.query().unwrap_or("").as_bytes())
}
pub fn 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 {
let mut parser = Parser::for_setter(mem::replace(&mut self.serialization, String::new()));
let result = f(&mut parser);
self.serialization = parser.serialization;
result
}
pub fn set_fragment(&mut self, fragment: Option<&str>) {
if let Some(start) = self.fragment_start {
debug_assert!(self.byte_at(start) == b'#');
self.serialization.truncate(start as usize);
}
if let Some(input) = fragment {
self.fragment_start = Some(to_u32(self.serialization.len()).unwrap());
self.serialization.push('#');
self.mutate(|parser| parser.parse_fragment(parser::Input::new(input)))
} else {
self.fragment_start = None
}
}
fn take_fragment(&mut self) -> Option<String> {
self.fragment_start.take().map(|start| {
debug_assert!(self.byte_at(start) == b'#');
let fragment = self.slice(start + 1..).to_owned();
self.serialization.truncate(start as usize);
fragment
})
}
fn restore_already_parsed_fragment(&mut self, fragment: Option<String>) {
if let Some(ref fragment) = fragment {
assert!(self.fragment_start.is_none());
self.fragment_start = Some(to_u32(self.serialization.len()).unwrap());
self.serialization.push('#');
self.serialization.push_str(fragment);
}
}
pub fn set_query(&mut self, query: Option<&str>) {
let fragment = self.take_fragment();
if let Some(start) = self.query_start.take() {
debug_assert!(self.byte_at(start) == b'?');
self.serialization.truncate(start as usize);
}
if let Some(input) = query {
self.query_start = Some(to_u32(self.serialization.len()).unwrap());
self.serialization.push('?');
let scheme_end = self.scheme_end;
self.mutate(|parser| parser.parse_query(scheme_end, parser::Input::new(input)));
}
self.restore_already_parsed_fragment(fragment);
}
pub fn query_pairs_mut(&mut self) -> form_urlencoded::Serializer<UrlQuery> {
let fragment = self.take_fragment();
let query_start;
if let Some(start) = self.query_start {
debug_assert!(self.byte_at(start) == b'?');
query_start = start as usize;
} else {
query_start = self.serialization.len();
self.query_start = Some(to_u32(query_start).unwrap());
self.serialization.push('?');
}
let query = UrlQuery { url: Some(self), fragment: fragment };
form_urlencoded::Serializer::for_suffix(query, query_start + "?".len())
}
fn take_after_path(&mut self) -> String {
match (self.query_start, self.fragment_start) {
(Some(i), _) | (None, Some(i)) => {
let after_path = self.slice(i..).to_owned();
self.serialization.truncate(i as usize);
after_path
},
(None, None) => String::new(),
}
}
pub fn set_path(&mut self, mut path: &str) {
let after_path = self.take_after_path();
let old_after_path_pos = to_u32(self.serialization.len()).unwrap();
let cannot_be_a_base = self.cannot_be_a_base();
let scheme_type = SchemeType::from(self.scheme());
self.serialization.truncate(self.path_start as usize);
self.mutate(|parser| {
if cannot_be_a_base {
if path.starts_with('/') {
parser.serialization.push_str("%2F");
path = &path[1..];
}
parser.parse_cannot_be_a_base_path(parser::Input::new(path));
} else {
let mut has_host = true; parser.parse_path_start(scheme_type, &mut has_host, parser::Input::new(path));
}
});
self.restore_after_path(old_after_path_pos, &after_path);
}
pub fn path_segments_mut(&mut self) -> Result<PathSegmentsMut, ()> {
if self.cannot_be_a_base() {
Err(())
} else {
Ok(path_segments::new(self))
}
}
fn restore_after_path(&mut self, old_after_path_position: u32, after_path: &str) {
let new_after_path_position = to_u32(self.serialization.len()).unwrap();
let adjust = |index: &mut u32| {
*index -= old_after_path_position;
*index += new_after_path_position;
};
if let Some(ref mut index) = self.query_start { adjust(index) }
if let Some(ref mut index) = self.fragment_start { adjust(index) }
self.serialization.push_str(after_path)
}
pub fn set_port(&mut self, mut port: Option<u16>) -> Result<(), ()> {
if !self.has_host() || self.host() == Some(Host::Domain("")) || self.scheme() == "file" {
return Err(())
}
if port.is_some() && port == parser::default_port(self.scheme()) {
port = None
}
self.set_port_internal(port);
Ok(())
}
fn set_port_internal(&mut self, port: Option<u16>) {
match (self.port, port) {
(None, None) => {}
(Some(_), None) => {
self.serialization.drain(self.host_end as usize .. self.path_start as usize);
let offset = self.path_start - self.host_end;
self.path_start = self.host_end;
if let Some(ref mut index) = self.query_start { *index -= offset }
if let Some(ref mut index) = self.fragment_start { *index -= offset }
}
(Some(old), Some(new)) if old == new => {}
(_, Some(new)) => {
let path_and_after = self.slice(self.path_start..).to_owned();
self.serialization.truncate(self.host_end as usize);
write!(&mut self.serialization, ":{}", new).unwrap();
let old_path_start = self.path_start;
let new_path_start = to_u32(self.serialization.len()).unwrap();
self.path_start = new_path_start;
let adjust = |index: &mut u32| {
*index -= old_path_start;
*index += new_path_start;
};
if let Some(ref mut index) = self.query_start { adjust(index) }
if let Some(ref mut index) = self.fragment_start { adjust(index) }
self.serialization.push_str(&path_and_after);
}
}
self.port = port;
}
pub fn set_host(&mut self, host: Option<&str>) -> Result<(), ParseError> {
if self.cannot_be_a_base() {
return Err(ParseError::SetHostOnCannotBeABaseUrl)
}
if let Some(host) = host {
if host == "" && SchemeType::from(self.scheme()).is_special() {
return Err(ParseError::EmptyHost);
}
if SchemeType::from(self.scheme()).is_special() {
self.set_host_internal(Host::parse(host)?, None)
} else {
self.set_host_internal(Host::parse_opaque(host)?, None)
}
} else if self.has_host() {
if SchemeType::from(self.scheme()).is_special() {
return Err(ParseError::EmptyHost)
}
debug_assert!(self.byte_at(self.scheme_end) == b':');
debug_assert!(self.byte_at(self.path_start) == b'/');
let new_path_start = self.scheme_end + 1;
self.serialization.drain(new_path_start as usize..self.path_start as usize);
let offset = self.path_start - new_path_start;
self.path_start = new_path_start;
self.username_end = new_path_start;
self.host_start = new_path_start;
self.host_end = new_path_start;
self.port = None;
if let Some(ref mut index) = self.query_start { *index -= offset }
if let Some(ref mut index) = self.fragment_start { *index -= offset }
}
Ok(())
}
fn set_host_internal(&mut self, host: Host<String>, opt_new_port: Option<Option<u16>>) {
let old_suffix_pos = if opt_new_port.is_some() { self.path_start } else { self.host_end };
let suffix = self.slice(old_suffix_pos..).to_owned();
self.serialization.truncate(self.host_start as usize);
if !self.has_authority() {
debug_assert!(self.slice(self.scheme_end..self.host_start) == ":");
debug_assert!(self.username_end == self.host_start);
self.serialization.push('/');
self.serialization.push('/');
self.username_end += 2;
self.host_start += 2;
}
write!(&mut self.serialization, "{}", host).unwrap();
self.host_end = to_u32(self.serialization.len()).unwrap();
self.host = host.into();
if let Some(new_port) = opt_new_port {
self.port = new_port;
if let Some(port) = new_port {
write!(&mut self.serialization, ":{}", port).unwrap();
}
}
let new_suffix_pos = to_u32(self.serialization.len()).unwrap();
self.serialization.push_str(&suffix);
let adjust = |index: &mut u32| {
*index -= old_suffix_pos;
*index += new_suffix_pos;
};
adjust(&mut self.path_start);
if let Some(ref mut index) = self.query_start { adjust(index) }
if let Some(ref mut index) = self.fragment_start { adjust(index) }
}
pub fn set_ip_host(&mut self, address: IpAddr) -> Result<(), ()> {
if self.cannot_be_a_base() {
return Err(())
}
let address = match address {
IpAddr::V4(address) => Host::Ipv4(address),
IpAddr::V6(address) => Host::Ipv6(address),
};
self.set_host_internal(address, None);
Ok(())
}
pub fn set_password(&mut self, password: Option<&str>) -> Result<(), ()> {
if !self.has_host() || self.host() == Some(Host::Domain("")) || self.scheme() == "file" {
return Err(())
}
if let Some(password) = password {
let host_and_after = self.slice(self.host_start..).to_owned();
self.serialization.truncate(self.username_end as usize);
self.serialization.push(':');
self.serialization.extend(utf8_percent_encode(password, USERINFO_ENCODE_SET));
self.serialization.push('@');
let old_host_start = self.host_start;
let new_host_start = to_u32(self.serialization.len()).unwrap();
let adjust = |index: &mut u32| {
*index -= old_host_start;
*index += new_host_start;
};
self.host_start = new_host_start;
adjust(&mut self.host_end);
adjust(&mut self.path_start);
if let Some(ref mut index) = self.query_start { adjust(index) }
if let Some(ref mut index) = self.fragment_start { adjust(index) }
self.serialization.push_str(&host_and_after);
} else if self.byte_at(self.username_end) == b':' { 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; let end = if empty_username {
self.host_start } else {
self.host_start - 1 };
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;
if let Some(ref mut index) = self.query_start { *index -= offset }
if let Some(ref mut index) = self.fragment_start { *index -= offset }
}
Ok(())
}
pub fn set_username(&mut self, username: &str) -> Result<(), ()> {
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) == "://");
if self.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_ENCODE_SET));
let mut removed_bytes = self.username_end;
self.username_end = to_u32(self.serialization.len()).unwrap();
let mut added_bytes = self.username_end;
let new_username_is_empty = self.username_end == username_start;
match (new_username_is_empty, after_username.chars().next()) {
(true, Some('@')) => {
removed_bytes += 1;
self.serialization.push_str(&after_username[1..]);
}
(false, Some('@')) | (_, Some(':')) | (true, _) => {
self.serialization.push_str(&after_username);
}
(false, _) => {
added_bytes += 1;
self.serialization.push('@');
self.serialization.push_str(&after_username);
}
}
let adjust = |index: &mut u32| {
*index -= removed_bytes;
*index += added_bytes;
};
adjust(&mut self.host_start);
adjust(&mut self.host_end);
adjust(&mut self.path_start);
if let Some(ref mut index) = self.query_start { adjust(index) }
if let Some(ref mut index) = self.fragment_start { adjust(index) }
Ok(())
}
pub fn set_scheme(&mut self, scheme: &str) -> Result<(), ()> {
let mut parser = Parser::for_setter(String::new());
let remaining = parser.parse_scheme(parser::Input::new(scheme))?;
if !remaining.is_empty() ||
(!self.has_host() && SchemeType::from(&parser.serialization).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;
};
self.scheme_end = new_scheme_end;
adjust(&mut self.username_end);
adjust(&mut self.host_start);
adjust(&mut self.host_end);
adjust(&mut self.path_start);
if let Some(ref mut index) = self.query_start { adjust(index) }
if let Some(ref mut index) = self.fragment_start { adjust(index) }
parser.serialization.push_str(self.slice(old_scheme_end..));
self.serialization = parser.serialization;
Ok(())
}
#[cfg(any(unix, windows, target_os="redox"))]
pub fn from_file_path<P: AsRef<Path>>(path: P) -> Result<Url, ()> {
let mut 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: serialization,
scheme_end: "file".len() as u32,
username_end: host_start,
host_start: host_start,
host_end: host_end,
host: host,
port: None,
path_start: host_end,
query_start: None,
fragment_start: None,
})
}
#[cfg(any(unix, windows, target_os="redox"))]
pub fn from_directory_path<P: AsRef<Path>>(path: P) -> Result<Url, ()> {
let mut url = Url::from_file_path(path)?;
if !url.serialization.ends_with('/') {
url.serialization.push('/')
}
Ok(url)
}
#[cfg(feature = "serde")]
#[deny(unused)]
pub fn serialize_internal<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: serde::Serializer {
use serde::Serialize;
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)
}
#[cfg(feature = "serde")]
#[deny(unused)]
pub fn deserialize_internal<D>(deserializer: &mut D) -> Result<Self, D::Error> where D: serde::Deserializer {
use serde::{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: serialization,
scheme_end: scheme_end,
username_end: username_end,
host_start: host_start,
host_end: host_end,
host: host,
port: port,
path_start: path_start,
query_start: query_start,
fragment_start: fragment_start
};
if cfg!(debug_assertions) {
url.check_invariants().map_err(|ref reason| Error::invalid_value(&reason))?
}
Ok(url)
}
#[inline]
#[cfg(any(unix, windows, target_os="redox"))]
pub fn to_file_path(&self) -> Result<PathBuf, ()> {
if let Some(segments) = self.path_segments() {
let host = match self.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(())
};
return file_url_segments_to_pathbuf(host, segments);
}
Err(())
}
#[inline]
fn slice<R>(&self, range: R) -> &str where R: RangeArg {
range.slice_of(&self.serialization)
}
#[inline]
fn byte_at(&self, i: u32) -> u8 {
self.serialization.as_bytes()[i as usize]
}
}
impl ToSocketAddrs for Url {
type Iter = SocketAddrs;
fn to_socket_addrs(&self) -> io::Result<Self::Iter> {
self.with_default_port(|_| Err(()))?.to_socket_addrs()
}
}
impl str::FromStr for Url {
type Err = ParseError;
#[inline]
fn from_str(input: &str) -> Result<Url, ::ParseError> {
Url::parse(input)
}
}
impl fmt::Display for Url {
#[inline]
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.serialization, formatter)
}
}
impl fmt::Debug for Url {
#[inline]
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self.serialization, formatter)
}
}
impl Eq for Url {}
impl PartialEq for Url {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.serialization == other.serialization
}
}
impl Ord for Url {
#[inline]
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.serialization.cmp(&other.serialization)
}
}
impl PartialOrd for Url {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
self.serialization.partial_cmp(&other.serialization)
}
}
impl hash::Hash for Url {
#[inline]
fn hash<H>(&self, state: &mut H) where H: hash::Hasher {
hash::Hash::hash(&self.serialization, state)
}
}
impl AsRef<str> for Url {
#[inline]
fn as_ref(&self) -> &str {
&self.serialization
}
}
trait RangeArg {
fn slice_of<'a>(&self, s: &'a str) -> &'a str;
}
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]
}
}
#[cfg(feature="rustc-serialize")]
impl rustc_serialize::Encodable for Url {
fn encode<S: rustc_serialize::Encoder>(&self, encoder: &mut S) -> Result<(), S::Error> {
encoder.emit_str(self.as_str())
}
}
#[cfg(feature="rustc-serialize")]
impl rustc_serialize::Decodable for Url {
fn decode<D: rustc_serialize::Decoder>(decoder: &mut D) -> Result<Url, D::Error> {
Url::parse(&*decoder.read_str()?).map_err(|error| {
decoder.error(&format!("URL parsing error: {}", error))
})
}
}
#[cfg(feature="serde")]
impl serde::Serialize for Url {
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: serde::Serializer {
serializer.serialize_str(self.as_str())
}
}
#[cfg(feature="serde")]
impl serde::Deserialize for Url {
fn deserialize<D>(deserializer: &mut D) -> Result<Url, D::Error> where D: serde::Deserializer {
let string_representation: String = serde::Deserialize::deserialize(deserializer)?;
Url::parse(&string_representation).map_err(|err| {
serde::Error::invalid_value(err.description())
})
}
}
#[cfg(any(unix, target_os = "redox"))]
fn path_to_file_url_segments(path: &Path, serialization: &mut String)
-> Result<(u32, HostInternal), ()> {
use std::os::unix::prelude::OsStrExt;
if !path.is_absolute() {
return Err(())
}
let host_end = to_u32(serialization.len()).unwrap();
let mut empty = true;
for component in path.components().skip(1) {
empty = false;
serialization.push('/');
serialization.extend(percent_encode(
component.as_os_str().as_bytes(), PATH_SEGMENT_ENCODE_SET));
}
if empty {
serialization.push('/');
}
Ok((host_end, HostInternal::None))
}
#[cfg(windows)]
fn path_to_file_url_segments(path: &Path, serialization: &mut String)
-> Result<(u32, HostInternal), ()> {
path_to_file_url_segments_windows(path, serialization)
}
#[cfg_attr(not(windows), allow(dead_code))]
fn path_to_file_url_segments_windows(path: &Path, serialization: &mut String)
-> Result<(u32, HostInternal), ()> {
use std::path::{Prefix, Component};
if !path.is_absolute() {
return Err(())
}
let mut components = path.components();
let host_end;
let host_internal;
match components.next() {
Some(Component::Prefix(ref p)) => match p.kind() {
Prefix::Disk(letter) | Prefix::VerbatimDisk(letter) => {
host_end = to_u32(serialization.len()).unwrap();
host_internal = HostInternal::None;
serialization.push('/');
serialization.push(letter as char);
serialization.push(':');
},
Prefix::UNC(server, share) | Prefix::VerbatimUNC(server, share) => {
let host = Host::parse(server.to_str().ok_or(())?).map_err(|_| ())?;
write!(serialization, "{}", host).unwrap();
host_end = to_u32(serialization.len()).unwrap();
host_internal = host.into();
serialization.push('/');
let share = share.to_str().ok_or(())?;
serialization.extend(percent_encode(share.as_bytes(), PATH_SEGMENT_ENCODE_SET));
},
_ => return Err(())
},
_ => return Err(())
}
for component in components {
if component == Component::RootDir { continue }
let component = component.as_os_str().to_str().ok_or(())?;
serialization.push('/');
serialization.extend(percent_encode(component.as_bytes(), PATH_SEGMENT_ENCODE_SET));
}
Ok((host_end, host_internal))
}
#[cfg(any(unix, target_os = "redox"))]
fn file_url_segments_to_pathbuf(host: Option<&str>, segments: str::Split<char>) -> Result<PathBuf, ()> {
use std::ffi::OsStr;
use std::os::unix::prelude::OsStrExt;
use std::path::PathBuf;
if host.is_some() {
return Err(());
}
let mut bytes = Vec::new();
for segment in segments {
bytes.push(b'/');
bytes.extend(percent_decode(segment.as_bytes()));
}
let os_str = OsStr::from_bytes(&bytes);
let path = PathBuf::from(os_str);
debug_assert!(path.is_absolute(),
"to_file_path() failed to produce an absolute Path");
Ok(path)
}
#[cfg(windows)]
fn file_url_segments_to_pathbuf(host: Option<&str>, segments: str::Split<char>) -> Result<PathBuf, ()> {
file_url_segments_to_pathbuf_windows(host, segments)
}
#[cfg_attr(not(windows), allow(dead_code))]
fn file_url_segments_to_pathbuf_windows(host: Option<&str>, mut segments: str::Split<char>) -> Result<PathBuf, ()> {
let mut string = if let Some(host) = host {
r"\\".to_owned() + host
} else {
let first = segments.next().ok_or(())?;
match first.len() {
2 => {
if !first.starts_with(parser::ascii_alpha) || first.as_bytes()[1] != b':' {
return Err(())
}
first.to_owned()
},
4 => {
if !first.starts_with(parser::ascii_alpha) {
return Err(())
}
let bytes = first.as_bytes();
if bytes[1] != b'%' || bytes[2] != b'3' || (bytes[3] != b'a' && bytes[3] != b'A') {
return Err(())
}
first[0..1].to_owned() + ":"
},
_ => return Err(()),
}
};
for segment in segments {
string.push('\\');
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)
}
fn io_error<T>(reason: &str) -> io::Result<T> {
Err(io::Error::new(io::ErrorKind::InvalidData, reason))
}
#[derive(Debug)]
pub struct UrlQuery<'a> {
url: Option<&'a mut Url>,
fragment: Option<String>,
}
impl<'a> Drop for UrlQuery<'a> {
fn drop(&mut self) {
if let Some(url) = self.url.take() {
url.restore_already_parsed_fragment(self.fragment.take())
}
}
}
#[macro_export]
macro_rules! define_encode_set {
($(#[$attr: meta])* pub $name: ident = [$base_set: expr] | {$($ch: pat),*}) => {
$(#[$attr])*
#[derive(Copy, Clone)]
#[allow(non_camel_case_types)]
pub struct $name;
impl $crate::percent_encoding::EncodeSet for $name {
#[inline]
fn contains(&self, byte: u8) -> bool {
match byte as char {
$(
$ch => true,
)*
_ => $base_set.contains(byte)
}
}
}
}
}