|
|
@@ -6,18 +6,12 @@
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
// except according to those terms.
|
|
|
|
|
|
-
|
|
|
use std::ascii::AsciiExt;
|
|
|
-use std::fmt::{Formatter, FormatError, Show};
|
|
|
+use std::error::Error;
|
|
|
+use std::fmt::{mod, Formatter, Show};
|
|
|
use std::str::CharRange;
|
|
|
|
|
|
-use encoding;
|
|
|
-
|
|
|
-use super::{
|
|
|
- UrlParser, Url, RelativeSchemeData, NonRelativeSchemeData, Host, Domain,
|
|
|
- SchemeType, FileLikeRelativeScheme, RelativeScheme, NonRelativeScheme,
|
|
|
- UrlRelativeSchemeData,
|
|
|
-};
|
|
|
+use super::{UrlParser, Url, SchemeData, RelativeSchemeData, Host, SchemeType};
|
|
|
use percent_encoding::{
|
|
|
utf8_percent_encode_to, percent_encode,
|
|
|
SIMPLE_ENCODE_SET, DEFAULT_ENCODE_SET, USERINFO_ENCODE_SET, QUERY_ENCODE_SET
|
|
|
@@ -33,56 +27,59 @@ macro_rules! is_match(
|
|
|
|
|
|
pub type ParseResult<T> = Result<T, ParseError>;
|
|
|
|
|
|
-/// Errors that can occur during parsing.
|
|
|
-#[deriving(PartialEq, Eq, Clone)]
|
|
|
-pub enum ParseError {
|
|
|
- EmptyHost,
|
|
|
- InvalidScheme,
|
|
|
- InvalidPort,
|
|
|
- InvalidIpv6Address,
|
|
|
- InvalidDomainCharacter,
|
|
|
- InvalidCharacter,
|
|
|
- InvalidBackslash,
|
|
|
- InvalidPercentEncoded,
|
|
|
- InvalidAtSymbolInUser,
|
|
|
- ExpectedTwoSlashes,
|
|
|
- ExpectedInitialSlash,
|
|
|
- NonUrlCodePoint,
|
|
|
- RelativeUrlWithScheme,
|
|
|
- RelativeUrlWithoutBase,
|
|
|
- RelativeUrlWithNonRelativeBase,
|
|
|
- NonAsciiDomainsNotSupportedYet,
|
|
|
- CannotSetFileScheme(&'static str),
|
|
|
- CannotSetJavascriptScheme(&'static str),
|
|
|
- CannotSetNonRelativeScheme(&'static str)
|
|
|
+
|
|
|
+macro_rules! simple_enum_error {
|
|
|
+ ($($name: ident => $description: expr,)+) => {
|
|
|
+ /// Errors that can occur during parsing.
|
|
|
+ #[deriving(PartialEq, Eq, Clone)]
|
|
|
+ pub enum ParseError {
|
|
|
+ $(
|
|
|
+ $name,
|
|
|
+ )+
|
|
|
+ }
|
|
|
+
|
|
|
+ impl Error for ParseError {
|
|
|
+ fn description(&self) -> &str {
|
|
|
+ match *self {
|
|
|
+ $(
|
|
|
+ ParseError::$name => $description,
|
|
|
+ )+
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+simple_enum_error! {
|
|
|
+ EmptyHost => "empty host",
|
|
|
+ InvalidScheme => "invalid scheme",
|
|
|
+ InvalidPort => "invalid port number",
|
|
|
+ InvalidIpv6Address => "invalid IPv6 address",
|
|
|
+ InvalidDomainCharacter => "invalid domain character",
|
|
|
+ InvalidCharacter => "invalid character",
|
|
|
+ InvalidBackslash => "invalid backslash",
|
|
|
+ InvalidPercentEncoded => "invalid percent-encoded sequence",
|
|
|
+ InvalidAtSymbolInUser => "invalid @-symbol in user",
|
|
|
+ ExpectedTwoSlashes => "expected two slashes (//)",
|
|
|
+ ExpectedInitialSlash => "expected the input to start with a slash",
|
|
|
+ NonUrlCodePoint => "non URL code point",
|
|
|
+ RelativeUrlWithScheme => "relative URL with scheme",
|
|
|
+ RelativeUrlWithoutBase => "relative URL without a base",
|
|
|
+ RelativeUrlWithNonRelativeBase => "relative URL with a non-relative base",
|
|
|
+ NonAsciiDomainsNotSupportedYet => "non-ASCII domains are not supported yet",
|
|
|
+ CannotSetJavascriptFragment => "cannot set fragment on javascript: URL",
|
|
|
+ CannotSetPortWithFileLikeScheme => "cannot set port with file-like scheme",
|
|
|
+ CannotSetUsernameWithNonRelativeScheme => "cannot set username with non-relative scheme",
|
|
|
+ CannotSetPasswordWithNonRelativeScheme => "cannot set password with non-relative scheme",
|
|
|
+ CannotSetHostPortWithNonRelativeScheme => "cannot set host and port with non-relative scheme",
|
|
|
+ CannotSetHostWithNonRelativeScheme => "cannot set host with non-relative scheme",
|
|
|
+ CannotSetPortWithNonRelativeScheme => "cannot set port with non-relative scheme",
|
|
|
+ CannotSetPathWithNonRelativeScheme => "cannot set path with non-relative scheme",
|
|
|
}
|
|
|
|
|
|
impl Show for ParseError {
|
|
|
- fn fmt(&self, fmt: &mut Formatter) -> Result<(), FormatError> {
|
|
|
- match *self {
|
|
|
- EmptyHost => "Empty host",
|
|
|
- InvalidScheme => "Invalid scheme",
|
|
|
- InvalidPort => "Invalid port number",
|
|
|
- InvalidIpv6Address => "Invalid IPv6 address",
|
|
|
- InvalidDomainCharacter => "Invalid domain character",
|
|
|
- InvalidCharacter => "Invalid character",
|
|
|
- InvalidBackslash => "Invalid backslash",
|
|
|
- InvalidPercentEncoded => "Invalid percent-encoded sequence",
|
|
|
- InvalidAtSymbolInUser => "Invalid @-symbol in user",
|
|
|
- ExpectedTwoSlashes => "Expected two slashes (//)",
|
|
|
- ExpectedInitialSlash => "Expected the input to start with a slash",
|
|
|
- NonUrlCodePoint => "Non URL code point",
|
|
|
- RelativeUrlWithScheme => "Relative URL with scheme",
|
|
|
- RelativeUrlWithoutBase => "Relative URL without a base",
|
|
|
- RelativeUrlWithNonRelativeBase => "Relative URL with a non-relative base",
|
|
|
- NonAsciiDomainsNotSupportedYet => "Non Ascii domains are not support yet",
|
|
|
- CannotSetFileScheme(ref part) =>
|
|
|
- return write!(fmt, "Cannot set {} on file: URLs", part),
|
|
|
- CannotSetJavascriptScheme(ref part) =>
|
|
|
- return write!(fmt, "Cannot set {} on javascript: URLs", part),
|
|
|
- CannotSetNonRelativeScheme(ref part) =>
|
|
|
- return write!(fmt, "Cannot set {} on non-relative URLs", part),
|
|
|
- }.fmt(fmt)
|
|
|
+ fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
|
|
|
+ self.description().fmt(fmt)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -97,59 +94,59 @@ pub type ErrorHandler = fn(reason: ParseError) -> ParseResult<()>;
|
|
|
|
|
|
#[deriving(PartialEq, Eq)]
|
|
|
pub enum Context {
|
|
|
- UrlParserContext,
|
|
|
- SetterContext,
|
|
|
+ UrlParser,
|
|
|
+ Setter,
|
|
|
}
|
|
|
|
|
|
|
|
|
pub fn parse_url(input: &str, parser: &UrlParser) -> ParseResult<Url> {
|
|
|
let input = input.trim_chars([' ', '\t', '\n', '\r', '\x0C'].as_slice());
|
|
|
- let (scheme, remaining) = match parse_scheme(input, UrlParserContext) {
|
|
|
+ let (scheme, remaining) = match parse_scheme(input, Context::UrlParser) {
|
|
|
Some((scheme, remaining)) => (scheme, remaining),
|
|
|
// No-scheme state
|
|
|
None => return match parser.base_url {
|
|
|
- Some(&Url { ref scheme, scheme_data: RelativeSchemeData(ref base),
|
|
|
+ Some(&Url { ref scheme, scheme_data: SchemeData::Relative(ref base),
|
|
|
ref query, .. }) => {
|
|
|
let scheme_type = parser.get_scheme_type(scheme.as_slice());
|
|
|
parse_relative_url(input, scheme.clone(), scheme_type, base, query, parser)
|
|
|
},
|
|
|
- Some(_) => Err(RelativeUrlWithNonRelativeBase),
|
|
|
- None => Err(RelativeUrlWithoutBase),
|
|
|
+ Some(_) => Err(ParseError::RelativeUrlWithNonRelativeBase),
|
|
|
+ None => Err(ParseError::RelativeUrlWithoutBase),
|
|
|
},
|
|
|
};
|
|
|
let scheme_type = parser.get_scheme_type(scheme.as_slice());
|
|
|
match scheme_type {
|
|
|
- FileLikeRelativeScheme => {
|
|
|
+ SchemeType::FileLike => {
|
|
|
// Relative state?
|
|
|
match parser.base_url {
|
|
|
- Some(&Url { scheme: ref base_scheme, scheme_data: RelativeSchemeData(ref base),
|
|
|
+ Some(&Url { scheme: ref base_scheme, scheme_data: SchemeData::Relative(ref base),
|
|
|
ref query, .. })
|
|
|
if scheme == *base_scheme => {
|
|
|
parse_relative_url(remaining, scheme, scheme_type, base, query, parser)
|
|
|
},
|
|
|
// FIXME: Should not have to use a made-up base URL.
|
|
|
- _ => parse_relative_url(remaining, scheme, scheme_type, &UrlRelativeSchemeData {
|
|
|
- username: String::new(), password: None, host: Domain(String::new()),
|
|
|
+ _ => parse_relative_url(remaining, scheme, scheme_type, &RelativeSchemeData {
|
|
|
+ username: String::new(), password: None, host: Host::Domain(String::new()),
|
|
|
port: None, default_port: None, path: Vec::new()
|
|
|
}, &None, parser)
|
|
|
}
|
|
|
},
|
|
|
- RelativeScheme(..) => {
|
|
|
+ SchemeType::Relative(..) => {
|
|
|
match parser.base_url {
|
|
|
- Some(&Url { scheme: ref base_scheme, scheme_data: RelativeSchemeData(ref base),
|
|
|
+ Some(&Url { scheme: ref base_scheme, scheme_data: SchemeData::Relative(ref base),
|
|
|
ref query, .. })
|
|
|
if scheme == *base_scheme && !remaining.starts_with("//") => {
|
|
|
- try!(parser.parse_error(RelativeUrlWithScheme));
|
|
|
+ try!(parser.parse_error(ParseError::RelativeUrlWithScheme));
|
|
|
parse_relative_url(remaining, scheme, scheme_type, base, query, parser)
|
|
|
},
|
|
|
_ => parse_absolute_url(scheme, scheme_type, remaining, parser),
|
|
|
}
|
|
|
},
|
|
|
- NonRelativeScheme => {
|
|
|
+ SchemeType::NonRelative => {
|
|
|
// Scheme data state
|
|
|
let (scheme_data, remaining) = try!(parse_scheme_data(remaining, parser));
|
|
|
let (query, fragment) = try!(parse_query_and_fragment(remaining, parser));
|
|
|
- Ok(Url { scheme: scheme, scheme_data: NonRelativeSchemeData(scheme_data),
|
|
|
+ Ok(Url { scheme: scheme, scheme_data: SchemeData::NonRelative(scheme_data),
|
|
|
query: query, fragment: fragment })
|
|
|
}
|
|
|
}
|
|
|
@@ -172,8 +169,8 @@ pub fn parse_scheme<'a>(input: &'a str, context: Context) -> Option<(String, &'a
|
|
|
}
|
|
|
// EOF before ':'
|
|
|
match context {
|
|
|
- SetterContext => Some((input.to_ascii_lower(), "")),
|
|
|
- UrlParserContext => None
|
|
|
+ Context::Setter => Some((input.to_ascii_lower(), "")),
|
|
|
+ Context::UrlParser => None
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -187,8 +184,8 @@ fn parse_absolute_url<'a>(scheme: String, scheme_type: SchemeType,
|
|
|
// Host state
|
|
|
let (host, port, default_port, remaining) = try!(parse_host(remaining, scheme_type, parser));
|
|
|
let (path, remaining) = try!(parse_path_start(
|
|
|
- remaining, UrlParserContext, scheme_type, parser));
|
|
|
- let scheme_data = RelativeSchemeData(UrlRelativeSchemeData {
|
|
|
+ remaining, Context::UrlParser, scheme_type, parser));
|
|
|
+ let scheme_data = SchemeData::Relative(RelativeSchemeData {
|
|
|
username: username, password: password,
|
|
|
host: host, port: port, default_port: default_port,
|
|
|
path: path });
|
|
|
@@ -198,19 +195,21 @@ fn parse_absolute_url<'a>(scheme: String, scheme_type: SchemeType,
|
|
|
|
|
|
|
|
|
fn parse_relative_url<'a>(input: &'a str, scheme: String, scheme_type: SchemeType,
|
|
|
- base: &UrlRelativeSchemeData, base_query: &Option<String>,
|
|
|
+ base: &RelativeSchemeData, base_query: &Option<String>,
|
|
|
parser: &UrlParser)
|
|
|
-> ParseResult<Url> {
|
|
|
if input.is_empty() {
|
|
|
- return Ok(Url { scheme: scheme, scheme_data: RelativeSchemeData(base.clone()),
|
|
|
+ return Ok(Url { scheme: scheme, scheme_data: SchemeData::Relative(base.clone()),
|
|
|
query: base_query.clone(), fragment: None })
|
|
|
}
|
|
|
match input.char_at(0) {
|
|
|
'/' | '\\' => {
|
|
|
// Relative slash state
|
|
|
if input.len() > 1 && is_match!(input.char_at(1), '/' | '\\') {
|
|
|
- if input.char_at(1) == '\\' { try!(parser.parse_error(InvalidBackslash)) }
|
|
|
- if scheme_type == FileLikeRelativeScheme {
|
|
|
+ if input.char_at(1) == '\\' {
|
|
|
+ try!(parser.parse_error(ParseError::InvalidBackslash))
|
|
|
+ }
|
|
|
+ if scheme_type == SchemeType::FileLike {
|
|
|
// File host state
|
|
|
let remaining = input.slice_from(2);
|
|
|
let (host, remaining) = if remaining.len() >= 2
|
|
|
@@ -221,13 +220,13 @@ fn parse_relative_url<'a>(input: &'a str, scheme: String, scheme_type: SchemeTyp
|
|
|
'/' | '\\' | '?' | '#'))
|
|
|
{
|
|
|
// Windows drive letter quirk
|
|
|
- (Domain(String::new()), remaining)
|
|
|
+ (Host::Domain(String::new()), remaining)
|
|
|
} else {
|
|
|
try!(parse_file_host(remaining, parser))
|
|
|
};
|
|
|
let (path, remaining) = try!(parse_path_start(
|
|
|
- remaining, UrlParserContext, scheme_type, parser));
|
|
|
- let scheme_data = RelativeSchemeData(UrlRelativeSchemeData {
|
|
|
+ remaining, Context::UrlParser, scheme_type, parser));
|
|
|
+ let scheme_data = SchemeData::Relative(RelativeSchemeData {
|
|
|
username: String::new(), password: None,
|
|
|
host: host, port: None, default_port: None, path: path
|
|
|
});
|
|
|
@@ -240,14 +239,14 @@ fn parse_relative_url<'a>(input: &'a str, scheme: String, scheme_type: SchemeTyp
|
|
|
} else {
|
|
|
// Relative path state
|
|
|
let (path, remaining) = try!(parse_path(
|
|
|
- [], input.slice_from(1), UrlParserContext, scheme_type, parser));
|
|
|
- let scheme_data = RelativeSchemeData(if scheme_type == FileLikeRelativeScheme {
|
|
|
- UrlRelativeSchemeData {
|
|
|
+ &[], input.slice_from(1), Context::UrlParser, scheme_type, parser));
|
|
|
+ let scheme_data = SchemeData::Relative(if scheme_type == SchemeType::FileLike {
|
|
|
+ RelativeSchemeData {
|
|
|
username: String::new(), password: None, host:
|
|
|
- Domain(String::new()), port: None, default_port: None, path: path
|
|
|
+ Host::Domain(String::new()), port: None, default_port: None, path: path
|
|
|
}
|
|
|
} else {
|
|
|
- UrlRelativeSchemeData {
|
|
|
+ RelativeSchemeData {
|
|
|
username: base.username.clone(),
|
|
|
password: base.password.clone(),
|
|
|
host: base.host.clone(),
|
|
|
@@ -264,16 +263,16 @@ fn parse_relative_url<'a>(input: &'a str, scheme: String, scheme_type: SchemeTyp
|
|
|
},
|
|
|
'?' => {
|
|
|
let (query, fragment) = try!(parse_query_and_fragment(input, parser));
|
|
|
- Ok(Url { scheme: scheme, scheme_data: RelativeSchemeData(base.clone()),
|
|
|
+ Ok(Url { scheme: scheme, scheme_data: SchemeData::Relative(base.clone()),
|
|
|
query: query, fragment: fragment })
|
|
|
},
|
|
|
'#' => {
|
|
|
let fragment = Some(try!(parse_fragment(input.slice_from(1), parser)));
|
|
|
- Ok(Url { scheme: scheme, scheme_data: RelativeSchemeData(base.clone()),
|
|
|
+ Ok(Url { scheme: scheme, scheme_data: SchemeData::Relative(base.clone()),
|
|
|
query: base_query.clone(), fragment: fragment })
|
|
|
}
|
|
|
_ => {
|
|
|
- let (scheme_data, remaining) = if scheme_type == FileLikeRelativeScheme
|
|
|
+ let (scheme_data, remaining) = if scheme_type == SchemeType::FileLike
|
|
|
&& input.len() >= 2
|
|
|
&& starts_with_ascii_alpha(input)
|
|
|
&& is_match!(input.char_at(1), ':' | '|')
|
|
|
@@ -282,10 +281,10 @@ fn parse_relative_url<'a>(input: &'a str, scheme: String, scheme_type: SchemeTyp
|
|
|
{
|
|
|
// Windows drive letter quirk
|
|
|
let (path, remaining) = try!(parse_path(
|
|
|
- [], input, UrlParserContext, scheme_type, parser));
|
|
|
- (RelativeSchemeData(UrlRelativeSchemeData {
|
|
|
+ &[], input, Context::UrlParser, scheme_type, parser));
|
|
|
+ (SchemeData::Relative(RelativeSchemeData {
|
|
|
username: String::new(), password: None,
|
|
|
- host: Domain(String::new()),
|
|
|
+ host: Host::Domain(String::new()),
|
|
|
port: None,
|
|
|
default_port: None,
|
|
|
path: path
|
|
|
@@ -294,8 +293,8 @@ fn parse_relative_url<'a>(input: &'a str, scheme: String, scheme_type: SchemeTyp
|
|
|
let base_path = base.path.slice_to(base.path.len() - 1);
|
|
|
// Relative path state
|
|
|
let (path, remaining) = try!(parse_path(
|
|
|
- base_path, input, UrlParserContext, scheme_type, parser));
|
|
|
- (RelativeSchemeData(UrlRelativeSchemeData {
|
|
|
+ base_path, input, Context::UrlParser, scheme_type, parser));
|
|
|
+ (SchemeData::Relative(RelativeSchemeData {
|
|
|
username: base.username.clone(),
|
|
|
password: base.password.clone(),
|
|
|
host: base.host.clone(),
|
|
|
@@ -315,7 +314,7 @@ fn parse_relative_url<'a>(input: &'a str, scheme: String, scheme_type: SchemeTyp
|
|
|
fn skip_slashes<'a>(input: &'a str, parser: &UrlParser) -> ParseResult<&'a str> {
|
|
|
let first_non_slash = input.find(|c| !is_match!(c, '/' | '\\')).unwrap_or(input.len());
|
|
|
if input.slice_to(first_non_slash) != "//" {
|
|
|
- try!(parser.parse_error(ExpectedTwoSlashes));
|
|
|
+ try!(parser.parse_error(ParseError::ExpectedTwoSlashes));
|
|
|
}
|
|
|
Ok(input.slice_from(first_non_slash))
|
|
|
}
|
|
|
@@ -327,7 +326,9 @@ fn parse_userinfo<'a>(input: &'a str, parser: &UrlParser)
|
|
|
for (i, c) in input.char_indices() {
|
|
|
match c {
|
|
|
'@' => {
|
|
|
- if last_at.is_some() { try!(parser.parse_error(InvalidAtSymbolInUser)) }
|
|
|
+ if last_at.is_some() {
|
|
|
+ try!(parser.parse_error(ParseError::InvalidAtSymbolInUser))
|
|
|
+ }
|
|
|
last_at = Some(i)
|
|
|
},
|
|
|
'/' | '\\' | '?' | '#' => break,
|
|
|
@@ -347,7 +348,7 @@ fn parse_userinfo<'a>(input: &'a str, parser: &UrlParser)
|
|
|
password = Some(try!(parse_password(input.slice_from(i + 1), parser)));
|
|
|
break
|
|
|
},
|
|
|
- '\t' | '\n' | '\r' => try!(parser.parse_error(InvalidCharacter)),
|
|
|
+ '\t' | '\n' | '\r' => try!(parser.parse_error(ParseError::InvalidCharacter)),
|
|
|
_ => {
|
|
|
try!(check_url_code_point(input, i, c, parser));
|
|
|
// The spec says to use the default encode set,
|
|
|
@@ -365,7 +366,7 @@ fn parse_password(input: &str, parser: &UrlParser) -> ParseResult<String> {
|
|
|
let mut password = String::new();
|
|
|
for (i, c, next_i) in input.char_ranges() {
|
|
|
match c {
|
|
|
- '\t' | '\n' | '\r' => try!(parser.parse_error(InvalidCharacter)),
|
|
|
+ '\t' | '\n' | '\r' => try!(parser.parse_error(ParseError::InvalidCharacter)),
|
|
|
_ => {
|
|
|
try!(check_url_code_point(input, i, c, parser));
|
|
|
// The spec says to use the default encode set,
|
|
|
@@ -406,7 +407,7 @@ pub fn parse_hostname<'a>(input: &'a str, parser: &UrlParser)
|
|
|
end = i;
|
|
|
break
|
|
|
},
|
|
|
- '\t' | '\n' | '\r' => try!(parser.parse_error(InvalidCharacter)),
|
|
|
+ '\t' | '\n' | '\r' => try!(parser.parse_error(ParseError::InvalidCharacter)),
|
|
|
c => {
|
|
|
match c {
|
|
|
'[' => inside_square_brackets = true,
|
|
|
@@ -432,7 +433,7 @@ pub fn parse_port<'a>(input: &'a str, scheme_type: SchemeType, parser: &UrlParse
|
|
|
'0'...'9' => {
|
|
|
port = port * 10 + (c as u32 - '0' as u32);
|
|
|
if port > ::std::u16::MAX as u32 {
|
|
|
- return Err(InvalidPort)
|
|
|
+ return Err(ParseError::InvalidPort)
|
|
|
}
|
|
|
has_any_digit = true;
|
|
|
},
|
|
|
@@ -440,8 +441,8 @@ pub fn parse_port<'a>(input: &'a str, scheme_type: SchemeType, parser: &UrlParse
|
|
|
end = i;
|
|
|
break
|
|
|
},
|
|
|
- '\t' | '\n' | '\r' => try!(parser.parse_error(InvalidCharacter)),
|
|
|
- _ => return Err(InvalidPort)
|
|
|
+ '\t' | '\n' | '\r' => try!(parser.parse_error(ParseError::InvalidCharacter)),
|
|
|
+ _ => return Err(ParseError::InvalidPort)
|
|
|
}
|
|
|
}
|
|
|
let default_port = scheme_type.default_port();
|
|
|
@@ -462,12 +463,12 @@ fn parse_file_host<'a>(input: &'a str, parser: &UrlParser) -> ParseResult<(Host,
|
|
|
end = i;
|
|
|
break
|
|
|
},
|
|
|
- '\t' | '\n' | '\r' => try!(parser.parse_error(InvalidCharacter)),
|
|
|
+ '\t' | '\n' | '\r' => try!(parser.parse_error(ParseError::InvalidCharacter)),
|
|
|
_ => host_input.push(c)
|
|
|
}
|
|
|
}
|
|
|
let host = if host_input.is_empty() {
|
|
|
- Domain(String::new())
|
|
|
+ Host::Domain(String::new())
|
|
|
} else {
|
|
|
try!(Host::parse(host_input.as_slice()))
|
|
|
};
|
|
|
@@ -479,13 +480,13 @@ pub fn parse_standalone_path(input: &str, parser: &UrlParser)
|
|
|
-> ParseResult<(Vec<String>, Option<String>, Option<String>)> {
|
|
|
if !input.starts_with("/") {
|
|
|
if input.starts_with("\\") {
|
|
|
- try!(parser.parse_error(InvalidBackslash));
|
|
|
+ try!(parser.parse_error(ParseError::InvalidBackslash));
|
|
|
} else {
|
|
|
- return Err(ExpectedInitialSlash)
|
|
|
+ return Err(ParseError::ExpectedInitialSlash)
|
|
|
}
|
|
|
}
|
|
|
let (path, remaining) = try!(parse_path(
|
|
|
- [], input.slice_from(1), UrlParserContext, RelativeScheme(0), parser));
|
|
|
+ &[], input.slice_from(1), Context::UrlParser, SchemeType::Relative(0), parser));
|
|
|
let (query, fragment) = try!(parse_query_and_fragment(remaining, parser));
|
|
|
Ok((path, query, fragment))
|
|
|
}
|
|
|
@@ -500,13 +501,13 @@ pub fn parse_path_start<'a>(input: &'a str, context: Context, scheme_type: Schem
|
|
|
match input.char_at(0) {
|
|
|
'/' => i = 1,
|
|
|
'\\' => {
|
|
|
- try!(parser.parse_error(InvalidBackslash));
|
|
|
+ try!(parser.parse_error(ParseError::InvalidBackslash));
|
|
|
i = 1;
|
|
|
},
|
|
|
_ => ()
|
|
|
}
|
|
|
}
|
|
|
- parse_path([], input.slice_from(i), context, scheme_type, parser)
|
|
|
+ parse_path(&[], input.slice_from(i), context, scheme_type, parser)
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -529,16 +530,16 @@ fn parse_path<'a>(base_path: &[String], input: &'a str, context: Context,
|
|
|
break
|
|
|
},
|
|
|
'\\' => {
|
|
|
- try!(parser.parse_error(InvalidBackslash));
|
|
|
+ try!(parser.parse_error(ParseError::InvalidBackslash));
|
|
|
ends_with_slash = true;
|
|
|
end = i;
|
|
|
break
|
|
|
},
|
|
|
- '?' | '#' if context == UrlParserContext => {
|
|
|
+ '?' | '#' if context == Context::UrlParser => {
|
|
|
end = i;
|
|
|
break
|
|
|
},
|
|
|
- '\t' | '\n' | '\r' => try!(parser.parse_error(InvalidCharacter)),
|
|
|
+ '\t' | '\n' | '\r' => try!(parser.parse_error(ParseError::InvalidCharacter)),
|
|
|
_ => {
|
|
|
try!(check_url_code_point(input, i, c, parser));
|
|
|
utf8_percent_encode_to(input.slice(i, next_i),
|
|
|
@@ -560,7 +561,7 @@ fn parse_path<'a>(base_path: &[String], input: &'a str, context: Context,
|
|
|
}
|
|
|
},
|
|
|
_ => {
|
|
|
- if scheme_type == FileLikeRelativeScheme
|
|
|
+ if scheme_type == SchemeType::FileLike
|
|
|
&& path.is_empty()
|
|
|
&& path_part.len() == 2
|
|
|
&& starts_with_ascii_alpha(path_part.as_slice())
|
|
|
@@ -591,7 +592,7 @@ fn parse_scheme_data<'a>(input: &'a str, parser: &UrlParser)
|
|
|
end = i;
|
|
|
break
|
|
|
},
|
|
|
- '\t' | '\n' | '\r' => try!(parser.parse_error(InvalidCharacter)),
|
|
|
+ '\t' | '\n' | '\r' => try!(parser.parse_error(ParseError::InvalidCharacter)),
|
|
|
_ => {
|
|
|
try!(check_url_code_point(input, i, c, parser));
|
|
|
utf8_percent_encode_to(input.slice(i, next_i),
|
|
|
@@ -612,7 +613,7 @@ fn parse_query_and_fragment(input: &str, parser: &UrlParser)
|
|
|
'#' => Ok((None, Some(try!(parse_fragment(input.slice_from(1), parser))))),
|
|
|
'?' => {
|
|
|
let (query, remaining) = try!(parse_query(
|
|
|
- input.slice_from(1), UrlParserContext, parser));
|
|
|
+ input.slice_from(1), Context::UrlParser, parser));
|
|
|
let fragment = match remaining {
|
|
|
Some(remaining) => Some(try!(parse_fragment(remaining, parser))),
|
|
|
None => None
|
|
|
@@ -631,26 +632,20 @@ pub fn parse_query<'a>(input: &'a str, context: Context, parser: &UrlParser)
|
|
|
let mut remaining = None;
|
|
|
for (i, c) in input.char_indices() {
|
|
|
match c {
|
|
|
- '#' if context == UrlParserContext => {
|
|
|
+ '#' if context == Context::UrlParser => {
|
|
|
remaining = Some(input.slice_from(i + 1));
|
|
|
break
|
|
|
},
|
|
|
- '\t' | '\n' | '\r' => try!(parser.parse_error(InvalidCharacter)),
|
|
|
+ '\t' | '\n' | '\r' => try!(parser.parse_error(ParseError::InvalidCharacter)),
|
|
|
_ => {
|
|
|
try!(check_url_code_point(input, i, c, parser));
|
|
|
query.push(c);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- let encoded;
|
|
|
- let query_bytes = match parser.query_encoding_override {
|
|
|
- Some(encoding) => {
|
|
|
- encoded = encoding.encode(query.as_slice(), encoding::EncodeReplace).unwrap();
|
|
|
- encoded.as_slice()
|
|
|
- },
|
|
|
- None => query.as_bytes() // UTF-8
|
|
|
- };
|
|
|
- ;
|
|
|
+
|
|
|
+ let tmp = &mut vec![];
|
|
|
+ let query_bytes = parser.query_encoding_override.encode(query.as_slice(), tmp);
|
|
|
Ok((percent_encode(query_bytes.as_slice(), QUERY_ENCODE_SET), remaining))
|
|
|
}
|
|
|
|
|
|
@@ -659,7 +654,7 @@ pub fn parse_fragment<'a>(input: &'a str, parser: &UrlParser) -> ParseResult<Str
|
|
|
let mut fragment = String::new();
|
|
|
for (i, c, next_i) in input.char_ranges() {
|
|
|
match c {
|
|
|
- '\t' | '\n' | '\r' => try!(parser.parse_error(InvalidCharacter)),
|
|
|
+ '\t' | '\n' | '\r' => try!(parser.parse_error(ParseError::InvalidCharacter)),
|
|
|
_ => {
|
|
|
try!(check_url_code_point(input, i, c, parser));
|
|
|
utf8_percent_encode_to(input.slice(i, next_i),
|
|
|
@@ -760,10 +755,10 @@ fn check_url_code_point(input: &str, i: uint, c: char, parser: &UrlParser)
|
|
|
-> ParseResult<()> {
|
|
|
if c == '%' {
|
|
|
if !starts_with_2_hex(input.slice_from(i + 1)) {
|
|
|
- try!(parser.parse_error(InvalidPercentEncoded));
|
|
|
+ try!(parser.parse_error(ParseError::InvalidPercentEncoded));
|
|
|
}
|
|
|
} else if !is_url_code_point(c) {
|
|
|
- try!(parser.parse_error(NonUrlCodePoint));
|
|
|
+ try!(parser.parse_error(ParseError::NonUrlCodePoint));
|
|
|
}
|
|
|
Ok(())
|
|
|
}
|