mirror of https://github.com/rwf2/Rocket.git
Somewhat good infrastructure for params and responses.
Can actually return strings and what-not from a route. Yay!
This commit is contained in:
parent
c6e8cd47da
commit
da2b0ed35a
|
@ -3,23 +3,36 @@
|
|||
|
||||
extern crate rocket;
|
||||
use rocket::Rocket;
|
||||
use rocket::response::{HypResponse, HypFresh, Responder};
|
||||
|
||||
use std::fs::File;
|
||||
|
||||
// #[route(GET, path = "/")]
|
||||
// fn simple() -> &'static str {
|
||||
// "Hello, simple example world! How is thou?"
|
||||
// }
|
||||
|
||||
#[route(GET, path = "/")]
|
||||
fn simple() -> &'static str {
|
||||
"Hello, simple example world! How is thou?"
|
||||
fn simple() -> File {
|
||||
File::open("/tmp/index.html").unwrap()
|
||||
}
|
||||
|
||||
#[route(GET, path = "/<name>")]
|
||||
fn hello(name: String) -> String {
|
||||
format!("Hello, {}!", name)
|
||||
// #[route(GET, path = "/")]
|
||||
// fn simple2() -> &'static str {
|
||||
// "Hello, world!"
|
||||
// }
|
||||
|
||||
#[route(GET, path = "/hello")]
|
||||
fn simple3() -> String {
|
||||
String::from("Hello, world!")
|
||||
}
|
||||
|
||||
#[route(PUT, path = "/<x>/<y>")]
|
||||
fn bye(x: usize, y: usize) -> String {
|
||||
format!("{} + {} = {}", x, y, x + y)
|
||||
}
|
||||
// #[route(GET, path = "/<name>/<age>")]
|
||||
// fn simple4(name: &str, age: i8) -> &str {
|
||||
// name
|
||||
// }
|
||||
|
||||
fn main() {
|
||||
let rocket = Rocket::new("localhost", 8000);
|
||||
rocket.mount_and_launch("/", routes![simple, hello, bye]);
|
||||
rocket.mount_and_launch("/", routes![simple, simple3]);
|
||||
}
|
||||
|
|
|
@ -1,38 +1,39 @@
|
|||
extern crate hyper;
|
||||
|
||||
mod method;
|
||||
mod error;
|
||||
mod response;
|
||||
mod request;
|
||||
pub mod method;
|
||||
pub mod error;
|
||||
pub mod response;
|
||||
pub mod request;
|
||||
pub mod param;
|
||||
|
||||
use std::io::Write;
|
||||
|
||||
pub use method::Method;
|
||||
pub use error::Error;
|
||||
pub use response::{Body, Response};
|
||||
pub use response::{Response, HypResponse, HypFresh, Responder};
|
||||
pub use request::Request;
|
||||
pub use param::FromParam;
|
||||
|
||||
use hyper::server::Handler as HypHandler;
|
||||
use hyper::server::Request as HypRequest;
|
||||
use hyper::server::Response as HypResponse;
|
||||
use hyper::net::Fresh as HypFresh;
|
||||
use hyper::Server;
|
||||
|
||||
pub type Handler<'a> = fn(Request) -> Response<'a>;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Clone)]
|
||||
pub struct Route<'a, 'b> {
|
||||
pub struct Route<'a> {
|
||||
pub method: Method,
|
||||
pub path: &'a str,
|
||||
pub handler: Handler<'b>
|
||||
pub path: &'static str,
|
||||
pub handler: Handler<'a>
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct Rocket {
|
||||
address: &'static str,
|
||||
port: isize,
|
||||
handler: Option<Route<'static, 'static>> // just for testing
|
||||
handler: Option<Route<'static>>, // just for testing
|
||||
paths: Vec<&'static str> // for now, to check for collisions
|
||||
// mounts: HashMap<&'static str, Route<'a>>
|
||||
}
|
||||
|
||||
|
@ -41,17 +42,9 @@ impl HypHandler for Rocket {
|
|||
mut res: HypResponse<'a, HypFresh>) {
|
||||
println!("Request: {:?}", req.uri);
|
||||
if self.handler.is_some() {
|
||||
let response = (self.handler.as_ref().unwrap().handler)(Request::empty());
|
||||
*(res.headers_mut()) = response.headers;
|
||||
*(res.status_mut()) = response.status;
|
||||
match response.body {
|
||||
Body::Str(string) => {
|
||||
let mut stream = res.start().unwrap();
|
||||
stream.write_all(string.as_bytes()).unwrap();
|
||||
stream.end();
|
||||
}
|
||||
_ => println!("UNIMPLEMENTED")
|
||||
}
|
||||
let handler = self.handler.as_ref();
|
||||
let mut response = (handler.unwrap().handler)(Request::empty());
|
||||
response.body.respond(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -65,8 +58,8 @@ impl Rocket {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn mount(&mut self, base: &str, routes: &[&Route<'static, 'static>]) -> &mut Self {
|
||||
println!("🛰 Mounting '{}':", base);
|
||||
pub fn mount(&mut self, base: &str, routes: &[&Route<'static>]) -> &mut Self {
|
||||
println!("🛰 Mounting '{}':", base);
|
||||
for route in routes {
|
||||
if self.handler.is_none() {
|
||||
println!("\t* INSTALLED: {} '{}'", route.method, route.path);
|
||||
|
@ -78,7 +71,7 @@ impl Rocket {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn mount_and_launch(mut self, base: &str, routes: &[&Route<'static, 'static>]) {
|
||||
pub fn mount_and_launch(mut self, base: &str, routes: &[&Route<'static>]) {
|
||||
self.mount(base, routes);
|
||||
self.launch();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
use std::str::FromStr;
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6, SocketAddr};
|
||||
|
||||
use error::Error;
|
||||
|
||||
pub trait FromParam<'a>: Sized {
|
||||
fn from_param(param: &'a str) -> Result<Self, Error>;
|
||||
}
|
||||
|
||||
impl<'a> FromParam<'a> for &'a str {
|
||||
fn from_param(param: &'a str) -> Result<&'a str, Error> {
|
||||
Ok(param)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_with_fromstr {
|
||||
($($T:ident),+) => (
|
||||
$(impl<'a> FromParam<'a> for $T {
|
||||
fn from_param(param: &'a str) -> Result<$T, Error> {
|
||||
$T::from_str(param).map_err(|_| Error::BadParse)
|
||||
}
|
||||
}
|
||||
)+)
|
||||
}
|
||||
|
||||
impl_with_fromstr!(f32, f64, isize, i8, i16, i32, i64, usize, u8, u16, u32, u64,
|
||||
bool, String, IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6,
|
||||
SocketAddr);
|
|
@ -1,5 +1,5 @@
|
|||
use std::str::FromStr;
|
||||
use error::Error;
|
||||
use param::FromParam;
|
||||
|
||||
pub struct Request;
|
||||
|
||||
|
@ -8,13 +8,12 @@ impl Request {
|
|||
Request
|
||||
}
|
||||
|
||||
pub fn get_param_str(&self, name: &str) -> Result<&str, Error> {
|
||||
pub fn get_param_str<'a>(&self, name: &'a str) -> Result<&'a str, Error> {
|
||||
Err(Error::NoKey)
|
||||
}
|
||||
|
||||
pub fn get_param<T: FromStr>(&self, name: &str) -> Result<T, Error> {
|
||||
self.get_param_str(name).and_then(|s| {
|
||||
T::from_str(s).map_err(|_| Error::BadParse)
|
||||
})
|
||||
pub fn get_param<'b, T: FromParam<'b>>(&self, name: &'b str)
|
||||
-> Result<T, Error> {
|
||||
self.get_param_str(name).and_then(T::from_param)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,71 +1,146 @@
|
|||
pub use hyper::status::StatusCode;
|
||||
pub use hyper::header::{self, Headers};
|
||||
use std::io::Read;
|
||||
pub use hyper::server::Response as HypResponse;
|
||||
pub use hyper::net::Fresh as HypFresh;
|
||||
|
||||
// Consider simply having Body be a trait, `RocketBody`, with one function that
|
||||
// takes in a prepped up HypResponse and acts on it. Then, `Response` changes
|
||||
// to be a Response<T: RocketBody> { body: T } and the Rocket HypHandler
|
||||
// simply sets up the status codes, headers, and calls body.fn(res).
|
||||
pub enum Body<'a> {
|
||||
Bytes(&'a [u8]),
|
||||
Str(&'a str),
|
||||
String(String),
|
||||
Stream(Box<Read>),
|
||||
Empty
|
||||
}
|
||||
use hyper::status::StatusCode;
|
||||
use hyper::header::{self, Headers, Encoding};
|
||||
use std::io::{self, Read, Write};
|
||||
use std::fs::File;
|
||||
|
||||
pub struct Response<'a> {
|
||||
pub status: StatusCode,
|
||||
pub headers: Headers,
|
||||
pub body: Body<'a>
|
||||
pub body: Box<Responder + 'a>
|
||||
}
|
||||
|
||||
impl<'a> Response<'a> {
|
||||
pub fn new<T: Responder + 'a>(body: T) -> Response<'a> {
|
||||
Response {
|
||||
body: Box::new(body)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Empty {
|
||||
status: StatusCode
|
||||
}
|
||||
|
||||
impl Empty {
|
||||
fn new(status: StatusCode) -> Empty {
|
||||
Empty {
|
||||
status: status
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Responder {
|
||||
fn respond<'a>(&mut self, mut res: HypResponse<'a, HypFresh>);
|
||||
}
|
||||
|
||||
impl Responder for Empty {
|
||||
fn respond<'a>(&mut self, mut res: HypResponse<'a, HypFresh>) {
|
||||
res.send(b"").unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Responder for &'a str {
|
||||
fn respond<'b>(&mut self, mut res: HypResponse<'b, HypFresh>) {
|
||||
res.send(self.as_bytes()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl Responder for String {
|
||||
fn respond<'b>(&mut self, mut res: HypResponse<'b, HypFresh>) {
|
||||
res.send(self.as_bytes()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl Responder for File {
|
||||
fn respond<'b>(&mut self, mut res: HypResponse<'b, HypFresh>) {
|
||||
let size = self.metadata().unwrap().len();
|
||||
|
||||
res.headers_mut().set(header::ContentLength(size));
|
||||
*(res.status_mut()) = StatusCode::Ok;
|
||||
|
||||
let mut s = String::new();
|
||||
self.read_to_string(&mut s).unwrap();
|
||||
|
||||
let mut stream = res.start().unwrap();
|
||||
stream.write_all(s.as_bytes()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// const CHUNK_SIZE: u32 = 4096;
|
||||
|
||||
// pub struct Stream<T: Read>(T);
|
||||
|
||||
// impl<T> Responder for Stream<T> {
|
||||
// fn respond<'a>(&self, mut r: HypResponse<'a, HypFresh>) {
|
||||
// r.headers_mut().set(header::TransferEncoding(vec![Encoding::Chunked]));
|
||||
// *(r.status_mut()) = StatusCode::Ok;
|
||||
// let mut stream = r.start();
|
||||
|
||||
// r.write()
|
||||
// Response {
|
||||
// status: StatusCode::Ok,
|
||||
// headers: headers,
|
||||
// body: Body::Stream(r)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
impl<'a> Response<'a> {
|
||||
pub fn empty() -> Response<'a> {
|
||||
Response {
|
||||
status: StatusCode::Ok,
|
||||
headers: Headers::new(),
|
||||
body: Body::Empty
|
||||
body: Box::new(Empty::new(StatusCode::Ok))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn not_found() -> Response<'a> {
|
||||
Response {
|
||||
status: StatusCode::NotFound,
|
||||
headers: Headers::new(),
|
||||
body: Body::Empty
|
||||
body: Box::new(Empty::new(StatusCode::NotFound))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn server_error() -> Response<'a> {
|
||||
Response {
|
||||
status: StatusCode::InternalServerError,
|
||||
headers: Headers::new(),
|
||||
body: Body::Empty
|
||||
body: Box::new(Empty::new(StatusCode::InternalServerError))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for Response<'a> {
|
||||
fn from(s: &'a str) -> Self {
|
||||
let mut headers = Headers::new();
|
||||
headers.set(header::ContentLength(s.len() as u64));
|
||||
Response {
|
||||
status: StatusCode::Ok,
|
||||
headers: headers,
|
||||
body: Body::Str(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<String> for Response<'a> {
|
||||
fn from(s: String) -> Self {
|
||||
let mut headers = Headers::new();
|
||||
headers.set(header::ContentLength(s.len() as u64));
|
||||
Response {
|
||||
status: StatusCode::Ok,
|
||||
headers: headers,
|
||||
body: Body::String(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
// macro_rules! impl_from_lengthed {
|
||||
// ($name:ident, $T:ty) => (
|
||||
// impl<'a> From<$T> for Response<'a> {
|
||||
// fn from(s: $T) -> Self {
|
||||
// Response {
|
||||
// status: StatusCode::Ok,
|
||||
// headers: Headers::new(),
|
||||
// body: Body::$name(s)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// )
|
||||
// }
|
||||
|
||||
// impl_from_lengthed!(Str, &'a str);
|
||||
// impl_from_lengthed!(String, String);
|
||||
// impl_from_lengthed!(Bytes, &'a [u8]);
|
||||
// impl_from_lengthed!(File, File);
|
||||
|
||||
// macro_rules! impl_from_reader {
|
||||
// ($T:ty) => (
|
||||
// impl<'a> From<&'a $T> for Response<'a> {
|
||||
// fn from(r: &'a $T) -> Self {
|
||||
// let mut headers = Headers::new();
|
||||
// headers.set(header::TransferEncoding(vec![Encoding::Chunked]));
|
||||
// Response {
|
||||
// status: StatusCode::Ok,
|
||||
// headers: headers,
|
||||
// body: Body::Stream(r)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// )
|
||||
// }
|
||||
|
||||
// impl_from_reader!(File);
|
||||
// impl_from_reader!(&'a [u8]);
|
||||
|
|
|
@ -11,7 +11,7 @@ use syntax::ast::{Item, Expr, ItemKind, MetaItem, MetaItemKind, FnDecl};
|
|||
use syntax::ext::base::{Annotatable, ExtCtxt};
|
||||
use syntax::ptr::P;
|
||||
use syntax::ext::build::AstBuilder;
|
||||
use syntax::print::pprust::item_to_string;
|
||||
use syntax::print::pprust::{item_to_string, stmt_to_string};
|
||||
use syntax::parse::token::{self, str_to_ident};
|
||||
|
||||
use rocket::Method;
|
||||
|
@ -117,6 +117,9 @@ fn method_variant_to_expr(ecx: &ExtCtxt, method: Method) -> P<Expr> {
|
|||
|
||||
pub fn get_fn_params(ecx: &ExtCtxt, sp: Span, path: &str,
|
||||
fn_decl: &FnDecl) -> Vec<String> {
|
||||
|
||||
debug!("FUNCTION: {:?}", fn_decl);
|
||||
|
||||
let mut seen = HashSet::new();
|
||||
let bad_match_err = "Path string is malformed.";
|
||||
let mut matching = false;
|
||||
|
@ -188,12 +191,15 @@ pub fn route_decorator(ecx: &mut ExtCtxt, sp: Span, meta_item: &MetaItem,
|
|||
let mut fn_param_exprs = vec![];
|
||||
for param in &fn_params {
|
||||
let param_ident = str_to_ident(param.as_str());
|
||||
fn_param_exprs.push(quote_stmt!(ecx,
|
||||
let param_fn_item = quote_stmt!(ecx,
|
||||
let $param_ident = match _req.get_param($param) {
|
||||
Ok(v) => v,
|
||||
Err(_) => return rocket::Response::not_found()
|
||||
};
|
||||
).unwrap());
|
||||
).unwrap();
|
||||
|
||||
debug!("Param FN: {:?}", stmt_to_string(¶m_fn_item));
|
||||
fn_param_exprs.push(param_fn_item);
|
||||
}
|
||||
|
||||
let mut fn_param_idents: Vec<TokenTree> = vec![];
|
||||
|
@ -212,9 +218,10 @@ pub fn route_decorator(ecx: &mut ExtCtxt, sp: Span, meta_item: &MetaItem,
|
|||
fn $route_fn_name<'a>(_req: rocket::Request) -> rocket::Response<'a> {
|
||||
$fn_param_exprs
|
||||
let result = $fn_name($fn_param_idents);
|
||||
rocket::Response::from(result)
|
||||
rocket::Response::new(result)
|
||||
}
|
||||
).unwrap();
|
||||
|
||||
debug!("{}", item_to_string(&route_fn_item));
|
||||
push(Annotatable::Item(route_fn_item));
|
||||
|
||||
|
@ -223,7 +230,7 @@ pub fn route_decorator(ecx: &mut ExtCtxt, sp: Span, meta_item: &MetaItem,
|
|||
let method = method_variant_to_expr(ecx, route_params.method);
|
||||
push(Annotatable::Item(quote_item!(ecx,
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub static $struct_name: rocket::Route<'static, 'static> = rocket::Route {
|
||||
pub static $struct_name: rocket::Route<'static> = rocket::Route {
|
||||
method: $method,
|
||||
path: $path,
|
||||
handler: $route_fn_name
|
||||
|
|
Loading…
Reference in New Issue