mirror of https://github.com/rwf2/Rocket.git
This commit squash three form-related commits:
Remove form_items function in favor of FormItems iterator. Add specialized `bool` implementation of FromFormValue. Add `&str` implementation of FromFormValue for debugging.
This commit is contained in:
parent
be50ddeca4
commit
23808d00bc
|
@ -23,4 +23,5 @@ members = [
|
||||||
"examples/stream",
|
"examples/stream",
|
||||||
"examples/json",
|
"examples/json",
|
||||||
"examples/handlebars_templates",
|
"examples/handlebars_templates",
|
||||||
|
"examples/form_types",
|
||||||
]
|
]
|
||||||
|
|
|
@ -146,19 +146,6 @@ fn from_form_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substruct
|
||||||
return Err(::rocket::Error::BadParse)
|
return Err(::rocket::Error::BadParse)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Generating the code that checks that the number of fields is correct.
|
|
||||||
let num_fields = fields_and_types.len();
|
|
||||||
let initial_block = quote_block!(cx, {
|
|
||||||
let mut items = [("", ""); $num_fields];
|
|
||||||
let form_count = ::rocket::form::form_items($arg, &mut items);
|
|
||||||
// if form_count != items.len() {
|
|
||||||
// println!("\t Form parse: Wrong number of items!");
|
|
||||||
// $return_err_stmt;
|
|
||||||
// };
|
|
||||||
});
|
|
||||||
|
|
||||||
stmts.extend(initial_block.unwrap().stmts);
|
|
||||||
|
|
||||||
// Generate the let bindings for parameters that will be unwrapped and
|
// Generate the let bindings for parameters that will be unwrapped and
|
||||||
// placed into the final struct. They start out as `None` and are changed
|
// placed into the final struct. They start out as `None` and are changed
|
||||||
// to Some when a parse completes, or some default value if the parse was
|
// to Some when a parse completes, or some default value if the parse was
|
||||||
|
@ -178,17 +165,20 @@ fn from_form_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substruct
|
||||||
arms.push(quote_tokens!(cx,
|
arms.push(quote_tokens!(cx,
|
||||||
$id_str => $ident = match ::rocket::form::FromFormValue::parse(v) {
|
$id_str => $ident = match ::rocket::form::FromFormValue::parse(v) {
|
||||||
Ok(v) => Some(v),
|
Ok(v) => Some(v),
|
||||||
Err(_) => $return_err_stmt
|
Err(e) => {
|
||||||
|
println!("\tError parsing form value '{}': {:?}", $id_str, e);
|
||||||
|
$return_err_stmt
|
||||||
|
}
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// The actual match statement. Uses the $arms generated above.
|
// The actual match statement. Iterate through all of the fields in the form
|
||||||
|
// and use the $arms generated above.
|
||||||
stmts.push(quote_stmt!(cx,
|
stmts.push(quote_stmt!(cx,
|
||||||
for &(k, v) in &items[..form_count] {
|
for (k, v) in ::rocket::form::FormItems($arg) {
|
||||||
match k {
|
match k {
|
||||||
$arms
|
$arms
|
||||||
// Return error when a field is in the form but not in struct.
|
|
||||||
_ => {
|
_ => {
|
||||||
println!("\t{}={} has no matching field in struct.", k, v);
|
println!("\t{}={} has no matching field in struct.", k, v);
|
||||||
$return_err_stmt
|
$return_err_stmt
|
||||||
|
@ -201,12 +191,18 @@ fn from_form_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substruct
|
||||||
// that each parameter actually is Some() or has a default value.
|
// that each parameter actually is Some() or has a default value.
|
||||||
let mut failure_conditions = vec![];
|
let mut failure_conditions = vec![];
|
||||||
for (i, &(ref ident, ty)) in (&fields_and_types).iter().enumerate() {
|
for (i, &(ref ident, ty)) in (&fields_and_types).iter().enumerate() {
|
||||||
|
// Pushing an "||" (or) between every condition.
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
failure_conditions.push(quote_tokens!(cx, ||));
|
failure_conditions.push(quote_tokens!(cx, ||));
|
||||||
}
|
}
|
||||||
|
|
||||||
failure_conditions.push(quote_tokens!(cx, $ident.is_none() &&
|
failure_conditions.push(quote_tokens!(cx,
|
||||||
<$ty as ::rocket::form::FromFormValue>::default().is_none()));
|
if $ident.is_none() &&
|
||||||
|
<$ty as ::rocket::form::FromFormValue>::default().is_none() {
|
||||||
|
println!("\t'{}' did not parse.", stringify!($ident));
|
||||||
|
true
|
||||||
|
} else { false }
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// The fields of the struct, which are just the let bindings declared above
|
// The fields of the struct, which are just the let bindings declared above
|
||||||
|
@ -225,7 +221,6 @@ fn from_form_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substruct
|
||||||
let self_ident = substr.type_ident;
|
let self_ident = substr.type_ident;
|
||||||
let final_block = quote_block!(cx, {
|
let final_block = quote_block!(cx, {
|
||||||
if $failure_conditions {
|
if $failure_conditions {
|
||||||
println!("\tOne of the fields didn't parse.");
|
|
||||||
$return_err_stmt;
|
$return_err_stmt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,13 +44,15 @@ Here's a quick summary of what it does: first, on lines 7 - 10, it declares the
|
||||||
`hello` route to `GET /<name>/<age>`, which returns a `String` formatted with
|
`hello` route to `GET /<name>/<age>`, which returns a `String` formatted with
|
||||||
`name` and `age` from the dynamic path. Then, in the `main` function, it creates
|
`name` and `age` from the dynamic path. Then, in the `main` function, it creates
|
||||||
a new `Rocket` instance, mounts the `hello` route at `"/hello"`, and launches
|
a new `Rocket` instance, mounts the `hello` route at `"/hello"`, and launches
|
||||||
the application. That's it! Let's break it down.
|
the application.
|
||||||
|
|
||||||
Let's start with lines 1 and 2. Rocket depends on Rust nightly; it makes
|
That's it! Let's break this down.
|
||||||
extensive use of Rust's code generation facilities through compiler plugins.
|
|
||||||
Plugins are still experimental, so we have to tell Rust that we're okay with
|
We'll start with lines 1 and 2. Rocket depends on the latest version Rust
|
||||||
that by writing `#![feature(plugin)]`. We also have to tell the compiler to use
|
nightly; it makes extensive use of Rust's code generation facilities through
|
||||||
Rocket's code generation crate during compilation with
|
compiler plugins. Plugins are still experimental, so we have to tell Rust that
|
||||||
|
we're okay with that by writing `#![feature(plugin)]`. We also have to tell the
|
||||||
|
compiler to use Rocket's code generation crate during compilation with
|
||||||
`#![plugin(rocket_codegen)]`. Lines 4 and 5 bring `rocket::Rocket` into the
|
`#![plugin(rocket_codegen)]`. Lines 4 and 5 bring `rocket::Rocket` into the
|
||||||
namespace.
|
namespace.
|
||||||
|
|
||||||
|
@ -60,7 +62,7 @@ The fun begins on line 7, where the `hello` route and request handler are
|
||||||
declared.
|
declared.
|
||||||
|
|
||||||
Rocket applications are composed primarily of request handlers and routes. A
|
Rocket applications are composed primarily of request handlers and routes. A
|
||||||
_request handler_ is a function that takes an arbitrary number of parameters and
|
_request handler_ is a function that takes an arbitrary number of arguments and
|
||||||
returns a response. A _route_ is a combination of:
|
returns a response. A _route_ is a combination of:
|
||||||
|
|
||||||
* A set of parameters to match an incoming request against.
|
* A set of parameters to match an incoming request against.
|
||||||
|
@ -81,10 +83,10 @@ You can also use `put`, `post`, `delete`, and `patch` in place of `get`.
|
||||||
|
|
||||||
## Dynamic Paths
|
## Dynamic Paths
|
||||||
|
|
||||||
The `hello` route declaration on line 7 tells Rocket that the `hello` function
|
The `hello` route declaration beginning on line 7 of our example applications
|
||||||
will handle HTTP `GET` requests to the `<name>/<age>` path. The handler uses
|
tells Rocket that the `hello` function will handle HTTP `GET` requests to the
|
||||||
`name` and `age` from the path to format and return a `String` to the user. Here
|
`<name>/<age>` path. The handler uses `name` and `age` from the path to format
|
||||||
are lines 7 - 10 again:
|
and return a `String` to the user. Here are lines 7 - 10 again:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
#[get("/<name>/<age>")]
|
#[get("/<name>/<age>")]
|
||||||
|
@ -94,20 +96,20 @@ fn hello(name: &str, age: u8) -> String {
|
||||||
```
|
```
|
||||||
|
|
||||||
The `<name>` and `<age>` parts of the path are _dynamic_: the actual values for
|
The `<name>` and `<age>` parts of the path are _dynamic_: the actual values for
|
||||||
these segments won't be known until someone visits a URL that matches. For
|
these segments won't be known until someone visits a matching URL. For example,
|
||||||
example, if someone visit `Mike/21`, `<name>` will be `"Mike"`, and `<age>` will
|
if someone visit `Mike/21`, `<name>` will be `"Mike"`, and `<age>` will be `21`.
|
||||||
be `21`. If someone else visits `Bob/91`, `<name>` and `<age>` will be `"Bob"`
|
If someone else visits `Bob/91`, `<name>` and `<age>` will be `"Bob"` and
|
||||||
and `91`, respectively. Rocket automatically parses dynamic path segments and
|
`91`, respectively. Rocket automatically parses dynamic path segments and
|
||||||
passes them to the request handler in variables with matching names. This means
|
passes them to the request handler in variables with matching names. This
|
||||||
that `name` and `age` can be used immediately in the handler - no parsing, no
|
means that `name` and `age` can be used immediately in the handler - no
|
||||||
checking.
|
parsing, no checking.
|
||||||
|
|
||||||
But wait: what happens if someone goes to a URL with an `<age>` that isn't a
|
But wait: what happens if someone goes to a URL with an `<age>` that isn't a
|
||||||
valid `u8`? In that case, Rocket doesn't call the handler. Instead, it
|
valid `u8`? In that case, Rocket doesn't call the handler. Instead, it
|
||||||
_forwards_ the request to the next matching route, if any, and ultimately
|
_forwards_ the request to the next matching route, if any, and ultimately
|
||||||
returns a `404` if all of them fail. If you want to know if the user passed in a
|
returns a `404` if all of them fail. If you want to know if the user passed in a
|
||||||
bad `<age>`, simply use a `Result<u8, &str>` or an `Option<u8>` type for `age`
|
bad `<age>`, simply use a `Result<u8, &str>` or an `Option<u8>` type for `age`
|
||||||
instead. For more details on routing, route collisions, and more see the
|
instead. For more details on routing, route collisions, and much more see the
|
||||||
[routing](guide/routing) chapter of the guide.
|
[routing](guide/routing) chapter of the guide.
|
||||||
|
|
||||||
Oh, one more thing before we move on! Notice how dynamic path parameters can be
|
Oh, one more thing before we move on! Notice how dynamic path parameters can be
|
||||||
|
@ -119,19 +121,20 @@ implemented `FromParam` for plenty of types in the standard library. See the
|
||||||
|
|
||||||
## Mounting
|
## Mounting
|
||||||
|
|
||||||
Now that we're set with the `hello` route, let's move on to lines 13 - 14.
|
Now that we understand the `hello` route, let's move on to lines 13 - 14. Before
|
||||||
Before Rocket dispatches requests to a route, the route needs to be _mounted_.
|
Rocket dispatches requests to a route, the route needs to be _mounted_. And
|
||||||
And before we can mount a route, we need an instance of `Rocket`.
|
before we can mount a route, we need an instance of `Rocket`.
|
||||||
|
|
||||||
Mounting a route is like namespacing it. Routes can be mounted any number of
|
Mounting a route is like namespacing it. Routes can be mounted any number of
|
||||||
times. Mounting happens with the `mount` method on a `Rocket` instance, which
|
times. Mounting happens with the `mount` method on a `Rocket` instance, which
|
||||||
itself is created with the `ignite()` static method. The `mount` method takes a
|
itself is created with the `ignite()` static method. The `mount` method takes a
|
||||||
list of route handlers given inside of the `route!` macro. The `route!` macro
|
list of route handlers given inside of the `route!` macro. The `route!` macro
|
||||||
ties Rocket's code generation to your application.
|
ties Rocket's code generation to your application. If you'd like to learn more
|
||||||
|
about the `route!` macro, see the [internals guide](guide/internals).
|
||||||
|
|
||||||
Let's look at lines 13 - 14 again, which we reproduce below:
|
Let's look at lines 13 - 14 again, which we reproduce below:
|
||||||
|
|
||||||
```
|
```rust
|
||||||
let mut rocket = Rocket::ignite();
|
let mut rocket = Rocket::ignite();
|
||||||
rocket.mount(“/hello”, routes![hello]);
|
rocket.mount(“/hello”, routes![hello]);
|
||||||
```
|
```
|
||||||
|
@ -169,7 +172,9 @@ invalid paths and see what happens! This example's complete crate, ready to
|
||||||
|
|
||||||
# Requests
|
# Requests
|
||||||
|
|
||||||
There's a lot more to do with requests. Let's take a closer look.
|
There's a lot more we can do with requests. The [requests](guide/requests)
|
||||||
|
chapter of the guide talks about requests in details. We'll give you a short
|
||||||
|
overview of some of the more important and useful features here.
|
||||||
|
|
||||||
## Forms and Queries
|
## Forms and Queries
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,14 @@ pub trait FromForm<'f>: Sized {
|
||||||
fn from_form_string(s: &'f str) -> Result<Self, Error>;
|
fn from_form_string(s: &'f str) -> Result<Self, Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This implementation should only be ued during debugging!
|
||||||
|
#[doc(hidden)]
|
||||||
|
impl<'f> FromForm<'f> for &'f str {
|
||||||
|
fn from_form_string(s: &'f str) -> Result<Self, Error> {
|
||||||
|
Ok(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait FromFormValue<'v>: Sized {
|
pub trait FromFormValue<'v>: Sized {
|
||||||
type Error;
|
type Error;
|
||||||
|
|
||||||
|
@ -53,6 +61,18 @@ impl<'v> FromFormValue<'v> for String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'v> FromFormValue<'v> for bool {
|
||||||
|
type Error = &'v str;
|
||||||
|
|
||||||
|
fn parse(v: &'v str) -> Result<Self, Self::Error> {
|
||||||
|
match v {
|
||||||
|
"on" | "true" => Ok(true),
|
||||||
|
"off" | "false" => Ok(false),
|
||||||
|
_ => Err(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! impl_with_fromstr {
|
macro_rules! impl_with_fromstr {
|
||||||
($($T:ident),+) => ($(
|
($($T:ident),+) => ($(
|
||||||
impl<'v> FromFormValue<'v> for $T {
|
impl<'v> FromFormValue<'v> for $T {
|
||||||
|
@ -65,8 +85,7 @@ macro_rules! impl_with_fromstr {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_with_fromstr!(f32, f64, isize, i8, i16, i32, i64, usize, u8, u16, u32, u64,
|
impl_with_fromstr!(f32, f64, isize, i8, i16, i32, i64, usize, u8, u16, u32, u64,
|
||||||
bool, IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6,
|
IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6, SocketAddr);
|
||||||
SocketAddr);
|
|
||||||
|
|
||||||
impl<'v, T: FromFormValue<'v>> FromFormValue<'v> for Option<T> {
|
impl<'v, T: FromFormValue<'v>> FromFormValue<'v> for Option<T> {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
@ -117,28 +136,14 @@ impl<'f> Iterator for FormItems<'f> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn form_items<'f>(string: &'f str, items: &mut [(&'f str, &'f str)]) -> usize {
|
|
||||||
let mut param_count = 0;
|
|
||||||
for (i, item) in FormItems(string).take(items.len()).enumerate() {
|
|
||||||
items[i] = item;
|
|
||||||
param_count += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
param_count
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::form_items;
|
use super::FormItems;
|
||||||
|
|
||||||
macro_rules! check_form {
|
macro_rules! check_form {
|
||||||
($string:expr, $expected: expr) => ({
|
($string:expr, $expected: expr) => ({
|
||||||
let mut output = Vec::with_capacity($expected.len());
|
let results: Vec<(&str, &str)> = FormItems($string).collect();
|
||||||
unsafe { output.set_len($expected.len()); }
|
assert_eq!($expected.len(), results.len());
|
||||||
|
|
||||||
let results = output.as_mut_slice();
|
|
||||||
assert_eq!($expected.len(), form_items($string, results));
|
|
||||||
|
|
||||||
for i in 0..results.len() {
|
for i in 0..results.len() {
|
||||||
let (expected_key, actual_key) = ($expected[i].0, results[i].0);
|
let (expected_key, actual_key) = ($expected[i].0, results[i].0);
|
||||||
|
|
Loading…
Reference in New Issue