HRTB for the win! Manual routes example fully working.

This commit is contained in:
Sergio Benitez 2016-04-03 03:36:30 -07:00
parent 72329a7145
commit 3dfa049a1a
11 changed files with 92 additions and 49 deletions

View File

@ -5,4 +5,3 @@ authors = ["Sergio Benitez <sb@sergio.bz>"]
[dependencies] [dependencies]
rocket = { path = "../../lib" } rocket = { path = "../../lib" }
rocket_macros = { path = "../../macros" }

View File

@ -3,23 +3,25 @@ extern crate rocket;
use rocket::{Rocket, Request, Response, Route}; use rocket::{Rocket, Request, Response, Route};
use rocket::Method::*; use rocket::Method::*;
fn root(req: Request) -> Response<'static> { fn root(req: Request) -> Response {
let name = req.get_param(0).unwrap_or("unnamed"); let name = req.get_param(0).unwrap_or("unnamed");
Response::new(format!("Hello, {}!", name)) Response::new(format!("Hello, {}!", name))
} }
// TODO: Work with these lifetimes.
#[allow(dead_code)] #[allow(dead_code)]
fn lifetime_root<'a>(req: Request<'a>) -> Response<'a> { fn echo_url<'a>(req: Request<'a>) -> Response<'a> {
Response::new(req.get_uri()) Response::new(req.get_uri().split_at(6).1)
} }
fn main() { fn main() {
let mut rocket = Rocket::new("localhost", 8000);
let first = Route::new(Get, "/hello", root); let first = Route::new(Get, "/hello", root);
let second = Route::new(Get, "/hello/<any>", root); let second = Route::new(Get, "/hello/<any>", root);
Rocket::new("localhost", 8000).mount_and_launch("/", &[&first, &second]); rocket.mount("/", vec![first, second]);
// This below _should_ work. let echo = Route::new(Get, "/", echo_url);
// let lifetime = Route::new(Get, "/other", lifetime_root); rocket.mount("/echo:<str>", vec![echo]);
// Rocket::new("localhost", 8000).mount_and_launch("/", &[&lifetime]);
rocket.launch();
} }

9
lib/src/codegen.rs Normal file
View File

@ -0,0 +1,9 @@
use method::Method;
use handler::Handler;
pub struct StaticRouteInfo {
pub method: Method,
pub path: &'static str,
pub handler: Handler
}

View File

@ -1,4 +1,4 @@
#![feature(str_char)] #![feature(str_char, question_mark)]
#![feature(specialization)] #![feature(specialization)]
extern crate term_painter; extern crate term_painter;
@ -9,6 +9,7 @@ mod error;
mod param; mod param;
mod router; mod router;
mod rocket; mod rocket;
mod codegen;
pub mod request; pub mod request;
pub mod response; pub mod response;
@ -16,9 +17,10 @@ pub mod response;
pub mod handler { pub mod handler {
use super::{Request, Response}; 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 request::Request;
pub use method::Method; pub use method::Method;
pub use response::{Response, Responder}; pub use response::{Response, Responder};

View File

@ -9,10 +9,6 @@ pub struct Request<'a> {
} }
impl<'a> 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> { pub fn new(params: Vec<&'a str>, uri: &'a str) -> Request<'a> {
Request { Request {
params: params, params: params,

View File

@ -69,11 +69,6 @@ impl Rocket {
self self
} }
pub fn mount_and_launch(mut self, base: &'static str, routes: Vec<Route>) {
self.mount(base, routes);
self.launch();
}
pub fn launch(self) { pub fn launch(self) {
if self.router.has_collisions() { if self.router.has_collisions() {
println!("{}", Yellow.paint("Warning: route collisions detected!")); println!("{}", Yellow.paint("Warning: route collisions detected!"));
@ -81,7 +76,12 @@ impl Rocket {
let full_addr = format!("{}:{}", self.address, self.port); let full_addr = format!("{}:{}", self.address, self.port);
println!("🚀 {} {}...", White.paint("Rocket has launched from"), 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); let _ = HyperServer::http(full_addr.as_str()).unwrap().handle(self);
} }
pub fn mount_and_launch(mut self, base: &'static str, routes: Vec<Route>) {
self.mount(base, routes);
self.launch();
}
} }

View File

@ -14,7 +14,7 @@ use method::Method;
type Selector = (Method, usize); type Selector = (Method, usize);
pub struct Router { pub struct Router {
routes: HashMap<Selector, Vec<Route>> // for now routes: HashMap<Selector, Vec<Route>> // using 'selector' for now
} }
impl Router { impl Router {

View File

@ -5,36 +5,36 @@ use method::Method;
use super::{Collider, URI, URIBuf}; // :D use super::{Collider, URI, URIBuf}; // :D
use handler::Handler; use handler::Handler;
// FIXME: Take in the handler! Or maybe keep that in `Router`?
pub struct Route { pub struct Route {
pub method: Method, pub method: Method,
pub handler: Handler<'static>, pub handler: Handler,
pub path: URIBuf, pub path: URIBuf,
pub rank: isize pub rank: isize
} }
impl Route { impl Route {
pub fn ranked(rank: isize, m: Method, path: String, pub fn ranked<S>(rank: isize, m: Method, path: S, handler: Handler)
handler: Handler<'static>) -> Route { -> Route where S: AsRef<str> {
Route { Route {
method: m, method: m,
path: URIBuf::new(path), path: URIBuf::from(path.as_ref()),
handler: handler, handler: handler,
rank: rank rank: rank
} }
} }
pub fn new(m: Method, path: String, handler: Handler<'static>) -> Route { pub fn new<S>(m: Method, path: S, handler: Handler)
-> Route where S: AsRef<str> {
Route { Route {
method: m, method: m,
handler: handler, handler: handler,
rank: (!path.contains("<") as isize), rank: (!path.as_ref().contains("<") as isize),
path: URIBuf::new(path), path: URIBuf::from(path.as_ref()),
} }
} }
pub fn set_path(&mut self, path: String) { pub fn set_path<S>(&mut self, path: S) where S: AsRef<str> {
self.path = URIBuf::new(path); self.path = URIBuf::from(path.as_ref());
} }
// FIXME: Decide whether a component has to be fully variable or not. That // FIXME: Decide whether a component has to be fully variable or not. That
@ -57,7 +57,7 @@ impl Route {
impl fmt::Display for Route { impl fmt::Display for Route {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 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))
} }
} }

View File

@ -1,7 +1,9 @@
use std::cell::Cell; use std::cell::Cell;
use super::Collider; use super::Collider;
use std::convert::From;
use std::fmt::{self, Write};
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct URI<'a> { pub struct URI<'a> {
path: &'a str, path: &'a str,
segment_count: Cell<Option<usize>> segment_count: Cell<Option<usize>>
@ -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! */ } unsafe impl<'a> Sync for URI<'a> { /* It's safe! */ }
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq, Eq)]
pub struct URIBuf { pub struct URIBuf {
path: String, path: String,
segment_count: Cell<Option<usize>> segment_count: Cell<Option<usize>>
@ -42,16 +56,8 @@ pub struct URIBuf {
// I don't like repeating all of this stuff. Is there a better way? // I don't like repeating all of this stuff. Is there a better way?
impl URIBuf { impl URIBuf {
pub fn new(path: String) -> URIBuf {
URIBuf {
segment_count: Cell::new(None),
path: path,
}
}
pub fn segment_count(&self) -> usize { pub fn segment_count(&self) -> usize {
self.segment_count.get().unwrap_or_else(|| { self.segment_count.get().unwrap_or_else(|| {
println!("Computing!");
let count = self.segments().count(); let count = self.segments().count();
self.segment_count.set(Some(count)); self.segment_count.set(Some(count));
count count
@ -62,6 +68,10 @@ impl URIBuf {
Segments(self.path.as_str()) 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> { pub fn as_uri<'a>(&'a self) -> URI<'a> {
let mut uri = URI::new(self.path.as_str()); let mut uri = URI::new(self.path.as_str());
uri.segment_count = self.segment_count.clone(); uri.segment_count = self.segment_count.clone();
@ -79,6 +89,30 @@ impl URIBuf {
unsafe impl Sync for URIBuf { /* It's safe! */ } 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<String> 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> { impl<'a> Collider for URI<'a> {
fn collides_with(&self, other: &URI) -> bool { fn collides_with(&self, other: &URI) -> bool {
if self.segment_count() != other.segment_count() { if self.segment_count() != other.segment_count() {
@ -156,7 +190,7 @@ mod tests {
fn seg_count(path: &str, expected: usize) -> bool { fn seg_count(path: &str, expected: usize) -> bool {
let actual = URI::new(path).segment_count(); 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 { if actual != expected || actual_buf != expected {
println!("Count mismatch: expected {}, got {}.", expected, actual); println!("Count mismatch: expected {}, got {}.", expected, actual);
println!("{}", if actual != expected { "lifetime" } else { "buf" }); println!("{}", if actual != expected { "lifetime" } else { "buf" });
@ -173,7 +207,7 @@ mod tests {
let uri = URI::new(path); let uri = URI::new(path);
let actual: Vec<&str> = uri.segments().collect(); 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(); let actual_buf: Vec<&str> = uri_buf.segments().collect();
actual == expected && actual_buf == expected actual == expected && actual_buf == expected

View File

@ -17,7 +17,7 @@ use syntax::parse::token::intern;
use routes_macro::routes_macro; use routes_macro::routes_macro;
use route_decorator::route_decorator; 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_"; const FN_PREFIX: &'static str = "rocket_route_fn_";
#[plugin_registrar] #[plugin_registrar]

View File

@ -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, 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 // Create a comma seperated list (token tree) of the function parameters
// We pass this in to the user's function that we're wrapping. // 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 route_fn_name = prepend_ident(FN_PREFIX, &item.ident);
let fn_name = item.ident; let fn_name = item.ident;
let route_fn_item = quote_item!(ecx, 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 $form_stmt
$fn_param_exprs $fn_param_exprs
let result = $fn_name($fn_param_idents); 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); let method = method_variant_to_expr(ecx, route_params.method.node);
push(Annotatable::Item(quote_item!(ecx, push(Annotatable::Item(quote_item!(ecx,
#[allow(non_upper_case_globals)] #[allow(non_upper_case_globals)]
pub static $struct_name: rocket::Route = rocket::Route { pub static $struct_name: rocket::StaticRouteInfo = rocket::StaticRouteInfo {
method: $method, method: $method,
path: $path, path: $path,
handler: $route_fn_name handler: $route_fn_name