mirror of https://github.com/rwf2/Rocket.git
Forms are now working!
This commit is contained in:
parent
ba6caad7c8
commit
3e449d2fb9
|
@ -14,21 +14,70 @@ fn user_page(username: &str) -> String {
|
||||||
format!("This is {}'s page.", username)
|
format!("This is {}'s page.", username)
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[derive(FormItem)] // FIXME: Make that happen.
|
// #[derive(FromForm)] // FIXME: Make that happen.
|
||||||
struct UserLogin<'a> {
|
struct UserLogin<'a> {
|
||||||
username: &'a str,
|
username: &'a str,
|
||||||
password: &'a str
|
password: &'a str
|
||||||
}
|
}
|
||||||
|
|
||||||
trait FormItem: Sized {
|
fn form_items<'f>(string: &'f str, items: &mut [(&'f str, &'f str)]) -> usize {
|
||||||
fn from_form_string(s: &str) -> Result<Self, Error>;
|
let mut param_num = 0;
|
||||||
|
let mut rest = string;
|
||||||
|
while rest.len() > 0 && param_num < items.len() {
|
||||||
|
let (key, remainder) = match rest.find('=') {
|
||||||
|
Some(index) => (&rest[..index], &rest[(index + 1)..]),
|
||||||
|
None => return param_num
|
||||||
|
};
|
||||||
|
|
||||||
|
rest = remainder;
|
||||||
|
let (value, remainder) = match rest.find('&') {
|
||||||
|
Some(index) => (&rest[..index], &rest[(index + 1)..]),
|
||||||
|
None => (rest, "")
|
||||||
|
};
|
||||||
|
|
||||||
|
rest = remainder;
|
||||||
|
items[param_num] = (key, value);
|
||||||
|
param_num += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
param_num
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FormItem for UserLogin<'a> {
|
trait FromForm<'f>: Sized {
|
||||||
fn from_form_string(s: &str) -> Result<Self, Error> {
|
fn from_form_string(s: &'f str) -> Result<Self, Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: Add a 'FromFormValue' trait and use it on each field in the form
|
||||||
|
// structure. Should be pretty simple. Implement for all FromStr and then
|
||||||
|
// implement for OptionT: FromFormValue> so forms still exist. Maybe have a way
|
||||||
|
// for FromFormValue types to have an error that says why it didn't work. This
|
||||||
|
// will help for validation. IE, can have a type Range(1, 10) that returns an
|
||||||
|
// enum with one of: TooLow(isize), TooHigh(isize), etc.
|
||||||
|
impl<'f> FromForm<'f> for UserLogin<'f> {
|
||||||
|
fn from_form_string(s: &'f str) -> Result<Self, Error> {
|
||||||
|
let mut items = [("", ""); 2];
|
||||||
|
let form_count = form_items(s, &mut items);
|
||||||
|
if form_count != 2 {
|
||||||
|
return Err(Error::BadParse);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut username = None;
|
||||||
|
let mut password = None;
|
||||||
|
for &(key, value) in &items {
|
||||||
|
match key {
|
||||||
|
"username" => username = Some(value),
|
||||||
|
"password" => password = Some(value),
|
||||||
|
_ => return Err(Error::BadParse)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if username.is_none() || password.is_none() {
|
||||||
|
return Err(Error::BadParse);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(UserLogin {
|
Ok(UserLogin {
|
||||||
username: "this",
|
username: username.unwrap(),
|
||||||
password: "that"
|
password: password.unwrap()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,7 +88,7 @@ impl<'a> FormItem for UserLogin<'a> {
|
||||||
fn login(user: UserLogin) -> Result<Redirect, String> {
|
fn login(user: UserLogin) -> Result<Redirect, String> {
|
||||||
match user.username {
|
match user.username {
|
||||||
"Sergio" => match user.password {
|
"Sergio" => match user.password {
|
||||||
"password" => Ok(Redirect::other("/user/some_name")),
|
"password" => Ok(Redirect::other("/user/Sergio")),
|
||||||
_ => Err("Wrong password!".to_string())
|
_ => Err("Wrong password!".to_string())
|
||||||
},
|
},
|
||||||
_ => Err(format!("Unrecognized user, '{}'.", user.username))
|
_ => Err(format!("Unrecognized user, '{}'.", user.username))
|
||||||
|
@ -51,3 +100,55 @@ fn main() {
|
||||||
rocket.mount("/", routes![files::index, files::files, user_page, login]);
|
rocket.mount("/", routes![files::index, files::files, user_page, login]);
|
||||||
rocket.launch();
|
rocket.launch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::form_items;
|
||||||
|
|
||||||
|
macro_rules! check_form {
|
||||||
|
($string:expr, $expected: expr) => ({
|
||||||
|
let mut output = Vec::with_capacity($expected.len());
|
||||||
|
unsafe { output.set_len($expected.len()); }
|
||||||
|
|
||||||
|
let results = output.as_mut_slice();
|
||||||
|
assert_eq!($expected.len(), form_items($string, results));
|
||||||
|
|
||||||
|
for i in 0..results.len() {
|
||||||
|
let (expected_key, actual_key) = ($expected[i].0, results[i].0);
|
||||||
|
let (expected_val, actual_val) = ($expected[i].1, results[i].1);
|
||||||
|
|
||||||
|
assert!(expected_key == actual_key,
|
||||||
|
"key [{}] mismatch: expected {}, got {}",
|
||||||
|
i, expected_key, actual_key);
|
||||||
|
|
||||||
|
assert!(expected_val == actual_val,
|
||||||
|
"val [{}] mismatch: expected {}, got {}",
|
||||||
|
i, expected_val, actual_val);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_form_string() {
|
||||||
|
let results = &[("username", "user"), ("password", "pass")];
|
||||||
|
check_form!("username=user&password=pass", results);
|
||||||
|
|
||||||
|
let results = &[("user", "user"), ("user", "pass")];
|
||||||
|
check_form!("user=user&user=pass", results);
|
||||||
|
|
||||||
|
let results = &[("user", ""), ("password", "pass")];
|
||||||
|
check_form!("user=&password=pass", results);
|
||||||
|
|
||||||
|
let results = &[("", ""), ("", "")];
|
||||||
|
check_form!("=&=", results);
|
||||||
|
|
||||||
|
let results = &[("a", "b")];
|
||||||
|
check_form!("a=b", results);
|
||||||
|
|
||||||
|
let results = &[("a", "b")];
|
||||||
|
check_form!("a=b&a", results);
|
||||||
|
|
||||||
|
let results = &[("a", "b"), ("a", "")];
|
||||||
|
check_form!("a=b&a=", results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -5,14 +5,16 @@ pub use hyper::server::Request as HyperRequest;
|
||||||
|
|
||||||
pub struct Request<'a> {
|
pub struct Request<'a> {
|
||||||
params: Vec<&'a str>,
|
params: Vec<&'a str>,
|
||||||
uri: &'a str,
|
pub uri: &'a str,
|
||||||
|
pub data: &'a [u8]
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Request<'a> {
|
impl<'a> Request<'a> {
|
||||||
pub fn new(params: Vec<&'a str>, uri: &'a str) -> Request<'a> {
|
pub fn new(params: Vec<&'a str>, uri: &'a str, data: &'a [u8]) -> Request<'a> {
|
||||||
Request {
|
Request {
|
||||||
params: params,
|
params: params,
|
||||||
uri: uri
|
uri: uri,
|
||||||
|
data: data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ impl HyperHandler for Rocket {
|
||||||
let route = self.router.route(method, uri_str);
|
let route = self.router.route(method, uri_str);
|
||||||
let mut response = route.map_or(Response::not_found(), |route| {
|
let mut response = route.map_or(Response::not_found(), |route| {
|
||||||
let params = route.get_params(uri_str);
|
let params = route.get_params(uri_str);
|
||||||
let request = Request::new(params, uri_str);
|
let request = Request::new(params, uri_str, &buf);
|
||||||
(route.handler)(request)
|
(route.handler)(request)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -154,19 +154,15 @@ impl<'a> Iterator for Segments<'a> {
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
// Find the start of the next segment (first that's not '/').
|
// Find the start of the next segment (first that's not '/').
|
||||||
let s = self.0.chars().enumerate().skip_while(|&(_, c)| c == '/').next();
|
let i = match self.0.find(|c| c != '/') {
|
||||||
if s.is_none() {
|
Some(index) => index,
|
||||||
return None;
|
None => return None
|
||||||
}
|
};
|
||||||
|
|
||||||
// i is the index of the first character that's not '/'
|
|
||||||
let (i, _) = s.unwrap();
|
|
||||||
|
|
||||||
// Get the index of the first character that _is_ a '/' after start.
|
// Get the index of the first character that _is_ a '/' after start.
|
||||||
// j = index of first character after i (hence the i +) that's not a '/'
|
// j = index of first character after i (hence the i +) that's not a '/'
|
||||||
let rest = &self.0[i..];
|
let rest = &self.0[i..];
|
||||||
let mut end_iter = rest.chars().enumerate().skip_while(|&(_, c)| c != '/');
|
let j = rest.find('/').map_or(self.0.len(), |j| i + j);
|
||||||
let j = end_iter.next().map_or(self.0.len(), |(j, _)| i + j);
|
|
||||||
|
|
||||||
// Save the result, update the iterator, and return!
|
// Save the result, update the iterator, and return!
|
||||||
let result = Some(&self.0[i..j]);
|
let result = Some(&self.0[i..j]);
|
||||||
|
|
|
@ -310,10 +310,20 @@ fn get_form_stmt(ecx: &ExtCtxt, fn_args: &mut Vec<SimpleArg>,
|
||||||
quote_stmt!(ecx,
|
quote_stmt!(ecx,
|
||||||
// TODO: Actually get the form parameters to pass into from_form_string.
|
// TODO: Actually get the form parameters to pass into from_form_string.
|
||||||
// Alternatively, pass in some already parsed thing.
|
// Alternatively, pass in some already parsed thing.
|
||||||
let $param_ident: $param_ty = match FormItem::from_form_string("test string") {
|
let $param_ident: $param_ty = {
|
||||||
Ok(v) => v,
|
let form_string = std::str::from_utf8(_req.data);
|
||||||
Err(_) => return rocket::Response::not_found()
|
if form_string.is_err() {
|
||||||
|
return rocket::Response::not_found()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
match FromForm::from_form_string(form_string.unwrap()) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(_) => {
|
||||||
|
println!("\t=> Form failed to parse.");
|
||||||
|
return rocket::Response::not_found()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue