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;
|
extern crate rocket;
|
||||||
use rocket::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 = "/")]
|
#[route(GET, path = "/")]
|
||||||
fn simple() -> &'static str {
|
fn simple() -> File {
|
||||||
"Hello, simple example world! How is thou?"
|
File::open("/tmp/index.html").unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[route(GET, path = "/<name>")]
|
// #[route(GET, path = "/")]
|
||||||
fn hello(name: String) -> String {
|
// fn simple2() -> &'static str {
|
||||||
format!("Hello, {}!", name)
|
// "Hello, world!"
|
||||||
|
// }
|
||||||
|
|
||||||
|
#[route(GET, path = "/hello")]
|
||||||
|
fn simple3() -> String {
|
||||||
|
String::from("Hello, world!")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[route(PUT, path = "/<x>/<y>")]
|
// #[route(GET, path = "/<name>/<age>")]
|
||||||
fn bye(x: usize, y: usize) -> String {
|
// fn simple4(name: &str, age: i8) -> &str {
|
||||||
format!("{} + {} = {}", x, y, x + y)
|
// name
|
||||||
}
|
// }
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let rocket = Rocket::new("localhost", 8000);
|
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;
|
extern crate hyper;
|
||||||
|
|
||||||
mod method;
|
pub mod method;
|
||||||
mod error;
|
pub mod error;
|
||||||
mod response;
|
pub mod response;
|
||||||
mod request;
|
pub mod request;
|
||||||
|
pub mod param;
|
||||||
|
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
pub use method::Method;
|
pub use method::Method;
|
||||||
pub use error::Error;
|
pub use error::Error;
|
||||||
pub use response::{Body, Response};
|
pub use response::{Response, HypResponse, HypFresh, Responder};
|
||||||
pub use request::Request;
|
pub use request::Request;
|
||||||
|
pub use param::FromParam;
|
||||||
|
|
||||||
use hyper::server::Handler as HypHandler;
|
use hyper::server::Handler as HypHandler;
|
||||||
use hyper::server::Request as HypRequest;
|
use hyper::server::Request as HypRequest;
|
||||||
use hyper::server::Response as HypResponse;
|
|
||||||
use hyper::net::Fresh as HypFresh;
|
|
||||||
use hyper::Server;
|
use hyper::Server;
|
||||||
|
|
||||||
pub type Handler<'a> = fn(Request) -> Response<'a>;
|
pub type Handler<'a> = fn(Request) -> Response<'a>;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Route<'a, 'b> {
|
pub struct Route<'a> {
|
||||||
pub method: Method,
|
pub method: Method,
|
||||||
pub path: &'a str,
|
pub path: &'static str,
|
||||||
pub handler: Handler<'b>
|
pub handler: Handler<'a>
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub struct Rocket {
|
pub struct Rocket {
|
||||||
address: &'static str,
|
address: &'static str,
|
||||||
port: isize,
|
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>>
|
// mounts: HashMap<&'static str, Route<'a>>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,17 +42,9 @@ impl HypHandler for Rocket {
|
||||||
mut res: HypResponse<'a, HypFresh>) {
|
mut res: HypResponse<'a, HypFresh>) {
|
||||||
println!("Request: {:?}", req.uri);
|
println!("Request: {:?}", req.uri);
|
||||||
if self.handler.is_some() {
|
if self.handler.is_some() {
|
||||||
let response = (self.handler.as_ref().unwrap().handler)(Request::empty());
|
let handler = self.handler.as_ref();
|
||||||
*(res.headers_mut()) = response.headers;
|
let mut response = (handler.unwrap().handler)(Request::empty());
|
||||||
*(res.status_mut()) = response.status;
|
response.body.respond(res);
|
||||||
match response.body {
|
|
||||||
Body::Str(string) => {
|
|
||||||
let mut stream = res.start().unwrap();
|
|
||||||
stream.write_all(string.as_bytes()).unwrap();
|
|
||||||
stream.end();
|
|
||||||
}
|
|
||||||
_ => println!("UNIMPLEMENTED")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,7 +58,7 @@ impl Rocket {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mount(&mut self, base: &str, routes: &[&Route<'static, 'static>]) -> &mut Self {
|
pub fn mount(&mut self, base: &str, routes: &[&Route<'static>]) -> &mut Self {
|
||||||
println!("🛰 Mounting '{}':", base);
|
println!("🛰 Mounting '{}':", base);
|
||||||
for route in routes {
|
for route in routes {
|
||||||
if self.handler.is_none() {
|
if self.handler.is_none() {
|
||||||
|
@ -78,7 +71,7 @@ impl Rocket {
|
||||||
self
|
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.mount(base, routes);
|
||||||
self.launch();
|
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 error::Error;
|
||||||
|
use param::FromParam;
|
||||||
|
|
||||||
pub struct Request;
|
pub struct Request;
|
||||||
|
|
||||||
|
@ -8,13 +8,12 @@ impl Request {
|
||||||
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)
|
Err(Error::NoKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_param<T: FromStr>(&self, name: &str) -> Result<T, Error> {
|
pub fn get_param<'b, T: FromParam<'b>>(&self, name: &'b str)
|
||||||
self.get_param_str(name).and_then(|s| {
|
-> Result<T, Error> {
|
||||||
T::from_str(s).map_err(|_| Error::BadParse)
|
self.get_param_str(name).and_then(T::from_param)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,71 +1,146 @@
|
||||||
pub use hyper::status::StatusCode;
|
pub use hyper::server::Response as HypResponse;
|
||||||
pub use hyper::header::{self, Headers};
|
pub use hyper::net::Fresh as HypFresh;
|
||||||
use std::io::Read;
|
|
||||||
|
|
||||||
// Consider simply having Body be a trait, `RocketBody`, with one function that
|
use hyper::status::StatusCode;
|
||||||
// takes in a prepped up HypResponse and acts on it. Then, `Response` changes
|
use hyper::header::{self, Headers, Encoding};
|
||||||
// to be a Response<T: RocketBody> { body: T } and the Rocket HypHandler
|
use std::io::{self, Read, Write};
|
||||||
// simply sets up the status codes, headers, and calls body.fn(res).
|
use std::fs::File;
|
||||||
pub enum Body<'a> {
|
|
||||||
Bytes(&'a [u8]),
|
|
||||||
Str(&'a str),
|
|
||||||
String(String),
|
|
||||||
Stream(Box<Read>),
|
|
||||||
Empty
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Response<'a> {
|
pub struct Response<'a> {
|
||||||
pub status: StatusCode,
|
pub body: Box<Responder + 'a>
|
||||||
pub headers: Headers,
|
|
||||||
pub body: Body<'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> {
|
impl<'a> Response<'a> {
|
||||||
pub fn empty() -> Response<'a> {
|
pub fn empty() -> Response<'a> {
|
||||||
Response {
|
Response {
|
||||||
status: StatusCode::Ok,
|
body: Box::new(Empty::new(StatusCode::Ok))
|
||||||
headers: Headers::new(),
|
|
||||||
body: Body::Empty
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn not_found() -> Response<'a> {
|
pub fn not_found() -> Response<'a> {
|
||||||
Response {
|
Response {
|
||||||
status: StatusCode::NotFound,
|
body: Box::new(Empty::new(StatusCode::NotFound))
|
||||||
headers: Headers::new(),
|
|
||||||
body: Body::Empty
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn server_error() -> Response<'a> {
|
pub fn server_error() -> Response<'a> {
|
||||||
Response {
|
Response {
|
||||||
status: StatusCode::InternalServerError,
|
body: Box::new(Empty::new(StatusCode::InternalServerError))
|
||||||
headers: Headers::new(),
|
|
||||||
body: Body::Empty
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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> {
|
// macro_rules! impl_from_lengthed {
|
||||||
fn from(s: String) -> Self {
|
// ($name:ident, $T:ty) => (
|
||||||
let mut headers = Headers::new();
|
// impl<'a> From<$T> for Response<'a> {
|
||||||
headers.set(header::ContentLength(s.len() as u64));
|
// fn from(s: $T) -> Self {
|
||||||
Response {
|
// Response {
|
||||||
status: StatusCode::Ok,
|
// status: StatusCode::Ok,
|
||||||
headers: headers,
|
// headers: Headers::new(),
|
||||||
body: Body::String(s)
|
// 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::ext::base::{Annotatable, ExtCtxt};
|
||||||
use syntax::ptr::P;
|
use syntax::ptr::P;
|
||||||
use syntax::ext::build::AstBuilder;
|
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 syntax::parse::token::{self, str_to_ident};
|
||||||
|
|
||||||
use rocket::Method;
|
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,
|
pub fn get_fn_params(ecx: &ExtCtxt, sp: Span, path: &str,
|
||||||
fn_decl: &FnDecl) -> Vec<String> {
|
fn_decl: &FnDecl) -> Vec<String> {
|
||||||
|
|
||||||
|
debug!("FUNCTION: {:?}", fn_decl);
|
||||||
|
|
||||||
let mut seen = HashSet::new();
|
let mut seen = HashSet::new();
|
||||||
let bad_match_err = "Path string is malformed.";
|
let bad_match_err = "Path string is malformed.";
|
||||||
let mut matching = false;
|
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![];
|
let mut fn_param_exprs = vec![];
|
||||||
for param in &fn_params {
|
for param in &fn_params {
|
||||||
let param_ident = str_to_ident(param.as_str());
|
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) {
|
let $param_ident = match _req.get_param($param) {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(_) => return rocket::Response::not_found()
|
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![];
|
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 $route_fn_name<'a>(_req: rocket::Request) -> rocket::Response<'a> {
|
||||||
$fn_param_exprs
|
$fn_param_exprs
|
||||||
let result = $fn_name($fn_param_idents);
|
let result = $fn_name($fn_param_idents);
|
||||||
rocket::Response::from(result)
|
rocket::Response::new(result)
|
||||||
}
|
}
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
debug!("{}", item_to_string(&route_fn_item));
|
debug!("{}", item_to_string(&route_fn_item));
|
||||||
push(Annotatable::Item(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);
|
let method = method_variant_to_expr(ecx, route_params.method);
|
||||||
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<'static, 'static> = rocket::Route {
|
pub static $struct_name: rocket::Route<'static> = rocket::Route {
|
||||||
method: $method,
|
method: $method,
|
||||||
path: $path,
|
path: $path,
|
||||||
handler: $route_fn_name
|
handler: $route_fn_name
|
||||||
|
|
Loading…
Reference in New Issue