Skip to content

Commit e36bdb1

Browse files
committed
Implement 'url!(..)' macro
servo/rust-url#136 servo/rust-url#137
1 parent 0146751 commit e36bdb1

28 files changed

+347
-128
lines changed

components/compositing/constellation.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -701,7 +701,7 @@ impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
701701
parent_info,
702702
window_size,
703703
None,
704-
LoadData::new(Url::parse("about:failure").unwrap()));
704+
LoadData::new(url!("about:failure")));
705705

706706
self.push_pending_frame(new_pipeline_id, Some(pipeline_id));
707707
}

components/plugins/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,8 @@ git = "https://github.com/Manishearth/rust-clippy"
1616
branch = "servo"
1717
optional = true
1818

19+
[dependencies.url]
20+
version = "0.2.36"
21+
1922
[features]
2023
default = []

components/plugins/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ extern crate tenacious;
2626
#[cfg(feature = "clippy")]
2727
extern crate clippy;
2828

29+
extern crate url;
30+
2931
use rustc::plugin::Registry;
3032
use syntax::ext::base::*;
3133
use syntax::feature_gate::AttributeType::Whitelisted;
@@ -41,6 +43,7 @@ pub mod lints;
4143
pub mod reflector;
4244
/// Utilities for writing plugins
4345
pub mod casing;
46+
mod url_plugin;
4447
pub mod utils;
4548

4649
#[plugin_registrar]
@@ -51,6 +54,7 @@ pub fn plugin_registrar(reg: &mut Registry) {
5154
reg.register_syntax_extension(intern("derive_HeapSizeOf"), MultiDecorator(box heap_size::expand_heap_size));
5255
reg.register_macro("to_lower", casing::expand_lower);
5356
reg.register_macro("to_upper", casing::expand_upper);
57+
reg.register_macro("url", url_plugin::expand_url);
5458
reg.register_late_lint_pass(box lints::transmute_type::TransmutePass);
5559
reg.register_late_lint_pass(box lints::unrooted_must_root::UnrootedPass::new());
5660
reg.register_late_lint_pass(box lints::privatize::PrivatizePass);

components/plugins/url_plugin.rs

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
5+
use std::error::Error;
6+
use syntax;
7+
use syntax::ast::{TokenTree, ExprLit, LitStr, Expr};
8+
use syntax::codemap::Span;
9+
use syntax::ext::base::{ExtCtxt, MacResult, MacEager, DummyResult};
10+
use syntax::ext::build::AstBuilder;
11+
use syntax::fold::Folder;
12+
use syntax::parse;
13+
use syntax::parse::token::InternedString;
14+
use url::{Url, Host, RelativeSchemeData, SchemeData};
15+
16+
pub fn expand_url(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree])
17+
-> Box<MacResult + 'static> {
18+
let mut parser = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(), tts.to_vec());
19+
let query_expr = cx.expander().fold_expr(parser.parse_expr());
20+
21+
// Ensure a str literal was passed to the macro
22+
let query = match parse_str_lit(&query_expr) {
23+
Some(query) => query,
24+
None => {
25+
cx.span_err(query_expr.span, "'url!' expected string literal");
26+
return DummyResult::any(sp)
27+
},
28+
};
29+
30+
// Parse the str literal
31+
let Url { scheme, scheme_data, query, fragment } = match Url::parse(&query) {
32+
Ok(url) => url,
33+
Err(error) => {
34+
cx.span_err(query_expr.span, error.description());
35+
return DummyResult::any(sp)
36+
}
37+
};
38+
39+
let scheme_data_expr = cx.expr_scheme_data(sp, scheme_data);
40+
let query_expr = cx.expr_option_string(sp, query);
41+
let fragment_expr = cx.expr_option_string(sp, fragment);
42+
43+
let url_expr = quote_expr!(cx, {
44+
::url::Url {
45+
scheme: $scheme.to_owned(),
46+
scheme_data: $scheme_data_expr,
47+
query: $query_expr,
48+
fragment: $fragment_expr,
49+
}
50+
});
51+
52+
MacEager::expr(url_expr)
53+
}
54+
55+
fn parse_str_lit(e: &Expr) -> Option<InternedString> {
56+
if let ExprLit(ref lit) = e.node {
57+
if let LitStr(ref s, _) = lit.node {
58+
return Some(s.clone());
59+
}
60+
}
61+
None
62+
}
63+
64+
trait ExtCtxtHelpers {
65+
fn expr_scheme_data(&self, sp: Span, scheme_data: SchemeData) -> syntax::ptr::P<Expr>;
66+
fn expr_option_string(&self, sp: Span, string: Option<String>) -> syntax::ptr::P<Expr>;
67+
fn expr_option_u16(&self, sp: Span, unsigned: Option<u16>) -> syntax::ptr::P<Expr>;
68+
fn expr_host(&self, sp: Span, host: Host) -> syntax::ptr::P<Expr>;
69+
fn expr_slice_u16(&self, sp: Span, unsigned: &[u16]) -> syntax::ptr::P<Expr>;
70+
fn expr_vec_string(&self, sp: Span, strings: Vec<String>) -> syntax::ptr::P<Expr>;
71+
}
72+
73+
impl<'a> ExtCtxtHelpers for ExtCtxt<'a> {
74+
fn expr_scheme_data(&self, sp: Span, scheme_data: SchemeData) -> syntax::ptr::P<Expr> {
75+
match scheme_data {
76+
SchemeData::Relative(
77+
RelativeSchemeData { username, password, host, port, default_port, path }) =>
78+
{
79+
let password_expr = self.expr_option_string(sp, password);
80+
let host_expr = self.expr_host(sp, host);
81+
let port_expr = self.expr_option_u16(sp, port);
82+
let default_port_expr = self.expr_option_u16(sp, default_port);
83+
let path_expr = self.expr_vec_string(sp, path);
84+
85+
quote_expr!(self,
86+
::url::SchemeData::Relative(
87+
::url::RelativeSchemeData {
88+
username: $username.to_owned(),
89+
password: $password_expr,
90+
host: $host_expr,
91+
port: $port_expr,
92+
default_port: $default_port_expr,
93+
path: $path_expr.to_owned(),
94+
}
95+
))
96+
},
97+
SchemeData::NonRelative(ref scheme_data) => {
98+
quote_expr!(self, ::url::SchemeData::NonRelative($scheme_data.to_owned()))
99+
},
100+
}
101+
}
102+
103+
fn expr_option_string(&self, sp: Span, string: Option<String>) -> syntax::ptr::P<Expr> {
104+
match string {
105+
Some(string) => quote_expr!(self, Some($string.to_owned())),
106+
None => self.expr_none(sp),
107+
}
108+
}
109+
110+
fn expr_option_u16(&self, sp: Span, unsigned: Option<u16>) -> syntax::ptr::P<Expr> {
111+
match unsigned {
112+
Some(unsigned) => quote_expr!(self, Some($unsigned)),
113+
None => self.expr_none(sp),
114+
}
115+
}
116+
117+
fn expr_host(&self, sp: Span, host: Host) -> syntax::ptr::P<Expr> {
118+
match host {
119+
Host::Domain(domain) => quote_expr!(self, ::url::Host::Domain(String::from($domain))),
120+
Host::Ipv6(address) => {
121+
let pieces_expr = self.expr_slice_u16(sp, &address.pieces);
122+
quote_expr!(self,
123+
::url::Host::Ipv6(
124+
::url::Ipv6Address {
125+
pieces: $pieces_expr.to_owned()
126+
}
127+
))
128+
},
129+
}
130+
}
131+
132+
fn expr_slice_u16(&self, sp: Span, unsigned: &[u16]) -> syntax::ptr::P<Expr> {
133+
let unsigned = unsigned.iter().map(|p| quote_expr!(self, $p)).collect();
134+
self.expr_vec_slice(sp, unsigned)
135+
}
136+
137+
fn expr_vec_string(&self, sp: Span, strings: Vec<String>) -> syntax::ptr::P<Expr> {
138+
let strings = strings.iter().map(|p| quote_expr!(self, $p.to_owned())).collect();
139+
self.expr_vec(sp, strings)
140+
}
141+
}

components/script/dom/document.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1388,7 +1388,7 @@ impl Document {
13881388
source: DocumentSource,
13891389
doc_loader: DocumentLoader)
13901390
-> Document {
1391-
let url = url.unwrap_or_else(|| Url::parse("about:blank").unwrap());
1391+
let url = url.unwrap_or_else(|| url!("about:blank"));
13921392

13931393
let (ready_state, domcontentloaded_dispatched) = if source == DocumentSource::FromParser {
13941394
(DocumentReadyState::Loading, false)

components/script/dom/htmliframeelement.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ impl HTMLIFrameElement {
124124
pub fn process_the_iframe_attributes(&self) {
125125
let url = match self.get_url() {
126126
Some(url) => url.clone(),
127-
None => Url::parse("about:blank").unwrap(),
127+
None => url!("about:blank"),
128128
};
129129

130130
self.navigate_child_browsing_context(url);

components/script/script_task.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1967,7 +1967,7 @@ impl ScriptTask {
19671967
};
19681968

19691969
if load_data.url.scheme == "javascript" {
1970-
load_data.url = Url::parse("about:blank").unwrap();
1970+
load_data.url = url!("about:blank");
19711971
}
19721972

19731973
resource_task.send(ControlMsg::Load(NetLoadData {

components/servo/Cargo.lock

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

components/servo/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ path = "../../tests/unit/net"
2929
[dev-dependencies.net_traits_tests]
3030
path = "../../tests/unit/net_traits"
3131

32+
[dev-dependencies.plugin_tests]
33+
path = "../../tests/unit/plugin"
34+
3235
[dev-dependencies.script_tests]
3336
path = "../../tests/unit/script"
3437

components/style/font_face.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ fn parse_one_src(context: &ParserContext, input: &mut Parser) -> Result<Source,
112112
}
113113
let url = try!(input.expect_url());
114114
let url = UrlParser::new().base_url(context.base_url).parse(&url).unwrap_or_else(
115-
|_error| Url::parse("about:invalid").unwrap());
115+
|_error| url!("about:invalid"));
116116

117117
// Parsing optional format()
118118
let format_hints = if input.try(|input| input.expect_function_matching("format")).is_ok() {

0 commit comments

Comments
 (0)