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/json",
|
||||
"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)
|
||||
);
|
||||
|
||||
// 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
|
||||
// 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
|
||||
|
@ -178,17 +165,20 @@ fn from_form_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substruct
|
|||
arms.push(quote_tokens!(cx,
|
||||
$id_str => $ident = match ::rocket::form::FromFormValue::parse(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,
|
||||
for &(k, v) in &items[..form_count] {
|
||||
for (k, v) in ::rocket::form::FormItems($arg) {
|
||||
match k {
|
||||
$arms
|
||||
// Return error when a field is in the form but not in struct.
|
||||
_ => {
|
||||
println!("\t{}={} has no matching field in struct.", k, v);
|
||||
$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.
|
||||
let mut failure_conditions = vec![];
|
||||
for (i, &(ref ident, ty)) in (&fields_and_types).iter().enumerate() {
|
||||
// Pushing an "||" (or) between every condition.
|
||||
if i > 0 {
|
||||
failure_conditions.push(quote_tokens!(cx, ||));
|
||||
}
|
||||
|
||||
failure_conditions.push(quote_tokens!(cx, $ident.is_none() &&
|
||||
<$ty as ::rocket::form::FromFormValue>::default().is_none()));
|
||||
failure_conditions.push(quote_tokens!(cx,
|
||||
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
|
||||
|
@ -225,7 +221,6 @@ fn from_form_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substruct
|
|||
let self_ident = substr.type_ident;
|
||||
let final_block = quote_block!(cx, {
|
||||
if $failure_conditions {
|
||||
println!("\tOne of the fields didn't parse.");
|
||||
$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
|
||||
`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
|
||||
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
|
||||
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
|
||||
that by writing `#![feature(plugin)]`. We also have to tell the compiler to use
|
||||
Rocket's code generation crate during compilation with
|
||||
That's it! Let's break this down.
|
||||
|
||||
We'll start with lines 1 and 2. Rocket depends on the latest version Rust
|
||||
nightly; it makes 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 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
|
||||
namespace.
|
||||
|
||||
|
@ -60,7 +62,7 @@ The fun begins on line 7, where the `hello` route and request handler are
|
|||
declared.
|
||||
|
||||
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:
|
||||
|
||||
* 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
|
||||
|
||||
The `hello` route declaration on line 7 tells Rocket that the `hello` function
|
||||
will handle HTTP `GET` requests to the `<name>/<age>` path. The handler uses
|
||||
`name` and `age` from the path to format and return a `String` to the user. Here
|
||||
are lines 7 - 10 again:
|
||||
The `hello` route declaration beginning on line 7 of our example applications
|
||||
tells Rocket that the `hello` function will handle HTTP `GET` requests to the
|
||||
`<name>/<age>` path. The handler uses `name` and `age` from the path to format
|
||||
and return a `String` to the user. Here are lines 7 - 10 again:
|
||||
|
||||
```rust
|
||||
#[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
|
||||
these segments won't be known until someone visits a URL that matches. For
|
||||
example, if someone visit `Mike/21`, `<name>` will be `"Mike"`, and `<age>` will
|
||||
be `21`. If someone else visits `Bob/91`, `<name>` and `<age>` will be `"Bob"`
|
||||
and `91`, respectively. Rocket automatically parses dynamic path segments and
|
||||
passes them to the request handler in variables with matching names. This means
|
||||
that `name` and `age` can be used immediately in the handler - no parsing, no
|
||||
checking.
|
||||
these segments won't be known until someone visits a matching URL. For example,
|
||||
if someone visit `Mike/21`, `<name>` will be `"Mike"`, and `<age>` will be `21`.
|
||||
If someone else visits `Bob/91`, `<name>` and `<age>` will be `"Bob"` and
|
||||
`91`, respectively. Rocket automatically parses dynamic path segments and
|
||||
passes them to the request handler in variables with matching names. This
|
||||
means that `name` and `age` can be used immediately in the handler - no
|
||||
parsing, no checking.
|
||||
|
||||
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
|
||||
_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
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
Now that we're set with the `hello` route, let's move on to lines 13 - 14.
|
||||
Before Rocket dispatches requests to a route, the route needs to be _mounted_.
|
||||
And before we can mount a route, we need an instance of `Rocket`.
|
||||
Now that we understand the `hello` route, let's move on to lines 13 - 14. Before
|
||||
Rocket dispatches requests to a route, the route needs to be _mounted_. And
|
||||
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
|
||||
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
|
||||
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:
|
||||
|
||||
```
|
||||
```rust
|
||||
let mut rocket = Rocket::ignite();
|
||||
rocket.mount(“/hello”, routes![hello]);
|
||||
```
|
||||
|
@ -169,7 +172,9 @@ invalid paths and see what happens! This example's complete crate, ready to
|
|||
|
||||
# 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
|
||||
|
||||
|
|
|
@ -8,6 +8,14 @@ pub trait FromForm<'f>: Sized {
|
|||
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 {
|
||||
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 {
|
||||
($($T:ident),+) => ($(
|
||||
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,
|
||||
bool, IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6,
|
||||
SocketAddr);
|
||||
IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6, SocketAddr);
|
||||
|
||||
impl<'v, T: FromFormValue<'v>> FromFormValue<'v> for Option<T> {
|
||||
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)]
|
||||
mod test {
|
||||
use super::form_items;
|
||||
use super::FormItems;
|
||||
|
||||
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));
|
||||
let results: Vec<(&str, &str)> = FormItems($string).collect();
|
||||
assert_eq!($expected.len(), results.len());
|
||||
|
||||
for i in 0..results.len() {
|
||||
let (expected_key, actual_key) = ($expected[i].0, results[i].0);
|
||||
|
|
Loading…
Reference in New Issue