mirror of https://github.com/rwf2/Rocket.git
Cleaned up response -> split into files. Started form example.
There's something going on with Hyper. When a 303 (see other) response is sent in response to a POST, the browser does a GET to the location header. Hyper somehow misreads the method parameter here, resulting in a route failer. I need to MITM the connection to see exactly what the browser is sending and what Hyper is receiving to see who's wrong.
This commit is contained in:
parent
cddc92f870
commit
fb8fdc3bc2
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "forms"
|
||||
version = "0.0.1"
|
||||
authors = ["Sergio Benitez <sb@sergio.bz>"]
|
||||
|
||||
[dependencies]
|
||||
rocket = { path = "../../lib" }
|
||||
rocket_macros = { path = "../../macros" }
|
|
@ -0,0 +1,39 @@
|
|||
#![feature(plugin)]
|
||||
#![plugin(rocket_macros)]
|
||||
|
||||
extern crate rocket;
|
||||
|
||||
use rocket::Rocket;
|
||||
use std::fs::File;
|
||||
use std::io::Error as IOError;
|
||||
use rocket::response::Redirect;
|
||||
|
||||
#[route(GET, path = "/")]
|
||||
fn index() -> File {
|
||||
File::open("static/index.html").unwrap()
|
||||
}
|
||||
|
||||
#[route(GET, path = "/<file>")]
|
||||
fn files(file: &str) -> Result<File, IOError> {
|
||||
File::open(format!("static/{}", file))
|
||||
}
|
||||
|
||||
#[route(GET, path = "/user/<username>")]
|
||||
fn user_page(username: &str) -> String {
|
||||
format!("This is {}'s page.", username)
|
||||
}
|
||||
|
||||
// TODO: Actually look at form parameters.
|
||||
#[route(POST, path = "/login")]
|
||||
fn login() -> Result<Redirect, &'static str> {
|
||||
if true {
|
||||
Ok(Redirect::other("/user/some_name"))
|
||||
} else {
|
||||
Err("Sorry, the username and password are invalid.")
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let rocket = Rocket::new("localhost", 8000);
|
||||
rocket.mount_and_launch("/", routes![index, files, user_page, login]);
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
<h1>Login</h1>
|
||||
|
||||
<form action="/login" method="post" accept-charset="utf-8">
|
||||
Username:<input type="text" name="username">
|
||||
Password:<input type="password" name="password">
|
||||
<input type="submit" value="Login">
|
||||
</form>
|
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "optional_result"
|
||||
version = "0.0.1"
|
||||
authors = ["Sergio Benitez <sb@sergio.bz>"]
|
||||
|
||||
[dependencies]
|
||||
rocket = { path = "../../lib" }
|
||||
rocket_macros = { path = "../../macros" }
|
|
@ -0,0 +1,23 @@
|
|||
#![feature(plugin)]
|
||||
#![plugin(rocket_macros)]
|
||||
extern crate rocket;
|
||||
|
||||
use rocket::Rocket;
|
||||
use rocket::response::Redirect;
|
||||
|
||||
#[route(GET, path = "/users/<name>")]
|
||||
fn user(name: &str) -> Result<&'static str, Redirect> {
|
||||
match name {
|
||||
"Sergio" => Ok("Hello, Sergio!"),
|
||||
_ => Err(Redirect::to("/users/login"))
|
||||
}
|
||||
}
|
||||
|
||||
#[route(GET, path = "/users/login")]
|
||||
fn login() -> &'static str {
|
||||
"Hi! That user doesn't exist. Maybe you need to log in?"
|
||||
}
|
||||
|
||||
fn main() {
|
||||
Rocket::new("localhost", 8000).mount_and_launch("/", routes![user, login]);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
name = "optional_result"
|
||||
version = "0.0.1"
|
||||
authors = ["Sergio Benitez <sb@sergio.bz>"]
|
||||
|
||||
[dependencies]
|
||||
rocket = { path = "../../lib" }
|
||||
rocket_macros = { path = "../../macros" }
|
|
@ -0,0 +1,18 @@
|
|||
#![feature(plugin)]
|
||||
#![plugin(rocket_macros)]
|
||||
|
||||
extern crate rocket;
|
||||
use rocket::Rocket;
|
||||
|
||||
#[route(GET, path = "/users/<name>")]
|
||||
fn user(name: &str) -> Option<&'static str> {
|
||||
if name == "Sergio" {
|
||||
Some("Hello, Sergio!")
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
Rocket::new("localhost", 8000).mount_and_launch("/", routes![user]);
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
#![feature(str_char)]
|
||||
#![feature(specialization)]
|
||||
|
||||
extern crate term_painter;
|
||||
extern crate hyper;
|
||||
|
@ -12,10 +13,10 @@ pub mod router;
|
|||
|
||||
pub use method::Method;
|
||||
pub use error::Error;
|
||||
pub use response::{Response, HypResponse, HypFresh, Responder};
|
||||
pub use request::Request;
|
||||
pub use param::FromParam;
|
||||
pub use router::Router;
|
||||
pub use response::{Response, HypResponse, Responder, HypFresh};
|
||||
|
||||
use std::fmt;
|
||||
use term_painter::ToStyle;
|
||||
|
@ -27,7 +28,8 @@ use hyper::Server;
|
|||
|
||||
pub type Handler<'a> = fn(Request) -> Response<'a>;
|
||||
|
||||
// TODO: Figure out if having Handler<'static> there is a good idea.
|
||||
// TODO: Figure out if using 'static for Handler is a good idea.
|
||||
// TODO: Merge this `Route` and route::Route, somewhow.
|
||||
pub struct Route {
|
||||
pub method: Method,
|
||||
pub path: &'static str,
|
||||
|
@ -35,7 +37,8 @@ pub struct Route {
|
|||
}
|
||||
|
||||
impl Route {
|
||||
pub fn new(method: Method, path: &'static str, handler: Handler<'static>) -> Route {
|
||||
pub fn new(method: Method, path: &'static str, handler: Handler<'static>)
|
||||
-> Route {
|
||||
Route {
|
||||
method: method,
|
||||
path: path,
|
||||
|
@ -50,7 +53,6 @@ impl<'a> fmt::Display for Route {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct Rocket {
|
||||
address: &'static str,
|
||||
port: isize,
|
||||
|
@ -60,9 +62,10 @@ pub struct Rocket {
|
|||
impl HypHandler for Rocket {
|
||||
fn handle<'a, 'k>(&'a self, req: HypRequest<'a, 'k>,
|
||||
res: HypResponse<'a, HypFresh>) {
|
||||
println!("{} {:?} {:?}", White.paint("Incoming:"),
|
||||
Green.paint(&req.method), Blue.paint(&req.uri));
|
||||
if let RequestUri::AbsolutePath(uri_string) = req.uri {
|
||||
if let Some(method) = Method::from_hyp(req.method) {
|
||||
println!("Request: {:?}", uri_string);
|
||||
let uri_str = uri_string.as_str();
|
||||
let route = self.router.route(method, uri_str);
|
||||
let mut response = route.map_or(Response::not_found(), |route| {
|
||||
|
@ -71,11 +74,15 @@ impl HypHandler for Rocket {
|
|||
(route.handler)(request)
|
||||
});
|
||||
|
||||
return response.body.respond(res);
|
||||
}
|
||||
println!("{}", Green.paint("\t=> Dispatched request."));
|
||||
return response.respond(res);
|
||||
}
|
||||
|
||||
Response::not_found().body.respond(res);
|
||||
println!("{}", Yellow.paint("\t=> Debug: Method::from_hyp failed!"));
|
||||
}
|
||||
|
||||
println!("{}", Red.paint("\t=> Dispatch failed. Returning 404."));
|
||||
Response::not_found().respond(res);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,14 @@ impl Method {
|
|||
pub fn from_hyp(method: HypMethod) -> Option<Method> {
|
||||
match method {
|
||||
HypMethod::Get => Some(Get),
|
||||
HypMethod::Put => Some(Put),
|
||||
HypMethod::Post => Some(Post),
|
||||
HypMethod::Delete => Some(Delete),
|
||||
HypMethod::Options => Some(Options),
|
||||
HypMethod::Head => Some(Head),
|
||||
HypMethod::Trace => Some(Trace),
|
||||
HypMethod::Connect => Some(Connect),
|
||||
HypMethod::Patch => Some(Patch),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,133 +0,0 @@
|
|||
pub use hyper::server::Response as HypResponse;
|
||||
pub use hyper::net::Fresh as HypFresh;
|
||||
|
||||
use hyper::status::StatusCode;
|
||||
use hyper::header;
|
||||
use std::io::{Read, Write};
|
||||
use std::fs::File;
|
||||
use std::fmt;
|
||||
|
||||
pub struct Response<'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)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn empty() -> Response<'a> {
|
||||
Response {
|
||||
body: Box::new(Empty::new(StatusCode::Ok))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn not_found() -> Response<'a> {
|
||||
Response {
|
||||
body: Box::new(Empty::new(StatusCode::NotFound))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn server_error() -> Response<'a> {
|
||||
Response {
|
||||
body: Box::new(Empty::new(StatusCode::InternalServerError))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Responder {
|
||||
fn respond<'a>(&mut self, mut res: HypResponse<'a, HypFresh>);
|
||||
}
|
||||
|
||||
pub struct Empty {
|
||||
status: StatusCode
|
||||
}
|
||||
|
||||
impl Empty {
|
||||
fn new(status: StatusCode) -> Empty {
|
||||
Empty {
|
||||
status: status
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Responder for Empty {
|
||||
fn respond<'a>(&mut self, mut res: HypResponse<'a, HypFresh>) {
|
||||
res.headers_mut().set(header::ContentLength(0));
|
||||
*(res.status_mut()) = self.status;
|
||||
|
||||
let mut stream = res.start().unwrap();
|
||||
stream.write_all(b"").unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Responder for &'a str {
|
||||
fn respond<'b>(&mut self, res: HypResponse<'b, HypFresh>) {
|
||||
res.send(self.as_bytes()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl Responder for String {
|
||||
fn respond<'b>(&mut self, 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 v = Vec::new();
|
||||
self.read_to_end(&mut v).unwrap();
|
||||
|
||||
let mut stream = res.start().unwrap();
|
||||
stream.write_all(&v).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// Waiting for RFC #1210: impl specialization. It's not quite stable yet.
|
||||
// impl<T: Responder, E: fmt::Display + fmt::Debug> Responder for Result<T, E> {
|
||||
// fn respond<'b>(&mut self, res: HypResponse<'b, HypFresh>) {
|
||||
// if self.is_err() {
|
||||
// println!("Response error: {}", self.as_ref().err().unwrap());
|
||||
// return;
|
||||
// }
|
||||
|
||||
// self.as_mut().unwrap().respond(res);
|
||||
// }
|
||||
// }
|
||||
|
||||
impl<T: Responder, E: fmt::Debug> Responder for Result<T, E> {
|
||||
// prepend with `default` when using impl specialization
|
||||
fn respond<'b>(&mut self, res: HypResponse<'b, HypFresh>) {
|
||||
if self.is_err() {
|
||||
println!("Error: {:?}", self.as_ref().err().unwrap());
|
||||
return;
|
||||
}
|
||||
|
||||
self.as_mut().unwrap().respond(res);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Allow streamed responses.
|
||||
// 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)
|
||||
// }
|
||||
// }
|
||||
// }
|
|
@ -0,0 +1,20 @@
|
|||
use response::*;
|
||||
use std::io::Write;
|
||||
|
||||
pub struct Empty(StatusCode);
|
||||
|
||||
impl Empty {
|
||||
pub fn new(status: StatusCode) -> Empty {
|
||||
Empty(status)
|
||||
}
|
||||
}
|
||||
|
||||
impl Responder for Empty {
|
||||
fn respond<'a>(&mut self, mut res: HypResponse<'a, HypFresh>) {
|
||||
res.headers_mut().set(header::ContentLength(0));
|
||||
*(res.status_mut()) = self.0;
|
||||
|
||||
let mut stream = res.start().unwrap();
|
||||
stream.write_all(b"").unwrap();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
mod empty;
|
||||
mod responder;
|
||||
mod redirect;
|
||||
|
||||
pub use self::responder::Responder;
|
||||
pub use self::empty::Empty;
|
||||
pub use self::redirect::Redirect;
|
||||
|
||||
pub use hyper::server::Response as HypResponse;
|
||||
pub use hyper::net::Fresh as HypFresh;
|
||||
pub use hyper::status::StatusCode;
|
||||
pub use hyper::header;
|
||||
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
pub struct Response<'a>(Box<Responder + 'a>);
|
||||
|
||||
impl<'a> Response<'a> {
|
||||
pub fn new<T: Responder + 'a>(body: T) -> Response<'a> {
|
||||
Response(Box::new(body))
|
||||
}
|
||||
|
||||
pub fn empty() -> Response<'a> {
|
||||
Response(Box::new(Empty::new(StatusCode::Ok)))
|
||||
}
|
||||
|
||||
pub fn not_found() -> Response<'a> {
|
||||
Response(Box::new(Empty::new(StatusCode::NotFound)))
|
||||
}
|
||||
|
||||
pub fn server_error() -> Response<'a> {
|
||||
Response(Box::new(Empty::new(StatusCode::InternalServerError)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deref for Response<'a> {
|
||||
type Target = Box<Responder + 'a>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DerefMut for Response<'a> {
|
||||
fn deref_mut(&mut self) -> &mut Box<Responder + 'a> {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
use response::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Redirect(StatusCode, String);
|
||||
|
||||
impl Redirect {
|
||||
pub fn to(uri: &str) -> Redirect {
|
||||
Redirect(StatusCode::TemporaryRedirect, String::from(uri))
|
||||
}
|
||||
|
||||
pub fn created(uri: &str) -> Redirect {
|
||||
Redirect(StatusCode::Created, String::from(uri))
|
||||
}
|
||||
|
||||
pub fn other(uri: &str) -> Redirect {
|
||||
Redirect(StatusCode::SeeOther, String::from(uri))
|
||||
}
|
||||
|
||||
pub fn permanent(uri: &str) -> Redirect {
|
||||
Redirect(StatusCode::PermanentRedirect, String::from(uri))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Responder for Redirect {
|
||||
fn respond<'b>(&mut self, mut res: HypResponse<'b, HypFresh>) {
|
||||
res.headers_mut().set(header::ContentLength(0));
|
||||
res.headers_mut().set(header::Location(self.1.clone()));
|
||||
*(res.status_mut()) = self.0;
|
||||
res.send(b"").unwrap();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
use response::*;
|
||||
use std::io::{Read, Write};
|
||||
use std::fs::File;
|
||||
use std::fmt;
|
||||
|
||||
pub trait Responder {
|
||||
fn respond<'a>(&mut self, mut res: HypResponse<'a, HypFresh>);
|
||||
}
|
||||
|
||||
impl<'a> Responder for &'a str {
|
||||
fn respond<'b>(&mut self, res: HypResponse<'b, HypFresh>) {
|
||||
res.send(self.as_bytes()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl Responder for String {
|
||||
fn respond<'b>(&mut self, res: HypResponse<'b, HypFresh>) {
|
||||
res.send(self.as_bytes()).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Should we set a content-type here? Safari needs text/html to render.
|
||||
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 v = Vec::new();
|
||||
self.read_to_end(&mut v).unwrap();
|
||||
|
||||
let mut stream = res.start().unwrap();
|
||||
stream.write_all(&v).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Responder> Responder for Option<T> {
|
||||
fn respond<'b>(&mut self, res: HypResponse<'b, HypFresh>) {
|
||||
if self.is_none() {
|
||||
println!("Option is none.");
|
||||
// TODO: Should this be a 404 or 500?
|
||||
return Empty::new(StatusCode::NotFound).respond(res)
|
||||
}
|
||||
|
||||
self.as_mut().unwrap().respond(res);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Responder, E: fmt::Debug> Responder for Result<T, E> {
|
||||
// prepend with `default` when using impl specialization
|
||||
default fn respond<'b>(&mut self, res: HypResponse<'b, HypFresh>) {
|
||||
if self.is_err() {
|
||||
println!("Error: {:?}", self.as_ref().err().unwrap());
|
||||
// TODO: Should this be a 404 or 500?
|
||||
return Empty::new(StatusCode::NotFound).respond(res)
|
||||
}
|
||||
|
||||
self.as_mut().unwrap().respond(res);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Responder, E: Responder + fmt::Debug> Responder for Result<T, E> {
|
||||
fn respond<'b>(&mut self, res: HypResponse<'b, HypFresh>) {
|
||||
match self {
|
||||
&mut Ok(ref mut responder) => responder.respond(res),
|
||||
&mut Err(ref mut responder) => responder.respond(res)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
// TODO: Allow streamed responses.
|
||||
// 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)
|
||||
// }
|
||||
// }
|
||||
// }
|
|
@ -42,7 +42,7 @@ impl Router {
|
|||
let num_components = path.components().count();
|
||||
if let Some(routes) = self.routes.get(&(method, num_components)) {
|
||||
for route in routes.iter().filter(|r| r.collides_with(uri)) {
|
||||
println!("Matched {} to: {}", uri, route);
|
||||
println!("\t=> Matched {} to: {}", uri, route);
|
||||
if let None = matched_route {
|
||||
matched_route = Some(route);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ use super::Collider; // :D
|
|||
use std::path::Component;
|
||||
use Handler;
|
||||
|
||||
// TODO: Add ranking to routes. Give static routes higher rank by default.
|
||||
// FIXME: Take in the handler! Or maybe keep that in `Router`?
|
||||
pub struct Route {
|
||||
method: Method,
|
||||
|
@ -50,7 +51,7 @@ impl Route {
|
|||
// FIXME: This is dirty (the comp_to_str and the RootDir thing). Might need
|
||||
// to have my own wrapper arround path strings.
|
||||
// FIXME: Decide whether a component has to be fully variable or not. That
|
||||
// is, whether you can have: /a<a>b/
|
||||
// is, whether you can have: /a<a>b/ or even /<a>:<b>/
|
||||
// TODO: Don't return a Vec...take in an &mut [&'a str] (no alloc!)
|
||||
pub fn get_params<'a>(&self, uri: &'a str) -> Vec<&'a str> {
|
||||
let mut result = Vec::with_capacity(self.component_count());
|
||||
|
|
|
@ -58,7 +58,7 @@ fn get_route_params(ecx: &mut ExtCtxt, meta_item: &MetaItem) -> Params {
|
|||
let params: &Vec<P<MetaItem>> = match meta_item.node {
|
||||
MetaItemKind::List(_, ref params) => params,
|
||||
_ => ecx.span_fatal(meta_item.span,
|
||||
"incorrect use of macro. correct form is: #[demo(...)]"),
|
||||
"Incorrect use of macro. correct form is: #[route(...)]"),
|
||||
};
|
||||
|
||||
// Ensure we can unwrap the k = v params.
|
||||
|
@ -117,7 +117,6 @@ 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();
|
||||
|
@ -125,6 +124,7 @@ pub fn get_fn_params(ecx: &ExtCtxt, sp: Span, path: &str,
|
|||
let mut matching = false;
|
||||
|
||||
// Collect all of the params in the path and insert into HashSet.
|
||||
// TODO: Move this logic into main library.
|
||||
let mut start = 0;
|
||||
for (i, c) in path.char_indices() {
|
||||
match c {
|
||||
|
@ -147,6 +147,7 @@ pub fn get_fn_params(ecx: &ExtCtxt, sp: Span, path: &str,
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: This should stay here, though.
|
||||
// Ensure every param in the function declaration is in `path`. Also add
|
||||
// each param name in the declaration to the result vector.
|
||||
let mut result = vec![];
|
||||
|
|
Loading…
Reference in New Issue