mirror of https://github.com/rwf2/Rocket.git
HRTB for the win! Manual routes example fully working.
This commit is contained in:
parent
72329a7145
commit
3dfa049a1a
|
@ -5,4 +5,3 @@ authors = ["Sergio Benitez <sb@sergio.bz>"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rocket = { path = "../../lib" }
|
rocket = { path = "../../lib" }
|
||||||
rocket_macros = { path = "../../macros" }
|
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
use method::Method;
|
||||||
|
use handler::Handler;
|
||||||
|
|
||||||
|
pub struct StaticRouteInfo {
|
||||||
|
pub method: Method,
|
||||||
|
pub path: &'static str,
|
||||||
|
pub handler: Handler
|
||||||
|
}
|
||||||
|
|
|
@ -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};
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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!"));
|
||||||
|
@ -84,4 +79,9 @@ impl Rocket {
|
||||||
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue