1 // Copyright 2013-2015 The rust-url developers.
3 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6 // option. This file may not be copied, modified, or distributed
7 // except according to those terms.
11 rust-url is an implementation of the [URL Standard](http://url.spec.whatwg.org/)
12 for the [Rust](http://rust-lang.org/) programming language.
15 # URL parsing and data structures
17 First, URL parsing may fail for various reasons and therefore returns a `Result`.
20 use url::{Url, ParseError};
22 assert!(Url::parse("http://[:::1]") == Err(ParseError::InvalidIpv6Address))
25 Let’s parse a valid URL and look at its components.
29 # use url::ParseError;
30 # fn run() -> Result<(), ParseError> {
31 let issue_list_url = Url::parse(
32 "https://github.com/rust-lang/rust/issues?labels=E-easy&state=open"
36 assert!(issue_list_url.scheme() == "https");
37 assert!(issue_list_url.username() == "");
38 assert!(issue_list_url.password() == None);
39 assert!(issue_list_url.host_str() == Some("github.com"));
40 assert!(issue_list_url.host() == Some(Host::Domain("github.com")));
41 assert!(issue_list_url.port() == None);
42 assert!(issue_list_url.path() == "/rust-lang/rust/issues");
43 assert!(issue_list_url.path_segments().map(|c| c.collect::<Vec<_>>()) ==
44 Some(vec!["rust-lang", "rust", "issues"]));
45 assert!(issue_list_url.query() == Some("labels=E-easy&state=open"));
46 assert!(issue_list_url.fragment() == None);
47 assert!(!issue_list_url.cannot_be_a_base());
53 Some URLs are said to be *cannot-be-a-base*:
54 they don’t have a username, password, host, or port,
55 and their "path" is an arbitrary string rather than slash-separated segments:
59 # use url::ParseError;
61 # fn run() -> Result<(), ParseError> {
62 let data_url = Url::parse("data:text/plain,Hello?World#")?;
64 assert!(data_url.cannot_be_a_base());
65 assert!(data_url.scheme() == "data");
66 assert!(data_url.path() == "text/plain,Hello");
67 assert!(data_url.path_segments().is_none());
68 assert!(data_url.query() == Some("World"));
69 assert!(data_url.fragment() == Some(""));
78 Many contexts allow URL *references* that can be relative to a *base URL*:
81 <link rel="stylesheet" href="../main.css">
84 Since parsed URL are absolute, giving a base is required for parsing relative URLs:
87 use url::{Url, ParseError};
89 assert!(Url::parse("../main.css") == Err(ParseError::RelativeUrlWithoutBase))
92 Use the `join` method on an `Url` to use it as a base URL:
96 # use url::ParseError;
98 # fn run() -> Result<(), ParseError> {
99 let this_document = Url::parse("http://servo.github.io/rust-url/url/index.html")?;
100 let css_url = this_document.join("../main.css")?;
101 assert_eq!(css_url.as_str(), "http://servo.github.io/rust-url/main.css");
107 #![doc(html_root_url = "https://docs.rs/url/1.6.0")]
109 #[cfg(feature="rustc-serialize")] extern crate rustc_serialize;
110 #[macro_use] extern crate matches;
111 #[cfg(feature="serde")] extern crate serde;
112 #[cfg(feature="heapsize")] #[macro_use] extern crate heapsize;
114 pub extern crate idna
;
115 pub extern crate percent_encoding
;
117 use encoding
::EncodingOverride
;
118 #[cfg(feature = "heapsize")] use heapsize::HeapSizeOf;
119 use host
::HostInternal
;
120 use parser
::{Parser, Context, SchemeType, to_u32}
;
121 use percent_encoding
::{PATH_SEGMENT_ENCODE_SET
, USERINFO_ENCODE_SET
,
122 percent_encode
, percent_decode
, utf8_percent_encode
};
123 use std
::borrow
::Borrow
;
125 #[cfg(feature = "serde")] use std::error::Error;
126 use std
::fmt
::{self, Write, Debug, Formatter}
;
130 use std
::net
::{ToSocketAddrs, IpAddr}
;
131 use std
::ops
::{Range, RangeFrom, RangeTo}
;
132 use std
::path
::{Path, PathBuf}
;
135 pub use origin
::{Origin, OpaqueOrigin}
;
136 pub use host
::{Host, HostAndPort, SocketAddrs}
;
137 pub use path_segments
::PathSegmentsMut
;
138 pub use parser
::ParseError
;
139 pub use slicing
::Position
;
148 pub mod form_urlencoded
;
149 #[doc(hidden)] pub mod quirks;
151 /// A parsed URL record.
154 /// Syntax in pseudo-BNF:
156 /// url = scheme ":" [ hierarchical | non-hierarchical ] [ "?" query ]? [ "#" fragment ]?
157 /// non-hierarchical = non-hierarchical-path
158 /// non-hierarchical-path = /* Does not start with "/" */
159 /// hierarchical = authority? hierarchical-path
160 /// authority = "//" userinfo? host [ ":" port ]?
161 /// userinfo = username [ ":" password ]? "@"
162 /// hierarchical-path = [ "/" path-segment ]+
163 serialization
: String
,
166 scheme_end
: u32, // Before ':'
167 username_end
: u32, // Before ':' (if a password is given) or '@' (if not)
172 path_start
: u32, // Before initial '/', if any
173 query_start
: Option
<u32>, // Before '?', unlike Position::QueryStart
174 fragment_start
: Option
<u32>, // Before '#', unlike Position::FragmentStart
177 #[cfg(feature = "heapsize")]
178 impl HeapSizeOf
for Url
{
179 fn heap_size_of_children(&self) -> usize {
180 self.serialization
.heap_size_of_children()
184 /// Full configuration for the URL parser.
185 #[derive(Copy, Clone)]
186 pub struct ParseOptions
<'a
> {
187 base_url
: Option
<&'a Url
>,
188 encoding_override
: encoding
::EncodingOverride
,
189 log_syntax_violation
: Option
<&'a
Fn(&'
static str)>,
192 impl<'a
> ParseOptions
<'a
> {
193 /// Change the base URL
194 pub fn base_url(mut self, new
: Option
<&'a Url
>) -> Self {
199 /// Override the character encoding of query strings.
200 /// This is a legacy concept only relevant for HTML.
202 /// `EncodingRef` is defined in [rust-encoding](https://github.com/lifthrasiir/rust-encoding).
204 /// This method is only available if the `query_encoding`
205 /// [feature](http://doc.crates.io/manifest.html#the-features-section]) is enabled.
206 #[cfg(feature = "query_encoding")]
207 pub fn encoding_override(mut self, new
: Option
<encoding
::EncodingRef
>) -> Self {
208 self.encoding_override
= EncodingOverride
::from_opt_encoding(new
).to_output_encoding();
212 /// Call the provided function or closure on non-fatal parse errors.
213 pub fn log_syntax_violation(mut self, new
: Option
<&'a
Fn(&'
static str)>) -> Self {
214 self.log_syntax_violation
= new
;
218 /// Parse an URL string with the configuration so far.
219 pub fn parse(self, input
: &str) -> Result
<Url
, ::ParseError
> {
221 serialization
: String
::with_capacity(input
.len()),
222 base_url
: self.base_url
,
223 query_encoding_override
: self.encoding_override
,
224 log_syntax_violation
: self.log_syntax_violation
,
225 context
: Context
::UrlParser
,
230 impl<'a
> Debug
for ParseOptions
<'a
> {
231 fn fmt(&self, f
: &mut Formatter
) -> fmt
::Result
{
232 write
!(f
, "ParseOptions {{ base_url: {:?}, encoding_override: {:?}, log_syntax_violation: ", self.base_url
, self.encoding_override
)?
;
233 match self.log_syntax_violation
{
234 Some(_
) => write
!(f
, "Some(Fn(&'static str)) }}"),
235 None
=> write
!(f
, "None }}")
241 /// Parse an absolute URL from a string.
247 /// # use url::ParseError;
249 /// # fn run() -> Result<(), ParseError> {
250 /// let url = Url::parse("https://example.net")?;
253 /// # run().unwrap();
258 /// If the function can not parse an absolute URL from the given string,
259 /// a [`ParseError`] variant will be returned.
261 /// [`ParseError`]: enum.ParseError.html
263 pub fn parse(input
: &str) -> Result
<Url
, ::ParseError
> {
264 Url
::options().parse(input
)
267 /// Parse an absolute URL from a string and add params to its query string.
269 /// Existing params are not removed.
275 /// # use url::ParseError;
277 /// # fn run() -> Result<(), ParseError> {
278 /// let url = Url::parse_with_params("https://example.net?dont=clobberme",
279 /// &[("lang", "rust"), ("browser", "servo")])?;
282 /// # run().unwrap();
287 /// If the function can not parse an absolute URL from the given string,
288 /// a [`ParseError`] variant will be returned.
290 /// [`ParseError`]: enum.ParseError.html
292 pub fn parse_with_params
<I
, K
, V
>(input
: &str, iter
: I
) -> Result
<Url
, ::ParseError
>
293 where I
: IntoIterator
,
294 I
::Item
: Borrow
<(K
, V
)>,
298 let mut url
= Url
::options().parse(input
);
300 if let Ok(ref mut url
) = url
{
301 url
.query_pairs_mut().extend_pairs(iter
);
307 /// Parse a string as an URL, with this URL as the base URL.
309 /// Note: a trailing slash is significant.
310 /// Without it, the last path component is considered to be a “file” name
311 /// to be removed to get at the “directory” that is used as the base:
317 /// # use url::ParseError;
319 /// # fn run() -> Result<(), ParseError> {
320 /// let base = Url::parse("https://example.net/a/b.html")?;
321 /// let url = base.join("c.png")?;
322 /// assert_eq!(url.as_str(), "https://example.net/a/c.png"); // Not /a/b.html/c.png
324 /// let base = Url::parse("https://example.net/a/b/")?;
325 /// let url = base.join("c.png")?;
326 /// assert_eq!(url.as_str(), "https://example.net/a/b/c.png");
329 /// # run().unwrap();
334 /// If the function can not parse an URL from the given string
335 /// with this URL as the base URL, a [`ParseError`] variant will be returned.
337 /// [`ParseError`]: enum.ParseError.html
339 pub fn join(&self, input
: &str) -> Result
<Url
, ::ParseError
> {
340 Url
::options().base_url(Some(self)).parse(input
)
343 /// Return a default `ParseOptions` that can fully configure the URL parser.
347 /// Get default `ParseOptions`, then change base url
351 /// # use url::ParseError;
352 /// # fn run() -> Result<(), ParseError> {
353 /// let options = Url::options();
354 /// let api = Url::parse("https://api.example.com")?;
355 /// let base_url = options.base_url(Some(&api));
356 /// let version_url = base_url.parse("version.json")?;
357 /// assert_eq!(version_url.as_str(), "https://api.example.com/version.json");
360 /// # run().unwrap();
362 pub fn options
<'a
>() -> ParseOptions
<'a
> {
365 encoding_override
: EncodingOverride
::utf8(),
366 log_syntax_violation
: None
,
370 /// Return the serialization of this URL.
372 /// This is fast since that serialization is already stored in the `Url` struct.
378 /// # use url::ParseError;
380 /// # fn run() -> Result<(), ParseError> {
381 /// let url_str = "https://example.net/";
382 /// let url = Url::parse(url_str)?;
383 /// assert_eq!(url.as_str(), url_str);
386 /// # run().unwrap();
389 pub fn as_str(&self) -> &str {
393 /// Return the serialization of this URL.
395 /// This consumes the `Url` and takes ownership of the `String` stored in it.
401 /// # use url::ParseError;
403 /// # fn run() -> Result<(), ParseError> {
404 /// let url_str = "https://example.net/";
405 /// let url = Url::parse(url_str)?;
406 /// assert_eq!(url.into_string(), url_str);
409 /// # run().unwrap();
412 pub fn into_string(self) -> String
{
416 /// For internal testing, not part of the public API.
418 /// Methods of the `Url` struct assume a number of invariants.
419 /// This checks each of these invariants and panic if one is not met.
420 /// This is for testing rust-url itself.
422 pub fn check_invariants(&self) -> Result
<(), String
> {
423 macro_rules
! assert
{
426 return Err(format
!("!( {} ) for URL {:?}",
427 stringify
!($x
), self.serialization
))
432 macro_rules
! assert_eq
{
433 ($a
: expr
, $b
: expr
) => {
438 return Err(format
!("{:?} != {:?} ({} != {}) for URL {:?}",
439 a
, b
, stringify
!($a
), stringify
!($b
),
446 assert
!(self.scheme_end
>= 1);
447 assert
!(matches
!(self.byte_at(0), b'a'
...b'z'
| b'A'
...b'Z'
));
448 assert
!(self.slice(1..self.scheme_end
).chars()
449 .all(|c
| matches
!(c
, 'a'
...'z'
| 'A'
...'Z'
| '
0'
...'
9'
| '
+'
| '
-'
| '
.'
)));
450 assert_eq
!(self.byte_at(self.scheme_end
), b'
:'
);
452 if self.slice(self.scheme_end
+ 1 ..).starts_with("//") {
453 // URL with authority
454 match self.byte_at(self.username_end
) {
456 assert
!(self.host_start
>= self.username_end
+ 2);
457 assert_eq
!(self.byte_at(self.host_start
- 1), b'@'
);
459 b'@'
=> assert
!(self.host_start
== self.username_end
+ 1),
460 _
=> assert_eq
!(self.username_end
, self.scheme_end
+ 3),
462 assert
!(self.host_start
>= self.username_end
);
463 assert
!(self.host_end
>= self.host_start
);
464 let host_str
= self.slice(self.host_start
..self.host_end
);
466 HostInternal
::None
=> assert_eq
!(host_str
, ""),
467 HostInternal
::Ipv4(address
) => assert_eq
!(host_str
, address
.to_string()),
468 HostInternal
::Ipv6(address
) => {
469 let h
: Host
<String
> = Host
::Ipv6(address
);
470 assert_eq
!(host_str
, h
.to_string())
472 HostInternal
::Domain
=> {
473 if SchemeType
::from(self.scheme()).is_special() {
474 assert
!(!host_str
.is_empty())
478 if self.path_start
== self.host_end
{
479 assert_eq
!(self.port
, None
);
481 assert_eq
!(self.byte_at(self.host_end
), b'
:'
);
482 let port_str
= self.slice(self.host_end
+ 1..self.path_start
);
483 assert_eq
!(self.port
, Some(port_str
.parse
::<u16>().expect("Couldn't parse port?")));
485 assert_eq
!(self.byte_at(self.path_start
), b'
/'
);
487 // Anarchist URL (no authority)
488 assert_eq
!(self.username_end
, self.scheme_end
+ 1);
489 assert_eq
!(self.host_start
, self.scheme_end
+ 1);
490 assert_eq
!(self.host_end
, self.scheme_end
+ 1);
491 assert_eq
!(self.host
, HostInternal
::None
);
492 assert_eq
!(self.port
, None
);
493 assert_eq
!(self.path_start
, self.scheme_end
+ 1);
495 if let Some(start
) = self.query_start
{
496 assert
!(start
> self.path_start
);
497 assert_eq
!(self.byte_at(start
), b'?'
);
499 if let Some(start
) = self.fragment_start
{
500 assert
!(start
> self.path_start
);
501 assert_eq
!(self.byte_at(start
), b'
#');
503 if let (Some(query_start
), Some(fragment_start
)) = (self.query_start
, self.fragment_start
) {
504 assert
!(fragment_start
> query_start
);
507 let other
= Url
::parse(self.as_str()).expect("Failed to parse myself?");
508 assert_eq
!(&self.serialization
, &other
.serialization
);
509 assert_eq
!(self.scheme_end
, other
.scheme_end
);
510 assert_eq
!(self.username_end
, other
.username_end
);
511 assert_eq
!(self.host_start
, other
.host_start
);
512 assert_eq
!(self.host_end
, other
.host_end
);
513 assert
!(self.host
== other
.host
||
514 // XXX No host round-trips to empty host.
515 // See https://github.com/whatwg/url/issues/79
516 (self.host_str(), other
.host_str()) == (None
, Some("")));
517 assert_eq
!(self.port
, other
.port
);
518 assert_eq
!(self.path_start
, other
.path_start
);
519 assert_eq
!(self.query_start
, other
.query_start
);
520 assert_eq
!(self.fragment_start
, other
.fragment_start
);
524 /// Return the origin of this URL (<https://url.spec.whatwg.org/#origin>)
526 /// Note: this returns an opaque origin for `file:` URLs, which causes
527 /// `url.origin() != url.origin()`.
531 /// URL with `ftp` scheme:
534 /// use url::{Host, Origin, Url};
535 /// # use url::ParseError;
537 /// # fn run() -> Result<(), ParseError> {
538 /// let url = Url::parse("ftp://example.com/foo")?;
539 /// assert_eq!(url.origin(),
540 /// Origin::Tuple("ftp".into(),
541 /// Host::Domain("example.com".into()),
545 /// # run().unwrap();
548 /// URL with `blob` scheme:
551 /// use url::{Host, Origin, Url};
552 /// # use url::ParseError;
554 /// # fn run() -> Result<(), ParseError> {
555 /// let url = Url::parse("blob:https://example.com/foo")?;
556 /// assert_eq!(url.origin(),
557 /// Origin::Tuple("https".into(),
558 /// Host::Domain("example.com".into()),
562 /// # run().unwrap();
565 /// URL with `file` scheme:
568 /// use url::{Host, Origin, Url};
569 /// # use url::ParseError;
571 /// # fn run() -> Result<(), ParseError> {
572 /// let url = Url::parse("file:///tmp/foo")?;
573 /// assert!(!url.origin().is_tuple());
575 /// let other_url = Url::parse("file:///tmp/foo")?;
576 /// assert!(url.origin() != other_url.origin());
579 /// # run().unwrap();
582 /// URL with other scheme:
585 /// use url::{Host, Origin, Url};
586 /// # use url::ParseError;
588 /// # fn run() -> Result<(), ParseError> {
589 /// let url = Url::parse("foo:bar")?;
590 /// assert!(!url.origin().is_tuple());
593 /// # run().unwrap();
596 pub fn origin(&self) -> Origin
{
597 origin
::url_origin(self)
600 /// Return the scheme of this URL, lower-cased, as an ASCII string without the ':' delimiter.
606 /// # use url::ParseError;
608 /// # fn run() -> Result<(), ParseError> {
609 /// let url = Url::parse("file:///tmp/foo")?;
610 /// assert_eq!(url.scheme(), "file");
613 /// # run().unwrap();
616 pub fn scheme(&self) -> &str {
617 self.slice(..self.scheme_end
)
620 /// Return whether the URL has an 'authority',
621 /// which can contain a username, password, host, and port number.
623 /// URLs that do *not* are either path-only like `unix:/run/foo.socket`
624 /// or cannot-be-a-base like `data:text/plain,Stuff`.
630 /// # use url::ParseError;
632 /// # fn run() -> Result<(), ParseError> {
633 /// let url = Url::parse("ftp://rms@example.com")?;
634 /// assert!(url.has_authority());
636 /// let url = Url::parse("unix:/run/foo.socket")?;
637 /// assert!(!url.has_authority());
639 /// let url = Url::parse("data:text/plain,Stuff")?;
640 /// assert!(!url.has_authority());
643 /// # run().unwrap();
646 pub fn has_authority(&self) -> bool
{
647 debug_assert
!(self.byte_at(self.scheme_end
) == b'
:'
);
648 self.slice(self.scheme_end
..).starts_with("://")
651 /// Return whether this URL is a cannot-be-a-base URL,
652 /// meaning that parsing a relative URL string with this URL as the base will return an error.
654 /// This is the case if the scheme and `:` delimiter are not followed by a `/` slash,
655 /// as is typically the case of `data:` and `mailto:` URLs.
661 /// # use url::ParseError;
663 /// # fn run() -> Result<(), ParseError> {
664 /// let url = Url::parse("ftp://rms@example.com")?;
665 /// assert!(!url.cannot_be_a_base());
667 /// let url = Url::parse("unix:/run/foo.socket")?;
668 /// assert!(!url.cannot_be_a_base());
670 /// let url = Url::parse("data:text/plain,Stuff")?;
671 /// assert!(url.cannot_be_a_base());
674 /// # run().unwrap();
677 pub fn cannot_be_a_base(&self) -> bool
{
678 !self.slice(self.path_start
..).starts_with('
/'
)
681 /// Return the username for this URL (typically the empty string)
682 /// as a percent-encoded ASCII string.
688 /// # use url::ParseError;
690 /// # fn run() -> Result<(), ParseError> {
691 /// let url = Url::parse("ftp://rms@example.com")?;
692 /// assert_eq!(url.username(), "rms");
694 /// let url = Url::parse("ftp://:secret123@example.com")?;
695 /// assert_eq!(url.username(), "");
697 /// let url = Url::parse("https://example.com")?;
698 /// assert_eq!(url.username(), "");
701 /// # run().unwrap();
703 pub fn username(&self) -> &str {
704 if self.has_authority() {
705 self.slice(self.scheme_end
+ ("://".len() as u32)..self.username_end
)
711 /// Return the password for this URL, if any, as a percent-encoded ASCII string.
717 /// # use url::ParseError;
719 /// # fn run() -> Result<(), ParseError> {
720 /// let url = Url::parse("ftp://rms:secret123@example.com")?;
721 /// assert_eq!(url.password(), Some("secret123"));
723 /// let url = Url::parse("ftp://:secret123@example.com")?;
724 /// assert_eq!(url.password(), Some("secret123"));
726 /// let url = Url::parse("ftp://rms@example.com")?;
727 /// assert_eq!(url.password(), None);
729 /// let url = Url::parse("https://example.com")?;
730 /// assert_eq!(url.password(), None);
733 /// # run().unwrap();
735 pub fn password(&self) -> Option
<&str> {
736 // This ':' is not the one marking a port number since a host can not be empty.
737 // (Except for file: URLs, which do not have port numbers.)
738 if self.has_authority() && self.byte_at(self.username_end
) == b'
:'
{
739 debug_assert
!(self.byte_at(self.host_start
- 1) == b'@'
);
740 Some(self.slice(self.username_end
+ 1..self.host_start
- 1))
746 /// Equivalent to `url.host().is_some()`.
752 /// # use url::ParseError;
754 /// # fn run() -> Result<(), ParseError> {
755 /// let url = Url::parse("ftp://rms@example.com")?;
756 /// assert!(url.has_host());
758 /// let url = Url::parse("unix:/run/foo.socket")?;
759 /// assert!(!url.has_host());
761 /// let url = Url::parse("data:text/plain,Stuff")?;
762 /// assert!(!url.has_host());
765 /// # run().unwrap();
767 pub fn has_host(&self) -> bool
{
768 !matches
!(self.host
, HostInternal
::None
)
771 /// Return the string representation of the host (domain or IP address) for this URL, if any.
773 /// Non-ASCII domains are punycode-encoded per IDNA.
774 /// IPv6 addresses are given between `[` and `]` brackets.
776 /// Cannot-be-a-base URLs (typical of `data:` and `mailto:`) and some `file:` URLs
777 /// don’t have a host.
779 /// See also the `host` method.
785 /// # use url::ParseError;
787 /// # fn run() -> Result<(), ParseError> {
788 /// let url = Url::parse("https://127.0.0.1/index.html")?;
789 /// assert_eq!(url.host_str(), Some("127.0.0.1"));
791 /// let url = Url::parse("ftp://rms@example.com")?;
792 /// assert_eq!(url.host_str(), Some("example.com"));
794 /// let url = Url::parse("unix:/run/foo.socket")?;
795 /// assert_eq!(url.host_str(), None);
797 /// let url = Url::parse("data:text/plain,Stuff")?;
798 /// assert_eq!(url.host_str(), None);
801 /// # run().unwrap();
803 pub fn host_str(&self) -> Option
<&str> {
805 Some(self.slice(self.host_start
..self.host_end
))
811 /// Return the parsed representation of the host for this URL.
812 /// Non-ASCII domain labels are punycode-encoded per IDNA.
814 /// Cannot-be-a-base URLs (typical of `data:` and `mailto:`) and some `file:` URLs
815 /// don’t have a host.
817 /// See also the `host_str` method.
823 /// # use url::ParseError;
825 /// # fn run() -> Result<(), ParseError> {
826 /// let url = Url::parse("https://127.0.0.1/index.html")?;
827 /// assert!(url.host().is_some());
829 /// let url = Url::parse("ftp://rms@example.com")?;
830 /// assert!(url.host().is_some());
832 /// let url = Url::parse("unix:/run/foo.socket")?;
833 /// assert!(url.host().is_none());
835 /// let url = Url::parse("data:text/plain,Stuff")?;
836 /// assert!(url.host().is_none());
839 /// # run().unwrap();
841 pub fn host(&self) -> Option
<Host
<&str>> {
843 HostInternal
::None
=> None
,
844 HostInternal
::Domain
=> Some(Host
::Domain(self.slice(self.host_start
..self.host_end
))),
845 HostInternal
::Ipv4(address
) => Some(Host
::Ipv4(address
)),
846 HostInternal
::Ipv6(address
) => Some(Host
::Ipv6(address
)),
850 /// If this URL has a host and it is a domain name (not an IP address), return it.
856 /// # use url::ParseError;
858 /// # fn run() -> Result<(), ParseError> {
859 /// let url = Url::parse("https://127.0.0.1/")?;
860 /// assert_eq!(url.domain(), None);
862 /// let url = Url::parse("mailto:rms@example.net")?;
863 /// assert_eq!(url.domain(), None);
865 /// let url = Url::parse("https://example.com/")?;
866 /// assert_eq!(url.domain(), Some("example.com"));
869 /// # run().unwrap();
871 pub fn domain(&self) -> Option
<&str> {
873 HostInternal
::Domain
=> Some(self.slice(self.host_start
..self.host_end
)),
878 /// Return the port number for this URL, if any.
884 /// # use url::ParseError;
886 /// # fn run() -> Result<(), ParseError> {
887 /// let url = Url::parse("https://example.com")?;
888 /// assert_eq!(url.port(), None);
890 /// let url = Url::parse("ssh://example.com:22")?;
891 /// assert_eq!(url.port(), Some(22));
894 /// # run().unwrap();
897 pub fn port(&self) -> Option
<u16> {
901 /// Return the port number for this URL, or the default port number if it is known.
903 /// This method only knows the default port number
904 /// of the `http`, `https`, `ws`, `wss`, `ftp`, and `gopher` schemes.
906 /// For URLs in these schemes, this method always returns `Some(_)`.
907 /// For other schemes, it is the same as `Url::port()`.
913 /// # use url::ParseError;
915 /// # fn run() -> Result<(), ParseError> {
916 /// let url = Url::parse("foo://example.com")?;
917 /// assert_eq!(url.port_or_known_default(), None);
919 /// let url = Url::parse("foo://example.com:1456")?;
920 /// assert_eq!(url.port_or_known_default(), Some(1456));
922 /// let url = Url::parse("https://example.com")?;
923 /// assert_eq!(url.port_or_known_default(), Some(443));
926 /// # run().unwrap();
929 pub fn port_or_known_default(&self) -> Option
<u16> {
930 self.port
.or_else(|| parser
::default_port(self.scheme()))
933 /// If the URL has a host, return something that implements `ToSocketAddrs`.
935 /// If the URL has no port number and the scheme’s default port number is not known
936 /// (see `Url::port_or_known_default`),
937 /// the closure is called to obtain a port number.
938 /// Typically, this closure can match on the result `Url::scheme`
939 /// to have per-scheme default port numbers,
940 /// and panic for schemes it’s not prepared to handle.
945 /// # use std::net::TcpStream;
947 /// fn connect(url: &Url) -> io::Result<TcpStream> {
948 /// TcpStream::connect(url.with_default_port(default_port)?)
951 /// fn default_port(url: &Url) -> Result<u16, ()> {
952 /// match url.scheme() {
953 /// "git" => Ok(9418),
954 /// "git+ssh" => Ok(22),
955 /// "git+https" => Ok(443),
956 /// "git+http" => Ok(80),
961 pub fn with_default_port
<F
>(&self, f
: F
) -> io
::Result
<HostAndPort
<&str>>
962 where F
: FnOnce(&Url
) -> Result
<u16, ()> {
966 .or_else(|()| io_error("URL has no host"))?
,
967 port
: self.port_or_known_default()
969 .or_else(|()| f(self))
970 .or_else(|()| io_error("URL has no port number"))?
974 /// Return the path for this URL, as a percent-encoded ASCII string.
975 /// For cannot-be-a-base URLs, this is an arbitrary string that doesn’t start with '/'.
976 /// For other URLs, this starts with a '/' slash
977 /// and continues with slash-separated path segments.
982 /// use url::{Url, ParseError};
984 /// # fn run() -> Result<(), ParseError> {
985 /// let url = Url::parse("https://example.com/api/versions?page=2")?;
986 /// assert_eq!(url.path(), "/api/versions");
988 /// let url = Url::parse("https://example.com")?;
989 /// assert_eq!(url.path(), "/");
991 /// let url = Url::parse("https://example.com/countries/việt nam")?;
992 /// assert_eq!(url.path(), "/countries/vi%E1%BB%87t%20nam");
995 /// # run().unwrap();
997 pub fn path(&self) -> &str {
998 match (self.query_start
, self.fragment_start
) {
999 (None
, None
) => self.slice(self.path_start
..),
1000 (Some(next_component_start
), _
) |
1001 (None
, Some(next_component_start
)) => {
1002 self.slice(self.path_start
..next_component_start
)
1007 /// Unless this URL is cannot-be-a-base,
1008 /// return an iterator of '/' slash-separated path segments,
1009 /// each as a percent-encoded ASCII string.
1011 /// Return `None` for cannot-be-a-base URLs.
1013 /// When `Some` is returned, the iterator always contains at least one string
1014 /// (which may be empty).
1020 /// # use std::error::Error;
1022 /// # fn run() -> Result<(), Box<Error>> {
1023 /// let url = Url::parse("https://example.com/foo/bar")?;
1024 /// let mut path_segments = url.path_segments().ok_or_else(|| "cannot be base")?;
1025 /// assert_eq!(path_segments.next(), Some("foo"));
1026 /// assert_eq!(path_segments.next(), Some("bar"));
1027 /// assert_eq!(path_segments.next(), None);
1029 /// let url = Url::parse("https://example.com")?;
1030 /// let mut path_segments = url.path_segments().ok_or_else(|| "cannot be base")?;
1031 /// assert_eq!(path_segments.next(), Some(""));
1032 /// assert_eq!(path_segments.next(), None);
1034 /// let url = Url::parse("data:text/plain,HelloWorld")?;
1035 /// assert!(url.path_segments().is_none());
1037 /// let url = Url::parse("https://example.com/countries/việt nam")?;
1038 /// let mut path_segments = url.path_segments().ok_or_else(|| "cannot be base")?;
1039 /// assert_eq!(path_segments.next(), Some("countries"));
1040 /// assert_eq!(path_segments.next(), Some("vi%E1%BB%87t%20nam"));
1043 /// # run().unwrap();
1045 pub fn path_segments(&self) -> Option
<str::Split
<char>> {
1046 let path
= self.path();
1047 if path
.starts_with('
/'
) {
1048 Some(path
[1..].split('
/'
))
1054 /// Return this URL’s query string, if any, as a percent-encoded ASCII string.
1060 /// # use url::ParseError;
1062 /// fn run() -> Result<(), ParseError> {
1063 /// let url = Url::parse("https://example.com/products?page=2")?;
1064 /// let query = url.query();
1065 /// assert_eq!(query, Some("page=2"));
1067 /// let url = Url::parse("https://example.com/products")?;
1068 /// let query = url.query();
1069 /// assert!(query.is_none());
1071 /// let url = Url::parse("https://example.com/?country=español")?;
1072 /// let query = url.query();
1073 /// assert_eq!(query, Some("country=espa%C3%B1ol"));
1076 /// # run().unwrap();
1078 pub fn query(&self) -> Option
<&str> {
1079 match (self.query_start
, self.fragment_start
) {
1081 (Some(query_start
), None
) => {
1082 debug_assert
!(self.byte_at(query_start
) == b'?'
);
1083 Some(self.slice(query_start
+ 1..))
1085 (Some(query_start
), Some(fragment_start
)) => {
1086 debug_assert
!(self.byte_at(query_start
) == b'?'
);
1087 Some(self.slice(query_start
+ 1..fragment_start
))
1092 /// Parse the URL’s query string, if any, as `application/x-www-form-urlencoded`
1093 /// and return an iterator of (key, value) pairs.
1098 /// use std::borrow::Cow;
1101 /// # use url::ParseError;
1103 /// # fn run() -> Result<(), ParseError> {
1104 /// let url = Url::parse("https://example.com/products?page=2&sort=desc")?;
1105 /// let mut pairs = url.query_pairs();
1107 /// assert_eq!(pairs.count(), 2);
1109 /// assert_eq!(pairs.next(), Some((Cow::Borrowed("page"), Cow::Borrowed("2"))));
1110 /// assert_eq!(pairs.next(), Some((Cow::Borrowed("sort"), Cow::Borrowed("desc"))));
1113 /// # run().unwrap();
1117 pub fn query_pairs(&self) -> form_urlencoded
::Parse
{
1118 form_urlencoded
::parse(self.query().unwrap_or("").as_bytes())
1121 /// Return this URL’s fragment identifier, if any.
1123 /// A fragment is the part of the URL after the `#` symbol.
1124 /// The fragment is optional and, if present, contains a fragment identifier
1125 /// that identifies a secondary resource, such as a section heading
1128 /// In HTML, the fragment identifier is usually the id attribute of a an element
1129 /// that is scrolled to on load. Browsers typically will not send the fragment portion
1130 /// of a URL to the server.
1132 /// **Note:** the parser did *not* percent-encode this component,
1133 /// but the input may have been percent-encoded already.
1139 /// # use url::ParseError;
1141 /// # fn run() -> Result<(), ParseError> {
1142 /// let url = Url::parse("https://example.com/data.csv#row=4")?;
1144 /// assert_eq!(url.fragment(), Some("row=4"));
1146 /// let url = Url::parse("https://example.com/data.csv#cell=4,1-6,2")?;
1148 /// assert_eq!(url.fragment(), Some("cell=4,1-6,2"));
1151 /// # run().unwrap();
1153 pub fn fragment(&self) -> Option
<&str> {
1154 self.fragment_start
.map(|start
| {
1155 debug_assert
!(self.byte_at(start
) == b'
#');
1156 self.slice(start
+ 1..)
1160 fn mutate
<F
: FnOnce(&mut Parser
) -> R
, R
>(&mut self, f
: F
) -> R
{
1161 let mut parser
= Parser
::for_setter(mem
::replace(&mut self.serialization
, String
::new()));
1162 let result
= f(&mut parser
);
1163 self.serialization
= parser
.serialization
;
1167 /// Change this URL’s fragment identifier.
1173 /// # use url::ParseError;
1175 /// # fn run() -> Result<(), ParseError> {
1176 /// let mut url = Url::parse("https://example.com/data.csv")?;
1177 /// assert_eq!(url.as_str(), "https://example.com/data.csv");
1179 /// url.set_fragment(Some("cell=4,1-6,2"));
1180 /// assert_eq!(url.as_str(), "https://example.com/data.csv#cell=4,1-6,2");
1181 /// assert_eq!(url.fragment(), Some("cell=4,1-6,2"));
1183 /// url.set_fragment(None);
1184 /// assert_eq!(url.as_str(), "https://example.com/data.csv");
1185 /// assert!(url.fragment().is_none());
1188 /// # run().unwrap();
1190 pub fn set_fragment(&mut self, fragment
: Option
<&str>) {
1191 // Remove any previous fragment
1192 if let Some(start
) = self.fragment_start
{
1193 debug_assert
!(self.byte_at(start
) == b'
#');
1194 self.serialization
.truncate(start
as usize);
1196 // Write the new one
1197 if let Some(input
) = fragment
{
1198 self.fragment_start
= Some(to_u32(self.serialization
.len()).unwrap());
1199 self.serialization
.push('
#');
1200 self.mutate(|parser
| parser
.parse_fragment(parser
::Input
::new(input
)))
1202 self.fragment_start
= None
1206 fn take_fragment(&mut self) -> Option
<String
> {
1207 self.fragment_start
.take().map(|start
| {
1208 debug_assert
!(self.byte_at(start
) == b'
#');
1209 let fragment
= self.slice(start
+ 1..).to_owned();
1210 self.serialization
.truncate(start
as usize);
1215 fn restore_already_parsed_fragment(&mut self, fragment
: Option
<String
>) {
1216 if let Some(ref fragment
) = fragment
{
1217 assert
!(self.fragment_start
.is_none());
1218 self.fragment_start
= Some(to_u32(self.serialization
.len()).unwrap());
1219 self.serialization
.push('
#');
1220 self.serialization
.push_str(fragment
);
1224 /// Change this URL’s query string.
1230 /// # use url::ParseError;
1232 /// # fn run() -> Result<(), ParseError> {
1233 /// let mut url = Url::parse("https://example.com/products")?;
1234 /// assert_eq!(url.as_str(), "https://example.com/products");
1236 /// url.set_query(Some("page=2"));
1237 /// assert_eq!(url.as_str(), "https://example.com/products?page=2");
1238 /// assert_eq!(url.query(), Some("page=2"));
1241 /// # run().unwrap();
1243 pub fn set_query(&mut self, query
: Option
<&str>) {
1244 let fragment
= self.take_fragment();
1246 // Remove any previous query
1247 if let Some(start
) = self.query_start
.take() {
1248 debug_assert
!(self.byte_at(start
) == b'?'
);
1249 self.serialization
.truncate(start
as usize);
1251 // Write the new query, if any
1252 if let Some(input
) = query
{
1253 self.query_start
= Some(to_u32(self.serialization
.len()).unwrap());
1254 self.serialization
.push('?'
);
1255 let scheme_end
= self.scheme_end
;
1256 self.mutate(|parser
| parser
.parse_query(scheme_end
, parser
::Input
::new(input
)));
1259 self.restore_already_parsed_fragment(fragment
);
1262 /// Manipulate this URL’s query string, viewed as a sequence of name/value pairs
1263 /// in `application/x-www-form-urlencoded` syntax.
1265 /// The return value has a method-chaining API:
1268 /// # use url::{Url, ParseError};
1270 /// # fn run() -> Result<(), ParseError> {
1271 /// let mut url = Url::parse("https://example.net?lang=fr#nav")?;
1272 /// assert_eq!(url.query(), Some("lang=fr"));
1274 /// url.query_pairs_mut().append_pair("foo", "bar");
1275 /// assert_eq!(url.query(), Some("lang=fr&foo=bar"));
1276 /// assert_eq!(url.as_str(), "https://example.net/?lang=fr&foo=bar#nav");
1278 /// url.query_pairs_mut()
1280 /// .append_pair("foo", "bar & baz")
1281 /// .append_pair("saisons", "\u{00C9}t\u{00E9}+hiver");
1282 /// assert_eq!(url.query(), Some("foo=bar+%26+baz&saisons=%C3%89t%C3%A9%2Bhiver"));
1283 /// assert_eq!(url.as_str(),
1284 /// "https://example.net/?foo=bar+%26+baz&saisons=%C3%89t%C3%A9%2Bhiver#nav");
1287 /// # run().unwrap();
1290 /// Note: `url.query_pairs_mut().clear();` is equivalent to `url.set_query(Some(""))`,
1291 /// not `url.set_query(None)`.
1293 /// The state of `Url` is unspecified if this return value is leaked without being dropped.
1294 pub fn query_pairs_mut(&mut self) -> form_urlencoded
::Serializer
<UrlQuery
> {
1295 let fragment
= self.take_fragment();
1298 if let Some(start
) = self.query_start
{
1299 debug_assert
!(self.byte_at(start
) == b'?'
);
1300 query_start
= start
as usize;
1302 query_start
= self.serialization
.len();
1303 self.query_start
= Some(to_u32(query_start
).unwrap());
1304 self.serialization
.push('?'
);
1307 let query
= UrlQuery { url: self, fragment: fragment }
;
1308 form_urlencoded
::Serializer
::for_suffix(query
, query_start
+ "?".len())
1311 fn take_after_path(&mut self) -> String
{
1312 match (self.query_start
, self.fragment_start
) {
1313 (Some(i
), _
) | (None
, Some(i
)) => {
1314 let after_path
= self.slice(i
..).to_owned();
1315 self.serialization
.truncate(i
as usize);
1318 (None
, None
) => String
::new(),
1322 /// Change this URL’s path.
1328 /// # use url::ParseError;
1330 /// # fn run() -> Result<(), ParseError> {
1331 /// let mut url = Url::parse("https://example.com")?;
1332 /// url.set_path("api/comments");
1333 /// assert_eq!(url.as_str(), "https://example.com/api/comments");
1334 /// assert_eq!(url.path(), "/api/comments");
1336 /// let mut url = Url::parse("https://example.com/api")?;
1337 /// url.set_path("data/report.csv");
1338 /// assert_eq!(url.as_str(), "https://example.com/data/report.csv");
1339 /// assert_eq!(url.path(), "/data/report.csv");
1342 /// # run().unwrap();
1344 pub fn set_path(&mut self, mut path
: &str) {
1345 let after_path
= self.take_after_path();
1346 let old_after_path_pos
= to_u32(self.serialization
.len()).unwrap();
1347 let cannot_be_a_base
= self.cannot_be_a_base();
1348 let scheme_type
= SchemeType
::from(self.scheme());
1349 self.serialization
.truncate(self.path_start
as usize);
1350 self.mutate(|parser
| {
1351 if cannot_be_a_base
{
1352 if path
.starts_with('
/'
) {
1353 parser
.serialization
.push_str("%2F");
1356 parser
.parse_cannot_be_a_base_path(parser
::Input
::new(path
));
1358 let mut has_host
= true; // FIXME
1359 parser
.parse_path_start(scheme_type
, &mut has_host
, parser
::Input
::new(path
));
1362 self.restore_after_path(old_after_path_pos
, &after_path
);
1365 /// Return an object with methods to manipulate this URL’s path segments.
1367 /// Return `Err(())` if this URL is cannot-be-a-base.
1368 pub fn path_segments_mut(&mut self) -> Result
<PathSegmentsMut
, ()> {
1369 if self.cannot_be_a_base() {
1372 Ok(path_segments
::new(self))
1376 fn restore_after_path(&mut self, old_after_path_position
: u32, after_path
: &str) {
1377 let new_after_path_position
= to_u32(self.serialization
.len()).unwrap();
1378 let adjust
= |index
: &mut u32| {
1379 *index
-= old_after_path_position
;
1380 *index
+= new_after_path_position
;
1382 if let Some(ref mut index
) = self.query_start { adjust(index) }
1383 if let Some(ref mut index
) = self.fragment_start { adjust(index) }
1384 self.serialization
.push_str(after_path
)
1387 /// Change this URL’s port number.
1389 /// If this URL is cannot-be-a-base, does not have a host, or has the `file` scheme;
1390 /// do nothing and return `Err`.
1396 /// # use std::error::Error;
1398 /// # fn run() -> Result<(), Box<Error>> {
1399 /// let mut url = Url::parse("ssh://example.net:2048/")?;
1401 /// url.set_port(Some(4096)).map_err(|_| "cannot be base")?;
1402 /// assert_eq!(url.as_str(), "ssh://example.net:4096/");
1404 /// url.set_port(None).map_err(|_| "cannot be base")?;
1405 /// assert_eq!(url.as_str(), "ssh://example.net/");
1408 /// # run().unwrap();
1411 /// Cannot set port for cannot-be-a-base URLs:
1415 /// # use url::ParseError;
1417 /// # fn run() -> Result<(), ParseError> {
1418 /// let mut url = Url::parse("mailto:rms@example.net")?;
1420 /// let result = url.set_port(Some(80));
1421 /// assert!(result.is_err());
1423 /// let result = url.set_port(None);
1424 /// assert!(result.is_err());
1427 /// # run().unwrap();
1429 pub fn set_port(&mut self, mut port
: Option
<u16>) -> Result
<(), ()> {
1430 if !self.has_host() || self.scheme() == "file" {
1433 if port
.is_some() && port
== parser
::default_port(self.scheme()) {
1436 self.set_port_internal(port
);
1440 fn set_port_internal(&mut self, port
: Option
<u16>) {
1441 match (self.port
, port
) {
1443 (Some(_
), None
) => {
1444 self.serialization
.drain(self.host_end
as usize .. self.path_start
as usize);
1445 let offset
= self.path_start
- self.host_end
;
1446 self.path_start
= self.host_end
;
1447 if let Some(ref mut index
) = self.query_start { *index -= offset }
1448 if let Some(ref mut index
) = self.fragment_start { *index -= offset }
1450 (Some(old
), Some(new
)) if old
== new
=> {}
1452 let path_and_after
= self.slice(self.path_start
..).to_owned();
1453 self.serialization
.truncate(self.host_end
as usize);
1454 write
!(&mut self.serialization
, ":{}", new
).unwrap();
1455 let old_path_start
= self.path_start
;
1456 let new_path_start
= to_u32(self.serialization
.len()).unwrap();
1457 self.path_start
= new_path_start
;
1458 let adjust
= |index
: &mut u32| {
1459 *index
-= old_path_start
;
1460 *index
+= new_path_start
;
1462 if let Some(ref mut index
) = self.query_start { adjust(index) }
1463 if let Some(ref mut index
) = self.fragment_start { adjust(index) }
1464 self.serialization
.push_str(&path_and_after
);
1470 /// Change this URL’s host.
1472 /// Removing the host (calling this with `None`)
1473 /// will also remove any username, password, and port number.
1481 /// # use url::ParseError;
1483 /// # fn run() -> Result<(), ParseError> {
1484 /// let mut url = Url::parse("https://example.net")?;
1485 /// let result = url.set_host(Some("rust-lang.org"));
1486 /// assert!(result.is_ok());
1487 /// assert_eq!(url.as_str(), "https://rust-lang.org/");
1490 /// # run().unwrap();
1497 /// # use url::ParseError;
1499 /// # fn run() -> Result<(), ParseError> {
1500 /// let mut url = Url::parse("foo://example.net")?;
1501 /// let result = url.set_host(None);
1502 /// assert!(result.is_ok());
1503 /// assert_eq!(url.as_str(), "foo:/");
1506 /// # run().unwrap();
1509 /// Cannot remove host for 'special' schemes (e.g. `http`):
1513 /// # use url::ParseError;
1515 /// # fn run() -> Result<(), ParseError> {
1516 /// let mut url = Url::parse("https://example.net")?;
1517 /// let result = url.set_host(None);
1518 /// assert!(result.is_err());
1519 /// assert_eq!(url.as_str(), "https://example.net/");
1522 /// # run().unwrap();
1525 /// Cannot change or remove host for cannot-be-a-base URLs:
1529 /// # use url::ParseError;
1531 /// # fn run() -> Result<(), ParseError> {
1532 /// let mut url = Url::parse("mailto:rms@example.net")?;
1534 /// let result = url.set_host(Some("rust-lang.org"));
1535 /// assert!(result.is_err());
1536 /// assert_eq!(url.as_str(), "mailto:rms@example.net");
1538 /// let result = url.set_host(None);
1539 /// assert!(result.is_err());
1540 /// assert_eq!(url.as_str(), "mailto:rms@example.net");
1543 /// # run().unwrap();
1548 /// If this URL is cannot-be-a-base or there is an error parsing the given `host`,
1549 /// a [`ParseError`] variant will be returned.
1551 /// [`ParseError`]: enum.ParseError.html
1552 pub fn set_host(&mut self, host
: Option
<&str>) -> Result
<(), ParseError
> {
1553 if self.cannot_be_a_base() {
1554 return Err(ParseError
::SetHostOnCannotBeABaseUrl
)
1557 if let Some(host
) = host
{
1558 if host
== "" && SchemeType
::from(self.scheme()).is_special() {
1559 return Err(ParseError
::EmptyHost
);
1561 self.set_host_internal(Host
::parse(host
)?
, None
)
1562 } else if self.has_host() {
1563 if SchemeType
::from(self.scheme()).is_special() {
1564 return Err(ParseError
::EmptyHost
)
1566 debug_assert
!(self.byte_at(self.scheme_end
) == b'
:'
);
1567 debug_assert
!(self.byte_at(self.path_start
) == b'
/'
);
1568 let new_path_start
= self.scheme_end
+ 1;
1569 self.serialization
.drain(new_path_start
as usize..self.path_start
as usize);
1570 let offset
= self.path_start
- new_path_start
;
1571 self.path_start
= new_path_start
;
1572 self.username_end
= new_path_start
;
1573 self.host_start
= new_path_start
;
1574 self.host_end
= new_path_start
;
1576 if let Some(ref mut index
) = self.query_start { *index -= offset }
1577 if let Some(ref mut index
) = self.fragment_start { *index -= offset }
1582 /// opt_new_port: None means leave unchanged, Some(None) means remove any port number.
1583 fn set_host_internal(&mut self, host
: Host
<String
>, opt_new_port
: Option
<Option
<u16>>) {
1584 let old_suffix_pos
= if opt_new_port
.is_some() { self.path_start }
else { self.host_end }
;
1585 let suffix
= self.slice(old_suffix_pos
..).to_owned();
1586 self.serialization
.truncate(self.host_start
as usize);
1587 if !self.has_authority() {
1588 debug_assert
!(self.slice(self.scheme_end
..self.host_start
) == ":");
1589 debug_assert
!(self.username_end
== self.host_start
);
1590 self.serialization
.push('
/'
);
1591 self.serialization
.push('
/'
);
1592 self.username_end
+= 2;
1593 self.host_start
+= 2;
1595 write
!(&mut self.serialization
, "{}", host
).unwrap();
1596 self.host_end
= to_u32(self.serialization
.len()).unwrap();
1597 self.host
= host
.into();
1599 if let Some(new_port
) = opt_new_port
{
1600 self.port
= new_port
;
1601 if let Some(port
) = new_port
{
1602 write
!(&mut self.serialization
, ":{}", port
).unwrap();
1605 let new_suffix_pos
= to_u32(self.serialization
.len()).unwrap();
1606 self.serialization
.push_str(&suffix
);
1608 let adjust
= |index
: &mut u32| {
1609 *index
-= old_suffix_pos
;
1610 *index
+= new_suffix_pos
;
1612 adjust(&mut self.path_start
);
1613 if let Some(ref mut index
) = self.query_start { adjust(index) }
1614 if let Some(ref mut index
) = self.fragment_start { adjust(index) }
1617 /// Change this URL’s host to the given IP address.
1619 /// If this URL is cannot-be-a-base, do nothing and return `Err`.
1621 /// Compared to `Url::set_host`, this skips the host parser.
1626 /// use url::{Url, ParseError};
1628 /// # fn run() -> Result<(), ParseError> {
1629 /// let mut url = Url::parse("http://example.com")?;
1630 /// url.set_ip_host("127.0.0.1".parse().unwrap());
1631 /// assert_eq!(url.host_str(), Some("127.0.0.1"));
1632 /// assert_eq!(url.as_str(), "http://127.0.0.1/");
1635 /// # run().unwrap();
1638 /// Cannot change URL's from mailto(cannot-be-base) to ip:
1641 /// use url::{Url, ParseError};
1643 /// # fn run() -> Result<(), ParseError> {
1644 /// let mut url = Url::parse("mailto:rms@example.com")?;
1645 /// let result = url.set_ip_host("127.0.0.1".parse().unwrap());
1647 /// assert_eq!(url.as_str(), "mailto:rms@example.com");
1648 /// assert!(result.is_err());
1651 /// # run().unwrap();
1654 pub fn set_ip_host(&mut self, address
: IpAddr
) -> Result
<(), ()> {
1655 if self.cannot_be_a_base() {
1659 let address
= match address
{
1660 IpAddr
::V4(address
) => Host
::Ipv4(address
),
1661 IpAddr
::V6(address
) => Host
::Ipv6(address
),
1663 self.set_host_internal(address
, None
);
1667 /// Change this URL’s password.
1669 /// If this URL is cannot-be-a-base or does not have a host, do nothing and return `Err`.
1674 /// use url::{Url, ParseError};
1676 /// # fn run() -> Result<(), ParseError> {
1677 /// let mut url = Url::parse("mailto:rmz@example.com")?;
1678 /// let result = url.set_password(Some("secret_password"));
1679 /// assert!(result.is_err());
1681 /// let mut url = Url::parse("ftp://user1:secret1@example.com")?;
1682 /// let result = url.set_password(Some("secret_password"));
1683 /// assert_eq!(url.password(), Some("secret_password"));
1685 /// let mut url = Url::parse("ftp://user2:@example.com")?;
1686 /// let result = url.set_password(Some("secret2"));
1687 /// assert!(result.is_ok());
1688 /// assert_eq!(url.password(), Some("secret2"));
1691 /// # run().unwrap();
1693 pub fn set_password(&mut self, password
: Option
<&str>) -> Result
<(), ()> {
1694 if !self.has_host() {
1697 if let Some(password
) = password
{
1698 let host_and_after
= self.slice(self.host_start
..).to_owned();
1699 self.serialization
.truncate(self.username_end
as usize);
1700 self.serialization
.push('
:'
);
1701 self.serialization
.extend(utf8_percent_encode(password
, USERINFO_ENCODE_SET
));
1702 self.serialization
.push('@'
);
1704 let old_host_start
= self.host_start
;
1705 let new_host_start
= to_u32(self.serialization
.len()).unwrap();
1706 let adjust
= |index
: &mut u32| {
1707 *index
-= old_host_start
;
1708 *index
+= new_host_start
;
1710 self.host_start
= new_host_start
;
1711 adjust(&mut self.host_end
);
1712 adjust(&mut self.path_start
);
1713 if let Some(ref mut index
) = self.query_start { adjust(index) }
1714 if let Some(ref mut index
) = self.fragment_start { adjust(index) }
1716 self.serialization
.push_str(&host_and_after
);
1717 } else if self.byte_at(self.username_end
) == b'
:'
{ // If there is a password to remove
1718 let has_username_or_password
= self.byte_at(self.host_start
- 1) == b'@'
;
1719 debug_assert
!(has_username_or_password
);
1720 let username_start
= self.scheme_end
+ 3;
1721 let empty_username
= username_start
== self.username_end
;
1722 let start
= self.username_end
; // Remove the ':'
1723 let end
= if empty_username
{
1724 self.host_start
// Remove the '@' as well
1726 self.host_start
- 1 // Keep the '@' to separate the username from the host
1728 self.serialization
.drain(start
as usize .. end
as usize);
1729 let offset
= end
- start
;
1730 self.host_start
-= offset
;
1731 self.host_end
-= offset
;
1732 self.path_start
-= offset
;
1733 if let Some(ref mut index
) = self.query_start { *index -= offset }
1734 if let Some(ref mut index
) = self.fragment_start { *index -= offset }
1739 /// Change this URL’s username.
1741 /// If this URL is cannot-be-a-base or does not have a host, do nothing and return `Err`.
1744 /// Cannot setup username from mailto(cannot-be-base)
1747 /// use url::{Url, ParseError};
1749 /// # fn run() -> Result<(), ParseError> {
1750 /// let mut url = Url::parse("mailto:rmz@example.com")?;
1751 /// let result = url.set_username("user1");
1752 /// assert_eq!(url.as_str(), "mailto:rmz@example.com");
1753 /// assert!(result.is_err());
1756 /// # run().unwrap();
1759 /// Setup username to user1
1762 /// use url::{Url, ParseError};
1764 /// # fn run() -> Result<(), ParseError> {
1765 /// let mut url = Url::parse("ftp://:secre1@example.com/")?;
1766 /// let result = url.set_username("user1");
1767 /// assert!(result.is_ok());
1768 /// assert_eq!(url.username(), "user1");
1769 /// assert_eq!(url.as_str(), "ftp://user1:secre1@example.com/");
1772 /// # run().unwrap();
1774 pub fn set_username(&mut self, username
: &str) -> Result
<(), ()> {
1775 if !self.has_host() {
1778 let username_start
= self.scheme_end
+ 3;
1779 debug_assert
!(self.slice(self.scheme_end
..username_start
) == "://");
1780 if self.slice(username_start
..self.username_end
) == username
{
1783 let after_username
= self.slice(self.username_end
..).to_owned();
1784 self.serialization
.truncate(username_start
as usize);
1785 self.serialization
.extend(utf8_percent_encode(username
, USERINFO_ENCODE_SET
));
1787 let mut removed_bytes
= self.username_end
;
1788 self.username_end
= to_u32(self.serialization
.len()).unwrap();
1789 let mut added_bytes
= self.username_end
;
1791 let new_username_is_empty
= self.username_end
== username_start
;
1792 match (new_username_is_empty
, after_username
.chars().next()) {
1793 (true, Some('@'
)) => {
1795 self.serialization
.push_str(&after_username
[1..]);
1797 (false, Some('@'
)) | (_
, Some('
:'
)) | (true, _
) => {
1798 self.serialization
.push_str(&after_username
);
1802 self.serialization
.push('@'
);
1803 self.serialization
.push_str(&after_username
);
1807 let adjust
= |index
: &mut u32| {
1808 *index
-= removed_bytes
;
1809 *index
+= added_bytes
;
1811 adjust(&mut self.host_start
);
1812 adjust(&mut self.host_end
);
1813 adjust(&mut self.path_start
);
1814 if let Some(ref mut index
) = self.query_start { adjust(index) }
1815 if let Some(ref mut index
) = self.fragment_start { adjust(index) }
1819 /// Change this URL’s scheme.
1821 /// Do nothing and return `Err` if:
1823 /// * The new scheme is not in `[a-zA-Z][a-zA-Z0-9+.-]+`
1824 /// * This URL is cannot-be-a-base and the new scheme is one of
1825 /// `http`, `https`, `ws`, `wss`, `ftp`, or `gopher`
1829 /// Change the URL’s scheme from `https` to `foo`:
1833 /// # use url::ParseError;
1835 /// # fn run() -> Result<(), ParseError> {
1836 /// let mut url = Url::parse("https://example.net")?;
1837 /// let result = url.set_scheme("foo");
1838 /// assert_eq!(url.as_str(), "foo://example.net/");
1839 /// assert!(result.is_ok());
1842 /// # run().unwrap();
1846 /// Cannot change URL’s scheme from `https` to `foõ`:
1850 /// # use url::ParseError;
1852 /// # fn run() -> Result<(), ParseError> {
1853 /// let mut url = Url::parse("https://example.net")?;
1854 /// let result = url.set_scheme("foõ");
1855 /// assert_eq!(url.as_str(), "https://example.net/");
1856 /// assert!(result.is_err());
1859 /// # run().unwrap();
1862 /// Cannot change URL’s scheme from `mailto` (cannot-be-a-base) to `https`:
1866 /// # use url::ParseError;
1868 /// # fn run() -> Result<(), ParseError> {
1869 /// let mut url = Url::parse("mailto:rms@example.net")?;
1870 /// let result = url.set_scheme("https");
1871 /// assert_eq!(url.as_str(), "mailto:rms@example.net");
1872 /// assert!(result.is_err());
1875 /// # run().unwrap();
1877 pub fn set_scheme(&mut self, scheme
: &str) -> Result
<(), ()> {
1878 let mut parser
= Parser
::for_setter(String
::new());
1879 let remaining
= parser
.parse_scheme(parser
::Input
::new(scheme
))?
;
1880 if !remaining
.is_empty() ||
1881 (!self.has_host() && SchemeType
::from(&parser
.serialization
).is_special()) {
1884 let old_scheme_end
= self.scheme_end
;
1885 let new_scheme_end
= to_u32(parser
.serialization
.len()).unwrap();
1886 let adjust
= |index
: &mut u32| {
1887 *index
-= old_scheme_end
;
1888 *index
+= new_scheme_end
;
1891 self.scheme_end
= new_scheme_end
;
1892 adjust(&mut self.username_end
);
1893 adjust(&mut self.host_start
);
1894 adjust(&mut self.host_end
);
1895 adjust(&mut self.path_start
);
1896 if let Some(ref mut index
) = self.query_start { adjust(index) }
1897 if let Some(ref mut index
) = self.fragment_start { adjust(index) }
1899 parser
.serialization
.push_str(self.slice(old_scheme_end
..));
1900 self.serialization
= parser
.serialization
;
1904 /// Convert a file name as `std::path::Path` into an URL in the `file` scheme.
1906 /// This returns `Err` if the given path is not absolute or,
1907 /// on Windows, if the prefix is not a disk prefix (e.g. `C:`) or a UNC prefix (`\\`).
1911 /// On Unix-like platforms:
1914 /// # if cfg!(unix) {
1917 /// # fn run() -> Result<(), ()> {
1918 /// let url = Url::from_file_path("/tmp/foo.txt")?;
1919 /// assert_eq!(url.as_str(), "file:///tmp/foo.txt");
1921 /// let url = Url::from_file_path("../foo.txt");
1922 /// assert!(url.is_err());
1924 /// let url = Url::from_file_path("https://google.com/");
1925 /// assert!(url.is_err());
1928 /// # run().unwrap();
1931 pub fn from_file_path
<P
: AsRef
<Path
>>(path
: P
) -> Result
<Url
, ()> {
1932 let mut serialization
= "file://".to_owned();
1933 let host_start
= serialization
.len() as u32;
1934 let (host_end
, host
) = path_to_file_url_segments(path
.as_ref(), &mut serialization
)?
;
1936 serialization
: serialization
,
1937 scheme_end
: "file".len() as u32,
1938 username_end
: host_start
,
1939 host_start
: host_start
,
1943 path_start
: host_end
,
1945 fragment_start
: None
,
1949 /// Convert a directory name as `std::path::Path` into an URL in the `file` scheme.
1951 /// This returns `Err` if the given path is not absolute or,
1952 /// on Windows, if the prefix is not a disk prefix (e.g. `C:`) or a UNC prefix (`\\`).
1954 /// Compared to `from_file_path`, this ensure that URL’s the path has a trailing slash
1955 /// so that the entire path is considered when using this URL as a base URL.
1959 /// * `"index.html"` parsed with `Url::from_directory_path(Path::new("/var/www"))`
1960 /// as the base URL is `file:///var/www/index.html`
1961 /// * `"index.html"` parsed with `Url::from_file_path(Path::new("/var/www"))`
1962 /// as the base URL is `file:///var/index.html`, which might not be what was intended.
1964 /// Note that `std::path` does not consider trailing slashes significant
1965 /// and usually does not include them (e.g. in `Path::parent()`).
1966 pub fn from_directory_path
<P
: AsRef
<Path
>>(path
: P
) -> Result
<Url
, ()> {
1967 let mut url
= Url
::from_file_path(path
)?
;
1968 if !url
.serialization
.ends_with('
/'
) {
1969 url
.serialization
.push('
/'
)
1974 /// Serialize with Serde using the internal representation of the `Url` struct.
1976 /// The corresponding `deserialize_internal` method sacrifices some invariant-checking
1977 /// for speed, compared to the `Deserialize` trait impl.
1979 /// This method is only available if the `serde` Cargo feature is enabled.
1980 #[cfg(feature = "serde")]
1982 pub fn serialize_internal
<S
>(&self, serializer
: &mut S
) -> Result
<(), S
::Error
> where S
: serde
::Serializer
{
1983 use serde
::Serialize
;
1984 // Destructuring first lets us ensure that adding or removing fields forces this method
1986 let Url
{ ref serialization
, ref scheme_end
,
1987 ref username_end
, ref host_start
,
1988 ref host_end
, ref host
, ref port
,
1989 ref path_start
, ref query_start
,
1990 ref fragment_start
} = *self;
1991 (serialization
, scheme_end
, username_end
,
1992 host_start
, host_end
, host
, port
, path_start
,
1993 query_start
, fragment_start
).serialize(serializer
)
1996 /// Serialize with Serde using the internal representation of the `Url` struct.
1998 /// The corresponding `deserialize_internal` method sacrifices some invariant-checking
1999 /// for speed, compared to the `Deserialize` trait impl.
2001 /// This method is only available if the `serde` Cargo feature is enabled.
2002 #[cfg(feature = "serde")]
2004 pub fn deserialize_internal
<D
>(deserializer
: &mut D
) -> Result
<Self, D
::Error
> where D
: serde
::Deserializer
{
2005 use serde
::{Deserialize, Error}
;
2006 let (serialization
, scheme_end
, username_end
,
2007 host_start
, host_end
, host
, port
, path_start
,
2008 query_start
, fragment_start
) = Deserialize
::deserialize(deserializer
)?
;
2010 serialization
: serialization
,
2011 scheme_end
: scheme_end
,
2012 username_end
: username_end
,
2013 host_start
: host_start
,
2017 path_start
: path_start
,
2018 query_start
: query_start
,
2019 fragment_start
: fragment_start
2021 if cfg
!(debug_assertions
) {
2022 url
.check_invariants().map_err(|ref reason
| Error
::invalid_value(&reason
))?
2028 /// Assuming the URL is in the `file` scheme or similar,
2029 /// convert its path to an absolute `std::path::Path`.
2031 /// **Note:** This does not actually check the URL’s `scheme`,
2032 /// and may give nonsensical results for other schemes.
2033 /// It is the user’s responsibility to check the URL’s scheme before calling this.
2037 /// # let url = Url::parse("file:///etc/passwd").unwrap();
2038 /// let path = url.to_file_path();
2041 /// Returns `Err` if the host is neither empty nor `"localhost"` (except on Windows, where
2042 /// `file:` URLs may have a non-local host),
2043 /// or if `Path::new_opt()` returns `None`.
2044 /// (That is, if the percent-decoded path contains a NUL byte or,
2045 /// for a Windows path, is not UTF-8.)
2047 pub fn to_file_path(&self) -> Result
<PathBuf
, ()> {
2048 if let Some(segments
) = self.path_segments() {
2049 let host
= match self.host() {
2050 None
| Some(Host
::Domain("localhost")) => None
,
2051 Some(_
) if cfg
!(windows
) && self.scheme() == "file" => {
2052 Some(&self.serialization
[self.host_start
as usize .. self.host_end
as usize])
2057 return file_url_segments_to_pathbuf(host
, segments
);
2062 // Private helper methods:
2065 fn slice
<R
>(&self, range
: R
) -> &str where R
: RangeArg
{
2066 range
.slice_of(&self.serialization
)
2070 fn byte_at(&self, i
: u32) -> u8 {
2071 self.serialization
.as_bytes()[i
as usize]
2075 /// Return an error if `Url::host` or `Url::port_or_known_default` return `None`.
2076 impl ToSocketAddrs
for Url
{
2077 type Iter
= SocketAddrs
;
2079 fn to_socket_addrs(&self) -> io
::Result
<Self::Iter
> {
2080 self.with_default_port(|_
| Err(()))?
.to_socket_addrs()
2084 /// Parse a string as an URL, without a base URL or encoding override.
2085 impl str::FromStr
for Url
{
2086 type Err
= ParseError
;
2089 fn from_str(input
: &str) -> Result
<Url
, ::ParseError
> {
2094 /// Display the serialization of this URL.
2095 impl fmt
::Display
for Url
{
2097 fn fmt(&self, formatter
: &mut fmt
::Formatter
) -> fmt
::Result
{
2098 fmt
::Display
::fmt(&self.serialization
, formatter
)
2102 /// Debug the serialization of this URL.
2103 impl fmt
::Debug
for Url
{
2105 fn fmt(&self, formatter
: &mut fmt
::Formatter
) -> fmt
::Result
{
2106 fmt
::Debug
::fmt(&self.serialization
, formatter
)
2110 /// URLs compare like their serialization.
2113 /// URLs compare like their serialization.
2114 impl PartialEq
for Url
{
2116 fn eq(&self, other
: &Self) -> bool
{
2117 self.serialization
== other
.serialization
2121 /// URLs compare like their serialization.
2124 fn cmp(&self, other
: &Self) -> cmp
::Ordering
{
2125 self.serialization
.cmp(&other
.serialization
)
2129 /// URLs compare like their serialization.
2130 impl PartialOrd
for Url
{
2132 fn partial_cmp(&self, other
: &Self) -> Option
<cmp
::Ordering
> {
2133 self.serialization
.partial_cmp(&other
.serialization
)
2137 /// URLs hash like their serialization.
2138 impl hash
::Hash
for Url
{
2140 fn hash
<H
>(&self, state
: &mut H
) where H
: hash
::Hasher
{
2141 hash
::Hash
::hash(&self.serialization
, state
)
2145 /// Return the serialization of this URL.
2146 impl AsRef
<str> for Url
{
2148 fn as_ref(&self) -> &str {
2154 fn slice_of
<'a
>(&self, s
: &'a
str) -> &'a
str;
2157 impl RangeArg
for Range
<u32> {
2159 fn slice_of
<'a
>(&self, s
: &'a
str) -> &'a
str {
2160 &s
[self.start
as usize .. self.end
as usize]
2164 impl RangeArg
for RangeFrom
<u32> {
2166 fn slice_of
<'a
>(&self, s
: &'a
str) -> &'a
str {
2167 &s
[self.start
as usize ..]
2171 impl RangeArg
for RangeTo
<u32> {
2173 fn slice_of
<'a
>(&self, s
: &'a
str) -> &'a
str {
2174 &s
[.. self.end
as usize]
2178 #[cfg(feature="rustc-serialize")]
2179 impl rustc_serialize
::Encodable
for Url
{
2180 fn encode
<S
: rustc_serialize
::Encoder
>(&self, encoder
: &mut S
) -> Result
<(), S
::Error
> {
2181 encoder
.emit_str(self.as_str())
2186 #[cfg(feature="rustc-serialize")]
2187 impl rustc_serialize
::Decodable
for Url
{
2188 fn decode
<D
: rustc_serialize
::Decoder
>(decoder
: &mut D
) -> Result
<Url
, D
::Error
> {
2189 Url
::parse(&*decoder
.read_str()?
).map_err(|error
| {
2190 decoder
.error(&format
!("URL parsing error: {}", error
))
2195 /// Serializes this URL into a `serde` stream.
2197 /// This implementation is only available if the `serde` Cargo feature is enabled.
2198 #[cfg(feature="serde")]
2199 impl serde
::Serialize
for Url
{
2200 fn serialize
<S
>(&self, serializer
: &mut S
) -> Result
<(), S
::Error
> where S
: serde
::Serializer
{
2201 serializer
.serialize_str(self.as_str())
2205 /// Deserializes this URL from a `serde` stream.
2207 /// This implementation is only available if the `serde` Cargo feature is enabled.
2208 #[cfg(feature="serde")]
2209 impl serde
::Deserialize
for Url
{
2210 fn deserialize
<D
>(deserializer
: &mut D
) -> Result
<Url
, D
::Error
> where D
: serde
::Deserializer
{
2211 let string_representation
: String
= serde
::Deserialize
::deserialize(deserializer
)?
;
2212 Url
::parse(&string_representation
).map_err(|err
| {
2213 serde
::Error
::invalid_value(err
.description())
2218 #[cfg(any(unix, target_os = "redox"))]
2219 fn path_to_file_url_segments(path
: &Path
, serialization
: &mut String
)
2220 -> Result
<(u32, HostInternal
), ()> {
2221 use std
::os
::unix
::prelude
::OsStrExt
;
2222 if !path
.is_absolute() {
2225 let host_end
= to_u32(serialization
.len()).unwrap();
2226 let mut empty
= true;
2227 // skip the root component
2228 for component
in path
.components().skip(1) {
2230 serialization
.push('
/'
);
2231 serialization
.extend(percent_encode(
2232 component
.as_os_str().as_bytes(), PATH_SEGMENT_ENCODE_SET
));
2235 // An URL’s path must not be empty.
2236 serialization
.push('
/'
);
2238 Ok((host_end
, HostInternal
::None
))
2242 fn path_to_file_url_segments(path
: &Path
, serialization
: &mut String
)
2243 -> Result
<(u32, HostInternal
), ()> {
2244 path_to_file_url_segments_windows(path
, serialization
)
2247 // Build this unconditionally to alleviate https://github.com/servo/rust-url/issues/102
2248 #[cfg_attr(not(windows), allow(dead_code))]
2249 fn path_to_file_url_segments_windows(path
: &Path
, serialization
: &mut String
)
2250 -> Result
<(u32, HostInternal
), ()> {
2251 use std
::path
::{Prefix, Component}
;
2252 if !path
.is_absolute() {
2255 let mut components
= path
.components();
2259 match components
.next() {
2260 Some(Component
::Prefix(ref p
)) => match p
.kind() {
2261 Prefix
::Disk(letter
) | Prefix
::VerbatimDisk(letter
) => {
2262 host_end
= to_u32(serialization
.len()).unwrap();
2263 host_internal
= HostInternal
::None
;
2264 serialization
.push('
/'
);
2265 serialization
.push(letter
as char);
2266 serialization
.push('
:'
);
2268 Prefix
::UNC(server
, share
) | Prefix
::VerbatimUNC(server
, share
) => {
2269 let host
= Host
::parse(server
.to_str().ok_or(())?
).map_err(|_
| ())?
;
2270 write
!(serialization
, "{}", host
).unwrap();
2271 host_end
= to_u32(serialization
.len()).unwrap();
2272 host_internal
= host
.into();
2273 serialization
.push('
/'
);
2274 let share
= share
.to_str().ok_or(())?
;
2275 serialization
.extend(percent_encode(share
.as_bytes(), PATH_SEGMENT_ENCODE_SET
));
2283 for component
in components
{
2284 if component
== Component
::RootDir { continue }
2285 // FIXME: somehow work with non-unicode?
2286 let component
= component
.as_os_str().to_str().ok_or(())?
;
2287 serialization
.push('
/'
);
2288 serialization
.extend(percent_encode(component
.as_bytes(), PATH_SEGMENT_ENCODE_SET
));
2290 Ok((host_end
, host_internal
))
2293 #[cfg(any(unix, target_os = "redox"))]
2294 fn file_url_segments_to_pathbuf(host
: Option
<&str>, segments
: str::Split
<char>) -> Result
<PathBuf
, ()> {
2295 use std
::ffi
::OsStr
;
2296 use std
::os
::unix
::prelude
::OsStrExt
;
2297 use std
::path
::PathBuf
;
2303 let mut bytes
= Vec
::new();
2304 for segment
in segments
{
2306 bytes
.extend(percent_decode(segment
.as_bytes()));
2308 let os_str
= OsStr
::from_bytes(&bytes
);
2309 let path
= PathBuf
::from(os_str
);
2310 debug_assert
!(path
.is_absolute(),
2311 "to_file_path() failed to produce an absolute Path");
2316 fn file_url_segments_to_pathbuf(host
: Option
<&str>, segments
: str::Split
<char>) -> Result
<PathBuf
, ()> {
2317 file_url_segments_to_pathbuf_windows(host
, segments
)
2320 // Build this unconditionally to alleviate https://github.com/servo/rust-url/issues/102
2321 #[cfg_attr(not(windows), allow(dead_code))]
2322 fn file_url_segments_to_pathbuf_windows(host
: Option
<&str>, mut segments
: str::Split
<char>) -> Result
<PathBuf
, ()> {
2324 let mut string
= if let Some(host
) = host
{
2325 r
"\\".to_owned() + host
2327 let first
= segments
.next().ok_or(())?
;
2331 if !first
.starts_with(parser
::ascii_alpha
) || first
.as_bytes()[1] != b'
:'
{
2339 if !first
.starts_with(parser
::ascii_alpha
) {
2342 let bytes
= first
.as_bytes();
2343 if bytes
[1] != b'
%'
|| bytes
[2] != b'
3'
|| (bytes
[3] != b'a'
&& bytes
[3] != b'A'
) {
2347 first
[0..1].to_owned() + ":"
2350 _
=> return Err(()),
2354 for segment
in segments
{
2357 // Currently non-unicode windows paths cannot be represented
2358 match String
::from_utf8(percent_decode(segment
.as_bytes()).collect()) {
2359 Ok(s
) => string
.push_str(&s
),
2360 Err(..) => return Err(()),
2363 let path
= PathBuf
::from(string
);
2364 debug_assert
!(path
.is_absolute(),
2365 "to_file_path() failed to produce an absolute Path");
2369 fn io_error
<T
>(reason
: &str) -> io
::Result
<T
> {
2370 Err(io
::Error
::new(io
::ErrorKind
::InvalidData
, reason
))
2373 /// Implementation detail of `Url::query_pairs_mut`. Typically not used directly.
2375 pub struct UrlQuery
<'a
> {
2377 fragment
: Option
<String
>,
2380 impl<'a
> Drop
for UrlQuery
<'a
> {
2381 fn drop(&mut self) {
2382 self.url
.restore_already_parsed_fragment(self.fragment
.take())
2387 /// Define a new struct
2388 /// that implements the [`EncodeSet`](percent_encoding/trait.EncodeSet.html) trait,
2389 /// for use in [`percent_decode()`](percent_encoding/fn.percent_encode.html)
2390 /// and related functions.
2392 /// Parameters are characters to include in the set in addition to those of the base set.
2393 /// See [encode sets specification](http://url.spec.whatwg.org/#simple-encode-set).
2399 /// #[macro_use] extern crate url;
2400 /// use url::percent_encoding::{utf8_percent_encode, SIMPLE_ENCODE_SET};
2401 /// define_encode_set! {
2402 /// /// This encode set is used in the URL parser for query strings.
2403 /// pub QUERY_ENCODE_SET = [SIMPLE_ENCODE_SET] | {' ', '"', '#', '<', '>'}
2406 /// assert_eq!(utf8_percent_encode("foo bar", QUERY_ENCODE_SET).collect::<String>(), "foo%20bar");
2410 macro_rules
! define_encode_set
{
2411 ($
(#[$attr: meta])* pub $name: ident = [$base_set: expr] | {$($ch: pat),*}) => {
2413 #[derive(Copy, Clone)]
2414 #[allow(non_camel_case_types)]
2417 impl $
crate::percent_encoding
::EncodeSet
for $name
{
2419 fn contains(&self, byte
: u8) -> bool
{
2420 match byte
as char {
2424 _
=> $base_set
.contains(byte
)