From 3dfa049a1a21605c7d4a78843362b996c7c013e5 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Sun, 3 Apr 2016 03:36:30 -0700 Subject: [PATCH] HRTB for the win! Manual routes example fully working. --- examples/manual_routes/Cargo.toml | 1 - examples/manual_routes/src/main.rs | 18 +++++----- lib/src/codegen.rs | 9 +++++ lib/src/lib.rs | 6 ++-- lib/src/request.rs | 4 --- lib/src/rocket.rs | 12 +++---- lib/src/router/mod.rs | 2 +- lib/src/router/route.rs | 22 ++++++------ lib/src/router/uri.rs | 58 +++++++++++++++++++++++------- macros/src/lib.rs | 2 +- macros/src/route_decorator.rs | 7 ++-- 11 files changed, 92 insertions(+), 49 deletions(-) create mode 100644 lib/src/codegen.rs diff --git a/examples/manual_routes/Cargo.toml b/examples/manual_routes/Cargo.toml index 05095dfe..b2f846b9 100644 --- a/examples/manual_routes/Cargo.toml +++ b/examples/manual_routes/Cargo.toml @@ -5,4 +5,3 @@ authors = ["Sergio Benitez "] [dependencies] rocket = { path = "../../lib" } -rocket_macros = { path = "../../macros" } diff --git a/examples/manual_routes/src/main.rs b/examples/manual_routes/src/main.rs index 72dd091a..ca5c0608 100644 --- a/examples/manual_routes/src/main.rs +++ b/examples/manual_routes/src/main.rs @@ -3,23 +3,25 @@ extern crate rocket; use rocket::{Rocket, Request, Response, Route}; use rocket::Method::*; -fn root(req: Request) -> Response<'static> { +fn root(req: Request) -> Response { let name = req.get_param(0).unwrap_or("unnamed"); Response::new(format!("Hello, {}!", name)) } -// TODO: Work with these lifetimes. #[allow(dead_code)] -fn lifetime_root<'a>(req: Request<'a>) -> Response<'a> { - Response::new(req.get_uri()) +fn echo_url<'a>(req: Request<'a>) -> Response<'a> { + Response::new(req.get_uri().split_at(6).1) } fn main() { + let mut rocket = Rocket::new("localhost", 8000); + let first = Route::new(Get, "/hello", root); let second = Route::new(Get, "/hello/", root); - Rocket::new("localhost", 8000).mount_and_launch("/", &[&first, &second]); + rocket.mount("/", vec![first, second]); - // This below _should_ work. - // let lifetime = Route::new(Get, "/other", lifetime_root); - // Rocket::new("localhost", 8000).mount_and_launch("/", &[&lifetime]); + let echo = Route::new(Get, "/", echo_url); + rocket.mount("/echo:", vec![echo]); + + rocket.launch(); } diff --git a/lib/src/codegen.rs b/lib/src/codegen.rs new file mode 100644 index 00000000..35184aaa --- /dev/null +++ b/lib/src/codegen.rs @@ -0,0 +1,9 @@ +use method::Method; +use handler::Handler; + +pub struct StaticRouteInfo { + pub method: Method, + pub path: &'static str, + pub handler: Handler +} + diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 941aba54..49d99114 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -1,4 +1,4 @@ -#![feature(str_char)] +#![feature(str_char, question_mark)] #![feature(specialization)] extern crate term_painter; @@ -9,6 +9,7 @@ mod error; mod param; mod router; mod rocket; +mod codegen; pub mod request; pub mod response; @@ -16,9 +17,10 @@ pub mod response; pub mod handler { use super::{Request, Response}; - pub type Handler<'a> = fn(Request) -> Response<'a>; + pub type Handler = for<'r> fn(Request<'r>) -> Response<'r>; } +pub use codegen::StaticRouteInfo; pub use request::Request; pub use method::Method; pub use response::{Response, Responder}; diff --git a/lib/src/request.rs b/lib/src/request.rs index f04dfc17..e3f8ffe6 100644 --- a/lib/src/request.rs +++ b/lib/src/request.rs @@ -9,10 +9,6 @@ pub struct Request<'a> { } impl<'a> Request<'a> { - pub fn empty() -> Request<'static> { - Request::new(vec![], "") - } - pub fn new(params: Vec<&'a str>, uri: &'a str) -> Request<'a> { Request { params: params, diff --git a/lib/src/rocket.rs b/lib/src/rocket.rs index 408bae1e..cdd08796 100644 --- a/lib/src/rocket.rs +++ b/lib/src/rocket.rs @@ -69,11 +69,6 @@ impl Rocket { self } - pub fn mount_and_launch(mut self, base: &'static str, routes: Vec) { - self.mount(base, routes); - self.launch(); - } - pub fn launch(self) { if self.router.has_collisions() { println!("{}", Yellow.paint("Warning: route collisions detected!")); @@ -81,7 +76,12 @@ impl Rocket { let full_addr = format!("{}:{}", self.address, self.port); println!("🚀 {} {}...", White.paint("Rocket has launched from"), - White.bold().paint(&full_addr)); + White.bold().paint(&full_addr)); let _ = HyperServer::http(full_addr.as_str()).unwrap().handle(self); } + + pub fn mount_and_launch(mut self, base: &'static str, routes: Vec) { + self.mount(base, routes); + self.launch(); + } } diff --git a/lib/src/router/mod.rs b/lib/src/router/mod.rs index 2dc01197..d938c0b6 100644 --- a/lib/src/router/mod.rs +++ b/lib/src/router/mod.rs @@ -14,7 +14,7 @@ use method::Method; type Selector = (Method, usize); pub struct Router { - routes: HashMap> // for now + routes: HashMap> // using 'selector' for now } impl Router { diff --git a/lib/src/router/route.rs b/lib/src/router/route.rs index 07adfcc2..428d45d9 100644 --- a/lib/src/router/route.rs +++ b/lib/src/router/route.rs @@ -5,36 +5,36 @@ use method::Method; use super::{Collider, URI, URIBuf}; // :D use handler::Handler; -// FIXME: Take in the handler! Or maybe keep that in `Router`? pub struct Route { pub method: Method, - pub handler: Handler<'static>, + pub handler: Handler, pub path: URIBuf, pub rank: isize } impl Route { - pub fn ranked(rank: isize, m: Method, path: String, - handler: Handler<'static>) -> Route { + pub fn ranked(rank: isize, m: Method, path: S, handler: Handler) + -> Route where S: AsRef { Route { method: m, - path: URIBuf::new(path), + path: URIBuf::from(path.as_ref()), handler: handler, rank: rank } } - pub fn new(m: Method, path: String, handler: Handler<'static>) -> Route { + pub fn new(m: Method, path: S, handler: Handler) + -> Route where S: AsRef { Route { method: m, handler: handler, - rank: (!path.contains("<") as isize), - path: URIBuf::new(path), + rank: (!path.as_ref().contains("<") as isize), + path: URIBuf::from(path.as_ref()), } } - pub fn set_path(&mut self, path: String) { - self.path = URIBuf::new(path); + pub fn set_path(&mut self, path: S) where S: AsRef { + self.path = URIBuf::from(path.as_ref()); } // FIXME: Decide whether a component has to be fully variable or not. That @@ -57,7 +57,7 @@ impl Route { impl fmt::Display for Route { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{} {:?}", Green.paint(&self.method), Blue.paint(&self.path)) + write!(f, "{} {}", Green.paint(&self.method), Blue.paint(&self.path)) } } diff --git a/lib/src/router/uri.rs b/lib/src/router/uri.rs index 1eef24a2..b064c60c 100644 --- a/lib/src/router/uri.rs +++ b/lib/src/router/uri.rs @@ -1,7 +1,9 @@ use std::cell::Cell; use super::Collider; +use std::convert::From; +use std::fmt::{self, Write}; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct URI<'a> { path: &'a str, segment_count: Cell> @@ -32,9 +34,21 @@ impl<'a> URI<'a> { } } +impl<'a> fmt::Display for URI<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut last = '\0'; + for c in self.path.chars() { + if !(c == '/' && last == '/') { f.write_char(c)?; } + last = c; + } + + Ok(()) + } +} + unsafe impl<'a> Sync for URI<'a> { /* It's safe! */ } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct URIBuf { path: String, segment_count: Cell> @@ -42,16 +56,8 @@ pub struct URIBuf { // I don't like repeating all of this stuff. Is there a better way? impl URIBuf { - pub fn new(path: String) -> URIBuf { - URIBuf { - segment_count: Cell::new(None), - path: path, - } - } - pub fn segment_count(&self) -> usize { self.segment_count.get().unwrap_or_else(|| { - println!("Computing!"); let count = self.segments().count(); self.segment_count.set(Some(count)); count @@ -62,6 +68,10 @@ impl URIBuf { Segments(self.path.as_str()) } + fn as_uri_uncached<'a>(&'a self) -> URI<'a> { + URI::new(self.path.as_str()) + } + pub fn as_uri<'a>(&'a self) -> URI<'a> { let mut uri = URI::new(self.path.as_str()); uri.segment_count = self.segment_count.clone(); @@ -79,6 +89,30 @@ impl URIBuf { unsafe impl Sync for URIBuf { /* It's safe! */ } +impl fmt::Display for URIBuf { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.as_uri_uncached().fmt(f) + } +} + +impl From for URIBuf { + fn from(path: String) -> URIBuf { + URIBuf { + segment_count: Cell::new(None), + path: path, + } + } +} + +impl<'a> From<&'a str> for URIBuf { + fn from(path: &'a str) -> URIBuf { + URIBuf { + segment_count: Cell::new(None), + path: path.to_string(), + } + } +} + impl<'a> Collider for URI<'a> { fn collides_with(&self, other: &URI) -> bool { if self.segment_count() != other.segment_count() { @@ -156,7 +190,7 @@ mod tests { fn seg_count(path: &str, expected: usize) -> bool { let actual = URI::new(path).segment_count(); - let actual_buf = URIBuf::new(path.to_string()).segment_count(); + let actual_buf = URIBuf::from(path).segment_count(); if actual != expected || actual_buf != expected { println!("Count mismatch: expected {}, got {}.", expected, actual); println!("{}", if actual != expected { "lifetime" } else { "buf" }); @@ -173,7 +207,7 @@ mod tests { let uri = URI::new(path); let actual: Vec<&str> = uri.segments().collect(); - let uri_buf = URIBuf::new(path.to_string()); + let uri_buf = URIBuf::from(path); let actual_buf: Vec<&str> = uri_buf.segments().collect(); actual == expected && actual_buf == expected diff --git a/macros/src/lib.rs b/macros/src/lib.rs index 629d9fcc..106d340b 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -17,7 +17,7 @@ use syntax::parse::token::intern; use routes_macro::routes_macro; use route_decorator::route_decorator; -const STRUCT_PREFIX: &'static str = "ROCKET_ROUTE_STRUCT_"; +const STRUCT_PREFIX: &'static str = "static_rocket_route_info_for_"; const FN_PREFIX: &'static str = "rocket_route_fn_"; #[plugin_registrar] diff --git a/macros/src/route_decorator.rs b/macros/src/route_decorator.rs index a709ac72..c975a903 100644 --- a/macros/src/route_decorator.rs +++ b/macros/src/route_decorator.rs @@ -326,7 +326,7 @@ pub fn route_decorator(ecx: &mut ExtCtxt, sp: Span, meta_item: &MetaItem, } let mut fn_params = get_fn_params(ecx, sp, &route_params.path, &fn_decl, - external_params.clone()); + external_params.clone()); // Create a comma seperated list (token tree) of the function parameters // We pass this in to the user's function that we're wrapping. @@ -365,7 +365,8 @@ pub fn route_decorator(ecx: &mut ExtCtxt, sp: Span, meta_item: &MetaItem, let route_fn_name = prepend_ident(FN_PREFIX, &item.ident); let fn_name = item.ident; let route_fn_item = quote_item!(ecx, - fn $route_fn_name<'rocket>(_req: rocket::Request) -> rocket::Response<'rocket> { + fn $route_fn_name<'rocket>(_req: rocket::Request<'rocket>) + -> rocket::Response<'rocket> { $form_stmt $fn_param_exprs let result = $fn_name($fn_param_idents); @@ -381,7 +382,7 @@ pub fn route_decorator(ecx: &mut ExtCtxt, sp: Span, meta_item: &MetaItem, let method = method_variant_to_expr(ecx, route_params.method.node); push(Annotatable::Item(quote_item!(ecx, #[allow(non_upper_case_globals)] - pub static $struct_name: rocket::Route = rocket::Route { + pub static $struct_name: rocket::StaticRouteInfo = rocket::StaticRouteInfo { method: $method, path: $path, handler: $route_fn_name