mirror of https://github.com/rwf2/Rocket.git
Actually useful forms!
This commit is contained in:
parent
3e449d2fb9
commit
b7d22d58f7
|
@ -8,6 +8,7 @@ mod files;
|
||||||
use rocket::Rocket;
|
use rocket::Rocket;
|
||||||
use rocket::response::Redirect;
|
use rocket::response::Redirect;
|
||||||
use rocket::Error;
|
use rocket::Error;
|
||||||
|
use rocket::form::{FromForm, FromFormValue, form_items};
|
||||||
|
|
||||||
#[route(GET, path = "/user/<username>")]
|
#[route(GET, path = "/user/<username>")]
|
||||||
fn user_page(username: &str) -> String {
|
fn user_page(username: &str) -> String {
|
||||||
|
@ -17,56 +18,37 @@ fn user_page(username: &str) -> String {
|
||||||
// #[derive(FromForm)] // 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,
|
||||||
|
age: Result<isize, &'a str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn form_items<'f>(string: &'f str, items: &mut [(&'f str, &'f str)]) -> usize {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
trait FromForm<'f>: Sized {
|
|
||||||
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
|
// will help for validation. IE, can have a type Range(1, 10) that returns an
|
||||||
// enum with one of: TooLow(isize), TooHigh(isize), etc.
|
// enum with one of: TooLow(isize), TooHigh(isize), etc.
|
||||||
impl<'f> FromForm<'f> for UserLogin<'f> {
|
impl<'f> FromForm<'f> for UserLogin<'f> {
|
||||||
fn from_form_string(s: &'f str) -> Result<Self, Error> {
|
fn from_form_string(s: &'f str) -> Result<Self, Error> {
|
||||||
let mut items = [("", ""); 2];
|
let mut items = [("", ""); 3];
|
||||||
let form_count = form_items(s, &mut items);
|
let form_count = form_items(s, &mut items);
|
||||||
if form_count != 2 {
|
if form_count != items.len() {
|
||||||
return Err(Error::BadParse);
|
return Err(Error::BadParse);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut username = None;
|
let mut username: Option<&'f str> = None;
|
||||||
let mut password = None;
|
let mut password: Option<&'f str> = None;
|
||||||
|
let mut age: Option<Result<isize, &'f str>> = None;
|
||||||
for &(key, value) in &items {
|
for &(key, value) in &items {
|
||||||
match key {
|
match key {
|
||||||
"username" => username = Some(value),
|
"username" => username = match FromFormValue::parse(value) {
|
||||||
"password" => password = Some(value),
|
Ok(v) => Some(v),
|
||||||
|
Err(_) => return Err(Error::BadParse)
|
||||||
|
},
|
||||||
|
"password" => password = match FromFormValue::parse(value) {
|
||||||
|
Ok(v) => Some(v),
|
||||||
|
Err(_) => return Err(Error::BadParse)
|
||||||
|
},
|
||||||
|
"age" => age = match FromFormValue::parse(value) {
|
||||||
|
Ok(v) => Some(v),
|
||||||
|
Err(_) => return Err(Error::BadParse)
|
||||||
|
},
|
||||||
_ => return Err(Error::BadParse)
|
_ => return Err(Error::BadParse)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,7 +59,8 @@ impl<'f> FromForm<'f> for UserLogin<'f> {
|
||||||
|
|
||||||
Ok(UserLogin {
|
Ok(UserLogin {
|
||||||
username: username.unwrap(),
|
username: username.unwrap(),
|
||||||
password: password.unwrap()
|
password: password.unwrap(),
|
||||||
|
age: age.unwrap(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,6 +69,15 @@ impl<'f> FromForm<'f> for UserLogin<'f> {
|
||||||
// FIXME: fn login<'a>(user: UserLogin<'a>)
|
// FIXME: fn login<'a>(user: UserLogin<'a>)
|
||||||
#[route(POST, path = "/login", form = "<user>")]
|
#[route(POST, path = "/login", form = "<user>")]
|
||||||
fn login(user: UserLogin) -> Result<Redirect, String> {
|
fn login(user: UserLogin) -> Result<Redirect, String> {
|
||||||
|
if user.age.is_err() {
|
||||||
|
let input = user.age.unwrap_err();
|
||||||
|
return Err(format!("'{}' is not a valid age integer.", input));
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.age.unwrap() < 20 {
|
||||||
|
return Err(format!("Sorry, {} is too young!", user.age.unwrap()));
|
||||||
|
}
|
||||||
|
|
||||||
match user.username {
|
match user.username {
|
||||||
"Sergio" => match user.password {
|
"Sergio" => match user.password {
|
||||||
"password" => Ok(Redirect::other("/user/Sergio")),
|
"password" => Ok(Redirect::other("/user/Sergio")),
|
||||||
|
@ -100,55 +92,3 @@ 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -3,5 +3,6 @@
|
||||||
<form action="/login" method="post" accept-charset="utf-8">
|
<form action="/login" method="post" accept-charset="utf-8">
|
||||||
Username:<input type="text" name="username">
|
Username:<input type="text" name="username">
|
||||||
Password:<input type="password" name="password">
|
Password:<input type="password" name="password">
|
||||||
|
Age:<input type="number" name="age">
|
||||||
<input type="submit" value="Login">
|
<input type="submit" value="Login">
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
BadMethod,
|
BadMethod,
|
||||||
BadParse,
|
BadParse,
|
||||||
|
|
|
@ -11,6 +11,7 @@ mod router;
|
||||||
mod rocket;
|
mod rocket;
|
||||||
mod codegen;
|
mod codegen;
|
||||||
|
|
||||||
|
pub mod form;
|
||||||
pub mod request;
|
pub mod request;
|
||||||
pub mod response;
|
pub mod response;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue