mirror of https://github.com/rwf2/Rocket.git
This commit changes parsing traits and documents some of the core library:
* All From* trait methods are now named like the trait. * All From* traits have an associated Error type. * Document all of the `form` module. * Add codegen tests for auto-derived forms. * The param parsing traits now live under Request.
This commit is contained in:
parent
76cbc14d23
commit
008605bec7
|
@ -92,9 +92,10 @@ Contributions are absolutely, positively welcome and encouraged! Contributions
|
||||||
come in many forms. You could:
|
come in many forms. You could:
|
||||||
|
|
||||||
1. Submit a feature request or bug report as an [issue](https://github.com/SergioBenitez/Rocket/issues).
|
1. Submit a feature request or bug report as an [issue](https://github.com/SergioBenitez/Rocket/issues).
|
||||||
2. Comment on [issues that require
|
2. Ask for improved documentation as an [issue](https://github.com/SergioBenitez/Rocket/issues).
|
||||||
|
3. Comment on [issues that require
|
||||||
feedback](https://github.com/SergioBenitez/Rocket/issues?q=is%3Aissue+is%3Aopen+label%3A%22feedback+wanted%22).
|
feedback](https://github.com/SergioBenitez/Rocket/issues?q=is%3Aissue+is%3Aopen+label%3A%22feedback+wanted%22).
|
||||||
3. Contribute code via [pull requests](https://github.com/SergioBenitez/Rocket/pulls).
|
4. Contribute code via [pull requests](https://github.com/SergioBenitez/Rocket/pulls).
|
||||||
|
|
||||||
We aim to keep Rocket's code quality at the highest level. This means that any
|
We aim to keep Rocket's code quality at the highest level. This means that any
|
||||||
code you contribute must be:
|
code you contribute must be:
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
use syntax::ext::base::{Annotatable, ExtCtxt};
|
use syntax::ext::base::{Annotatable, ExtCtxt};
|
||||||
use syntax::print::pprust::{stmt_to_string};
|
use syntax::print::pprust::{stmt_to_string};
|
||||||
|
use syntax::parse::token::{str_to_ident};
|
||||||
use syntax::ast::{ItemKind, Expr, MetaItem, Mutability, VariantData};
|
use syntax::ast::{ItemKind, Expr, MetaItem, Mutability, VariantData};
|
||||||
use syntax::codemap::Span;
|
use syntax::codemap::Span;
|
||||||
use syntax::ext::build::AstBuilder;
|
use syntax::ext::build::AstBuilder;
|
||||||
|
@ -58,6 +59,9 @@ pub fn from_form_derive(ecx: &mut ExtCtxt, span: Span, meta_item: &MetaItem,
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// The error type in the derived implementation.
|
||||||
|
let error_type = ty::Ty::Literal(ty::Path::new(vec!["rocket", "Error"]));
|
||||||
|
|
||||||
let trait_def = TraitDef {
|
let trait_def = TraitDef {
|
||||||
is_unsafe: false,
|
is_unsafe: false,
|
||||||
supports_unions: false,
|
supports_unions: false,
|
||||||
|
@ -88,9 +92,7 @@ pub fn from_form_derive(ecx: &mut ExtCtxt, span: Span, meta_item: &MetaItem,
|
||||||
lifetime: None,
|
lifetime: None,
|
||||||
params: vec![
|
params: vec![
|
||||||
Box::new(ty::Ty::Self_),
|
Box::new(ty::Ty::Self_),
|
||||||
Box::new(ty::Ty::Literal(
|
Box::new(error_type.clone())
|
||||||
ty::Path::new(vec!["rocket", "Error"])
|
|
||||||
)),
|
|
||||||
],
|
],
|
||||||
global: true,
|
global: true,
|
||||||
}
|
}
|
||||||
|
@ -101,7 +103,9 @@ pub fn from_form_derive(ecx: &mut ExtCtxt, span: Span, meta_item: &MetaItem,
|
||||||
unify_fieldless_variants: false,
|
unify_fieldless_variants: false,
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
associated_types: vec![],
|
associated_types: vec![
|
||||||
|
(str_to_ident("Error"), error_type.clone())
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
trait_def.expand(ecx, meta_item, annotated, push);
|
trait_def.expand(ecx, meta_item, annotated, push);
|
||||||
|
@ -164,12 +168,14 @@ fn from_form_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substruct
|
||||||
let ident_string = ident.to_string();
|
let ident_string = ident.to_string();
|
||||||
let id_str = ident_string.as_str();
|
let id_str = ident_string.as_str();
|
||||||
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::from_form_value(v) {
|
||||||
Ok(v) => Some(v),
|
Ok(v) => Some(v),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("\tError parsing form value '{}': {:?}", $id_str, e);
|
println!("\tError parsing form val '{}': {:?}", $id_str, e);
|
||||||
$return_err_stmt
|
$return_err_stmt
|
||||||
}
|
}
|
||||||
|
};
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
#![feature(plugin, custom_derive)]
|
||||||
|
#![plugin(rocket_codegen)]
|
||||||
|
|
||||||
|
extern crate rocket;
|
||||||
|
|
||||||
|
use rocket::form::FromForm;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, FromForm)]
|
||||||
|
struct TodoTask {
|
||||||
|
description: String,
|
||||||
|
completed: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Make deriving `FromForm` for this enum possible.
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
enum FormOption {
|
||||||
|
A, B, C
|
||||||
|
}
|
||||||
|
|
||||||
|
use rocket::form::FromFormValue;
|
||||||
|
|
||||||
|
impl<'v> FromFormValue<'v> for FormOption {
|
||||||
|
type Error = &'v str;
|
||||||
|
|
||||||
|
fn from_form_value(v: &'v str) -> Result<Self, Self::Error> {
|
||||||
|
let variant = match v {
|
||||||
|
"a" => FormOption::A,
|
||||||
|
"b" => FormOption::B,
|
||||||
|
"c" => FormOption::C,
|
||||||
|
_ => return Err(v)
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(variant)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, FromForm)]
|
||||||
|
struct FormInput<'r> {
|
||||||
|
checkbox: bool,
|
||||||
|
number: usize,
|
||||||
|
radio: FormOption,
|
||||||
|
password: &'r str,
|
||||||
|
textarea: String,
|
||||||
|
select: FormOption,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, FromForm)]
|
||||||
|
struct DefaultInput<'r> {
|
||||||
|
arg: Option<&'r str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Same number of arguments: simple case.
|
||||||
|
let task = TodoTask::from_form_string("description=Hello&completed=on");
|
||||||
|
assert_eq!(task, Ok(TodoTask {
|
||||||
|
description: "Hello".to_string(),
|
||||||
|
completed: true
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Argument in string but not in form.
|
||||||
|
let task = TodoTask::from_form_string("other=a&description=Hello&completed=on");
|
||||||
|
assert!(task.is_err());
|
||||||
|
|
||||||
|
let form_string = &[
|
||||||
|
"password=testing", "checkbox=off", "checkbox=on", "number=10",
|
||||||
|
"checkbox=off", "textarea=", "select=a", "radio=c",
|
||||||
|
].join("&");
|
||||||
|
|
||||||
|
let input = FormInput::from_form_string(&form_string);
|
||||||
|
assert_eq!(input, Ok(FormInput {
|
||||||
|
checkbox: false,
|
||||||
|
number: 10,
|
||||||
|
radio: FormOption::C,
|
||||||
|
password: "testing",
|
||||||
|
textarea: "".to_string(),
|
||||||
|
select: FormOption::A,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Argument not in string with default in form.
|
||||||
|
let default = DefaultInput::from_form_string("");
|
||||||
|
assert_eq!(default, Ok(DefaultInput {
|
||||||
|
arg: None
|
||||||
|
}));
|
||||||
|
}
|
|
@ -25,7 +25,7 @@ struct UserLogin<'r> {
|
||||||
impl<'v> FromFormValue<'v> for StrongPassword<'v> {
|
impl<'v> FromFormValue<'v> for StrongPassword<'v> {
|
||||||
type Error = &'static str;
|
type Error = &'static str;
|
||||||
|
|
||||||
fn parse(v: &'v str) -> Result<Self, Self::Error> {
|
fn from_form_value(v: &'v str) -> Result<Self, Self::Error> {
|
||||||
if v.len() < 8 {
|
if v.len() < 8 {
|
||||||
Err("Too short!")
|
Err("Too short!")
|
||||||
} else {
|
} else {
|
||||||
|
@ -37,8 +37,8 @@ impl<'v> FromFormValue<'v> for StrongPassword<'v> {
|
||||||
impl<'v> FromFormValue<'v> for AdultAge {
|
impl<'v> FromFormValue<'v> for AdultAge {
|
||||||
type Error = &'static str;
|
type Error = &'static str;
|
||||||
|
|
||||||
fn parse(v: &'v str) -> Result<Self, Self::Error> {
|
fn from_form_value(v: &'v str) -> Result<Self, Self::Error> {
|
||||||
let age = match isize::parse(v) {
|
let age = match isize::from_form_value(v) {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(_) => return Err("Age value is not a number."),
|
Err(_) => return Err("Age value is not a number."),
|
||||||
};
|
};
|
||||||
|
|
|
@ -17,7 +17,7 @@ enum FormOption {
|
||||||
impl<'v> FromFormValue<'v> for FormOption {
|
impl<'v> FromFormValue<'v> for FormOption {
|
||||||
type Error = &'v str;
|
type Error = &'v str;
|
||||||
|
|
||||||
fn parse(v: &'v str) -> Result<Self, Self::Error> {
|
fn from_form_value(v: &'v str) -> Result<Self, Self::Error> {
|
||||||
let variant = match v {
|
let variant = match v {
|
||||||
"a" => FormOption::A,
|
"a" => FormOption::A,
|
||||||
"b" => FormOption::B,
|
"b" => FormOption::B,
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
extern crate rocket;
|
extern crate rocket;
|
||||||
|
|
||||||
use rocket::{Rocket, Error};
|
use rocket::Rocket;
|
||||||
|
|
||||||
#[derive(FromForm)]
|
#[derive(FromForm)]
|
||||||
struct Person<'r> {
|
struct Person<'r> {
|
||||||
|
|
145
lib/src/form.rs
145
lib/src/form.rs
|
@ -1,25 +1,111 @@
|
||||||
|
//! Types and traits to handle form processing.
|
||||||
|
//!
|
||||||
|
//! In general, you will deal with forms in Rocket via the `form` parameter in
|
||||||
|
//! routes:
|
||||||
|
//!
|
||||||
|
//! ```rust,ignore
|
||||||
|
//! #[post("/", form = <my_form>)]
|
||||||
|
//! fn form_submit(my_form: MyType) -> ...
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Form parameter types must implement the [FromForm](trait.FromForm.html)
|
||||||
|
//! trait, which is automatically derivable. Automatically deriving `FromForm`
|
||||||
|
//! for a structure requires that all of its fields implement
|
||||||
|
//! [FromFormValue](trait.FormFormValue.html). See the
|
||||||
|
//! [codegen](/rocket_codegen/) documentation or the [forms guide](/guide/forms)
|
||||||
|
//! for more information on forms and on deriving `FromForm`.
|
||||||
|
|
||||||
|
use url;
|
||||||
|
use error::Error;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6, SocketAddr};
|
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6, SocketAddr};
|
||||||
use url;
|
|
||||||
|
|
||||||
use error::Error;
|
|
||||||
|
|
||||||
|
/// Trait to create instance of some type from an HTTP form; used by code
|
||||||
|
/// generation for `form` route parameters.
|
||||||
|
///
|
||||||
|
/// This trait can be automatically derived via the
|
||||||
|
/// [rocket_codegen](/rocket_codegen) plugin:
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// #![feature(plugin, custom_derive)]
|
||||||
|
/// #![plugin(rocket_codegen)]
|
||||||
|
///
|
||||||
|
/// extern crate rocket;
|
||||||
|
///
|
||||||
|
/// #[derive(FromForm)]
|
||||||
|
/// struct TodoTask {
|
||||||
|
/// description: String,
|
||||||
|
/// completed: bool
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// When deriving `FromForm`, every field in the structure must implement
|
||||||
|
/// [FromFormValue](trait.FromFormValue.html). If you implement `FormForm`
|
||||||
|
/// yourself, use the [FormItems](struct.FormItems.html) iterator to iterate
|
||||||
|
/// through the form key/value pairs.
|
||||||
pub trait FromForm<'f>: Sized {
|
pub trait FromForm<'f>: Sized {
|
||||||
fn from_form_string(s: &'f str) -> Result<Self, Error>;
|
/// The associated error which can be returned from parsing.
|
||||||
|
type Error;
|
||||||
|
|
||||||
|
/// Parses an instance of `Self` from a raw HTTP form
|
||||||
|
/// (`application/x-www-form-urlencoded data`) or returns an `Error` if one
|
||||||
|
/// cannot be parsed.
|
||||||
|
fn from_form_string(form_string: &'f str) -> Result<Self, Self::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This implementation should only be ued during debugging!
|
/// This implementation should only be used during debugging!
|
||||||
#[doc(hidden)]
|
|
||||||
impl<'f> FromForm<'f> for &'f str {
|
impl<'f> FromForm<'f> for &'f str {
|
||||||
|
type Error = Error;
|
||||||
fn from_form_string(s: &'f str) -> Result<Self, Error> {
|
fn from_form_string(s: &'f str) -> Result<Self, Error> {
|
||||||
Ok(s)
|
Ok(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Trait to create instance of some type from a form value; expected from field
|
||||||
|
/// types in structs deriving `FromForm`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// This trait is generally implemented when verifying form inputs. For example,
|
||||||
|
/// if you'd like to verify that some user is over some age in a form, then you
|
||||||
|
/// might define a new type and implement `FromFormValue` as follows:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use rocket::form::FromFormValue;
|
||||||
|
/// use rocket::Error;
|
||||||
|
///
|
||||||
|
/// struct AdultAge(usize);
|
||||||
|
///
|
||||||
|
/// impl<'v> FromFormValue<'v> for AdultAge {
|
||||||
|
/// type Error = &'v str;
|
||||||
|
///
|
||||||
|
/// fn from_form_value(form_value: &'v str) -> Result<AdultAge, &'v str> {
|
||||||
|
/// match usize::from_form_value(form_value) {
|
||||||
|
/// Ok(age) if age >= 21 => Ok(AdultAge(age)),
|
||||||
|
/// _ => Err(form_value),
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// This type can then be used in a `FromForm` struct as follows:
|
||||||
|
///
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// #[derive(FromForm)]
|
||||||
|
/// struct User {
|
||||||
|
/// age: AdultAge,
|
||||||
|
/// ...
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
pub trait FromFormValue<'v>: Sized {
|
pub trait FromFormValue<'v>: Sized {
|
||||||
|
/// The associated error which can be returned from parsing. It is a good
|
||||||
|
/// idea to have the return type be or contain an `&'v str` so that the
|
||||||
|
/// unparseable string can be examined after a bad parse.
|
||||||
type Error;
|
type Error;
|
||||||
|
|
||||||
fn parse(v: &'v str) -> Result<Self, Self::Error>;
|
/// Parses an instance of `Self` from an HTTP form field value or returns an
|
||||||
|
/// `Error` if one cannot be parsed.
|
||||||
|
fn from_form_value(form_value: &'v str) -> Result<Self, Self::Error>;
|
||||||
|
|
||||||
/// Returns a default value to be used when the form field does not exist.
|
/// Returns a default value to be used when the form field does not exist.
|
||||||
/// If this returns None, then the field is required. Otherwise, this should
|
/// If this returns None, then the field is required. Otherwise, this should
|
||||||
|
@ -32,7 +118,7 @@ pub trait FromFormValue<'v>: Sized {
|
||||||
impl<'v> FromFormValue<'v> for &'v str {
|
impl<'v> FromFormValue<'v> for &'v str {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
fn parse(v: &'v str) -> Result<Self, Self::Error> {
|
fn from_form_value(v: &'v str) -> Result<Self, Self::Error> {
|
||||||
Ok(v)
|
Ok(v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,7 +127,7 @@ impl<'v> FromFormValue<'v> for String {
|
||||||
type Error = &'v str;
|
type Error = &'v str;
|
||||||
|
|
||||||
// This actually parses the value according to the standard.
|
// This actually parses the value according to the standard.
|
||||||
fn parse(v: &'v str) -> Result<Self, Self::Error> {
|
fn from_form_value(v: &'v str) -> Result<Self, Self::Error> {
|
||||||
let decoder = url::percent_encoding::percent_decode(v.as_bytes());
|
let decoder = url::percent_encoding::percent_decode(v.as_bytes());
|
||||||
let res = decoder.decode_utf8().map_err(|_| v).map(|s| s.into_owned());
|
let res = decoder.decode_utf8().map_err(|_| v).map(|s| s.into_owned());
|
||||||
match res {
|
match res {
|
||||||
|
@ -64,7 +150,7 @@ impl<'v> FromFormValue<'v> for String {
|
||||||
impl<'v> FromFormValue<'v> for bool {
|
impl<'v> FromFormValue<'v> for bool {
|
||||||
type Error = &'v str;
|
type Error = &'v str;
|
||||||
|
|
||||||
fn parse(v: &'v str) -> Result<Self, Self::Error> {
|
fn from_form_value(v: &'v str) -> Result<Self, Self::Error> {
|
||||||
match v {
|
match v {
|
||||||
"on" | "true" => Ok(true),
|
"on" | "true" => Ok(true),
|
||||||
"off" | "false" => Ok(false),
|
"off" | "false" => Ok(false),
|
||||||
|
@ -77,7 +163,7 @@ macro_rules! impl_with_fromstr {
|
||||||
($($T:ident),+) => ($(
|
($($T:ident),+) => ($(
|
||||||
impl<'v> FromFormValue<'v> for $T {
|
impl<'v> FromFormValue<'v> for $T {
|
||||||
type Error = &'v str;
|
type Error = &'v str;
|
||||||
fn parse(v: &'v str) -> Result<Self, Self::Error> {
|
fn from_form_value(v: &'v str) -> Result<Self, Self::Error> {
|
||||||
$T::from_str(v).map_err(|_| v)
|
$T::from_str(v).map_err(|_| v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,8 +176,8 @@ impl_with_fromstr!(f32, f64, isize, i8, i16, i32, i64, usize, u8, u16, u32, u64,
|
||||||
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;
|
||||||
|
|
||||||
fn parse(v: &'v str) -> Result<Self, Self::Error> {
|
fn from_form_value(v: &'v str) -> Result<Self, Self::Error> {
|
||||||
match T::parse(v) {
|
match T::from_form_value(v) {
|
||||||
Ok(v) => Ok(Some(v)),
|
Ok(v) => Ok(Some(v)),
|
||||||
Err(_) => Ok(None)
|
Err(_) => Ok(None)
|
||||||
}
|
}
|
||||||
|
@ -106,14 +192,43 @@ impl<'v, T: FromFormValue<'v>> FromFormValue<'v> for Option<T> {
|
||||||
impl<'v, T: FromFormValue<'v>> FromFormValue<'v> for Result<T, T::Error> {
|
impl<'v, T: FromFormValue<'v>> FromFormValue<'v> for Result<T, T::Error> {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
fn parse(v: &'v str) -> Result<Self, Self::Error> {
|
fn from_form_value(v: &'v str) -> Result<Self, Self::Error> {
|
||||||
match T::parse(v) {
|
match T::from_form_value(v) {
|
||||||
ok@Ok(_) => Ok(ok),
|
ok@Ok(_) => Ok(ok),
|
||||||
e@Err(_) => Ok(e)
|
e@Err(_) => Ok(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Iterator over the key/value pairs of a given HTTP form string. You'll likely
|
||||||
|
/// want to use this if you're implementing [FromForm](trait.FromForm.html)
|
||||||
|
/// manually, for whatever reason, by iterating over the items in `form_string`.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// `FormItems` can be used directly as an iterator:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use rocket::form::FormItems;
|
||||||
|
///
|
||||||
|
/// // prints "greeting = hello" then "username = jake"
|
||||||
|
/// let form_string = "greeting=hello&username=jake";
|
||||||
|
/// for (key, value) in FormItems(form_string) {
|
||||||
|
/// println!("{} = {}", key, value);
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// This is the same example as above, but the iterator is used explicitly.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// use rocket::form::FormItems;
|
||||||
|
///
|
||||||
|
/// let form_string = "greeting=hello&username=jake";
|
||||||
|
/// let mut items = FormItems(form_string);
|
||||||
|
/// assert_eq!(items.next(), Some(("greeting", "hello")));
|
||||||
|
/// assert_eq!(items.next(), Some(("username", "jake")));
|
||||||
|
/// assert_eq!(items.next(), None);
|
||||||
|
/// ```
|
||||||
pub struct FormItems<'f>(pub &'f str);
|
pub struct FormItems<'f>(pub &'f str);
|
||||||
|
|
||||||
impl<'f> Iterator for FormItems<'f> {
|
impl<'f> Iterator for FormItems<'f> {
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
//! chapter](https://rocket.rs/guide/getting_started) of the guide.
|
//! chapter](https://rocket.rs/guide/getting_started) of the guide.
|
||||||
//!
|
//!
|
||||||
//! You may also be interested in looking at the [contrib API
|
//! You may also be interested in looking at the [contrib API
|
||||||
//! documentation](../rocket_contrib), which contains JSON and templaring
|
//! documentation](../rocket_contrib), which contains JSON and templating
|
||||||
//! support.
|
//! support.
|
||||||
|
|
||||||
extern crate term_painter;
|
extern crate term_painter;
|
||||||
|
@ -22,6 +22,7 @@ extern crate url;
|
||||||
extern crate mime;
|
extern crate mime;
|
||||||
#[macro_use] extern crate log;
|
#[macro_use] extern crate log;
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
pub mod logger;
|
pub mod logger;
|
||||||
pub mod form;
|
pub mod form;
|
||||||
|
@ -32,30 +33,33 @@ pub mod content_type;
|
||||||
|
|
||||||
mod method;
|
mod method;
|
||||||
mod error;
|
mod error;
|
||||||
mod param;
|
|
||||||
mod router;
|
mod router;
|
||||||
mod rocket;
|
mod rocket;
|
||||||
mod codegen;
|
mod codegen;
|
||||||
mod catcher;
|
mod catcher;
|
||||||
|
|
||||||
#[doc(hidden)]
|
/// Defines the types for request and error handlers.
|
||||||
pub mod handler {
|
pub mod handler {
|
||||||
use super::{Request, Response, Error};
|
use super::{Request, Response, Error};
|
||||||
|
|
||||||
|
/// The type of a request handler.
|
||||||
pub type Handler = for<'r> fn(&'r Request<'r>) -> Response<'r>;
|
pub type Handler = for<'r> fn(&'r Request<'r>) -> Response<'r>;
|
||||||
|
|
||||||
|
/// The type of an error handler.
|
||||||
pub type ErrorHandler = for<'r> fn(error: Error, &'r Request<'r>) -> Response<'r>;
|
pub type ErrorHandler = for<'r> fn(error: Error, &'r Request<'r>) -> Response<'r>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub use logger::{RocketLogger, LoggingLevel};
|
|
||||||
pub use content_type::ContentType;
|
pub use content_type::ContentType;
|
||||||
pub use codegen::{StaticRouteInfo, StaticCatchInfo};
|
pub use codegen::{StaticRouteInfo, StaticCatchInfo};
|
||||||
pub use request::Request;
|
pub use request::Request;
|
||||||
pub use method::Method;
|
pub use method::Method;
|
||||||
|
#[doc(inline)]
|
||||||
pub use response::{Response, Responder};
|
pub use response::{Response, Responder};
|
||||||
pub use error::Error;
|
pub use error::Error;
|
||||||
pub use param::{FromParam, FromSegments};
|
|
||||||
pub use router::{Router, Route};
|
pub use router::{Router, Route};
|
||||||
pub use catcher::Catcher;
|
pub use catcher::Catcher;
|
||||||
pub use rocket::Rocket;
|
pub use rocket::Rocket;
|
||||||
|
#[doc(inline)]
|
||||||
pub use handler::{Handler, ErrorHandler};
|
pub use handler::{Handler, ErrorHandler};
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use logger::LoggingLevel;
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
|
//! Rocket's logging infrastructure.
|
||||||
|
|
||||||
use log::{self, Log, LogLevel, LogRecord, LogMetadata};
|
use log::{self, Log, LogLevel, LogRecord, LogMetadata};
|
||||||
use term_painter::Color::*;
|
use term_painter::Color::*;
|
||||||
use term_painter::ToStyle;
|
use term_painter::ToStyle;
|
||||||
|
|
||||||
pub struct RocketLogger(LoggingLevel);
|
struct RocketLogger(LoggingLevel);
|
||||||
|
|
||||||
|
/// Defines the different levels for log messages.
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
pub enum LoggingLevel {
|
pub enum LoggingLevel {
|
||||||
/// Only shows errors and warning.
|
/// Only shows errors and warning.
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
|
//! Types and traits that deal with request parsing and handling.
|
||||||
|
|
||||||
mod request;
|
mod request;
|
||||||
|
mod param;
|
||||||
mod from_request;
|
mod from_request;
|
||||||
|
|
||||||
pub use self::request::Request;
|
pub use self::request::Request;
|
||||||
pub use self::from_request::FromRequest;
|
pub use self::from_request::FromRequest;
|
||||||
|
pub use self::param::{FromParam, FromSegments};
|
||||||
#[doc(hidden)]
|
|
||||||
pub use hyper::server::Request as HyperRequest;
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub use hyper::header::Headers as HyperHeaders;
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub use hyper::header::Cookie as HyperCookie;
|
|
||||||
|
|
||||||
pub use hyper::header::CookiePair as Cookie;
|
pub use hyper::header::CookiePair as Cookie;
|
||||||
|
|
||||||
|
// Unexported Hyper types.
|
||||||
|
#[doc(hidden)] pub use hyper::server::Request as HyperRequest;
|
||||||
|
#[doc(hidden)] pub use hyper::header::Headers as HyperHeaders;
|
||||||
|
#[doc(hidden)] pub use hyper::header::Cookie as HyperCookie;
|
||||||
|
|
||||||
use hyper::header::CookieJar;
|
use hyper::header::CookieJar;
|
||||||
pub type Cookies = CookieJar<'static>;
|
pub type Cookies = CookieJar<'static>;
|
||||||
|
|
|
@ -1,34 +1,36 @@
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6, SocketAddr};
|
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6, SocketAddr};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use router::Segments;
|
|
||||||
|
|
||||||
|
use router::Segments;
|
||||||
use url;
|
use url;
|
||||||
|
|
||||||
use error::Error;
|
|
||||||
|
|
||||||
pub trait FromParam<'a>: Sized {
|
pub trait FromParam<'a>: Sized {
|
||||||
fn from_param(param: &'a str) -> Result<Self, Error>;
|
type Error;
|
||||||
|
fn from_param(param: &'a str) -> Result<Self, Self::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FromParam<'a> for &'a str {
|
impl<'a> FromParam<'a> for &'a str {
|
||||||
fn from_param(param: &'a str) -> Result<&'a str, Error> {
|
type Error = ();
|
||||||
|
fn from_param(param: &'a str) -> Result<&'a str, Self::Error> {
|
||||||
Ok(param)
|
Ok(param)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FromParam<'a> for String {
|
impl<'a> FromParam<'a> for String {
|
||||||
fn from_param(p: &'a str) -> Result<String, Error> {
|
type Error = &'a str;
|
||||||
|
fn from_param(p: &'a str) -> Result<String, Self::Error> {
|
||||||
let decoder = url::percent_encoding::percent_decode(p.as_bytes());
|
let decoder = url::percent_encoding::percent_decode(p.as_bytes());
|
||||||
decoder.decode_utf8().map_err(|_| Error::BadParse).map(|s| s.into_owned())
|
decoder.decode_utf8().map_err(|_| p).map(|s| s.into_owned())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! impl_with_fromstr {
|
macro_rules! impl_with_fromstr {
|
||||||
($($T:ident),+) => ($(
|
($($T:ident),+) => ($(
|
||||||
impl<'a> FromParam<'a> for $T {
|
impl<'a> FromParam<'a> for $T {
|
||||||
fn from_param(param: &'a str) -> Result<Self, Error> {
|
type Error = &'a str;
|
||||||
$T::from_str(param).map_err(|_| Error::BadParse)
|
fn from_param(param: &'a str) -> Result<Self, Self::Error> {
|
||||||
|
$T::from_str(param).map_err(|_| param)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)+)
|
)+)
|
||||||
|
@ -39,17 +41,20 @@ impl_with_fromstr!(f32, f64, isize, i8, i16, i32, i64, usize, u8, u16, u32, u64,
|
||||||
SocketAddr);
|
SocketAddr);
|
||||||
|
|
||||||
pub trait FromSegments<'a>: Sized {
|
pub trait FromSegments<'a>: Sized {
|
||||||
fn from_segments(segments: Segments<'a>) -> Result<Self, Error>;
|
type Error;
|
||||||
|
fn from_segments(segments: Segments<'a>) -> Result<Self, Self::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FromSegments<'a> for Segments<'a> {
|
impl<'a> FromSegments<'a> for Segments<'a> {
|
||||||
fn from_segments(segments: Segments<'a>) -> Result<Segments<'a>, Error> {
|
type Error = ();
|
||||||
|
fn from_segments(segments: Segments<'a>) -> Result<Segments<'a>, ()> {
|
||||||
Ok(segments)
|
Ok(segments)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FromSegments<'a> for PathBuf {
|
impl<'a> FromSegments<'a> for PathBuf {
|
||||||
fn from_segments(segments: Segments<'a>) -> Result<PathBuf, Error> {
|
type Error = ();
|
||||||
|
fn from_segments(segments: Segments<'a>) -> Result<PathBuf, ()> {
|
||||||
Ok(segments.collect())
|
Ok(segments.collect())
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,7 +6,7 @@ use term_painter::Color::*;
|
||||||
use term_painter::ToStyle;
|
use term_painter::ToStyle;
|
||||||
|
|
||||||
use error::Error;
|
use error::Error;
|
||||||
use param::{FromParam, FromSegments};
|
use super::{FromParam, FromSegments};
|
||||||
use method::Method;
|
use method::Method;
|
||||||
|
|
||||||
use content_type::ContentType;
|
use content_type::ContentType;
|
||||||
|
@ -36,7 +36,7 @@ impl<'a> Request<'a> {
|
||||||
debug!("{} is >= param count {}", n, params.as_ref().unwrap().len());
|
debug!("{} is >= param count {}", n, params.as_ref().unwrap().len());
|
||||||
Err(Error::NoKey)
|
Err(Error::NoKey)
|
||||||
} else {
|
} else {
|
||||||
T::from_param(params.as_ref().unwrap()[n])
|
T::from_param(params.as_ref().unwrap()[n]).map_err(|_| Error::BadParse)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ impl<'a> Request<'a> {
|
||||||
// but the std lib doesn't implement it for Skip.
|
// but the std lib doesn't implement it for Skip.
|
||||||
let mut segments = self.uri.segments();
|
let mut segments = self.uri.segments();
|
||||||
for _ in segments.by_ref().take(i) { /* do nothing */ }
|
for _ in segments.by_ref().take(i) { /* do nothing */ }
|
||||||
T::from_segments(segments)
|
T::from_segments(segments).map_err(|_| Error::BadParse)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue