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:
|
||||
|
||||
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).
|
||||
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
|
||||
code you contribute must be:
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
use syntax::ext::base::{Annotatable, ExtCtxt};
|
||||
use syntax::print::pprust::{stmt_to_string};
|
||||
use syntax::parse::token::{str_to_ident};
|
||||
use syntax::ast::{ItemKind, Expr, MetaItem, Mutability, VariantData};
|
||||
use syntax::codemap::Span;
|
||||
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 {
|
||||
is_unsafe: false,
|
||||
supports_unions: false,
|
||||
|
@ -88,9 +92,7 @@ pub fn from_form_derive(ecx: &mut ExtCtxt, span: Span, meta_item: &MetaItem,
|
|||
lifetime: None,
|
||||
params: vec![
|
||||
Box::new(ty::Ty::Self_),
|
||||
Box::new(ty::Ty::Literal(
|
||||
ty::Path::new(vec!["rocket", "Error"])
|
||||
)),
|
||||
Box::new(error_type.clone())
|
||||
],
|
||||
global: true,
|
||||
}
|
||||
|
@ -101,7 +103,9 @@ pub fn from_form_derive(ecx: &mut ExtCtxt, span: Span, meta_item: &MetaItem,
|
|||
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);
|
||||
|
@ -164,12 +168,14 @@ fn from_form_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substruct
|
|||
let ident_string = ident.to_string();
|
||||
let id_str = ident_string.as_str();
|
||||
arms.push(quote_tokens!(cx,
|
||||
$id_str => $ident = match ::rocket::form::FromFormValue::parse(v) {
|
||||
Ok(v) => Some(v),
|
||||
Err(e) => {
|
||||
println!("\tError parsing form value '{}': {:?}", $id_str, e);
|
||||
$return_err_stmt
|
||||
}
|
||||
$id_str => {
|
||||
$ident = match ::rocket::form::FromFormValue::from_form_value(v) {
|
||||
Ok(v) => Some(v),
|
||||
Err(e) => {
|
||||
println!("\tError parsing form val '{}': {:?}", $id_str, e);
|
||||
$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> {
|
||||
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 {
|
||||
Err("Too short!")
|
||||
} else {
|
||||
|
@ -37,8 +37,8 @@ impl<'v> FromFormValue<'v> for StrongPassword<'v> {
|
|||
impl<'v> FromFormValue<'v> for AdultAge {
|
||||
type Error = &'static str;
|
||||
|
||||
fn parse(v: &'v str) -> Result<Self, Self::Error> {
|
||||
let age = match isize::parse(v) {
|
||||
fn from_form_value(v: &'v str) -> Result<Self, Self::Error> {
|
||||
let age = match isize::from_form_value(v) {
|
||||
Ok(v) => v,
|
||||
Err(_) => return Err("Age value is not a number."),
|
||||
};
|
||||
|
|
|
@ -17,7 +17,7 @@ enum FormOption {
|
|||
impl<'v> FromFormValue<'v> for FormOption {
|
||||
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 {
|
||||
"a" => FormOption::A,
|
||||
"b" => FormOption::B,
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
extern crate rocket;
|
||||
|
||||
use rocket::{Rocket, Error};
|
||||
use rocket::Rocket;
|
||||
|
||||
#[derive(FromForm)]
|
||||
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::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 {
|
||||
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!
|
||||
#[doc(hidden)]
|
||||
/// This implementation should only be used during debugging!
|
||||
impl<'f> FromForm<'f> for &'f str {
|
||||
type Error = Error;
|
||||
fn from_form_string(s: &'f str) -> Result<Self, Error> {
|
||||
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 {
|
||||
/// 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;
|
||||
|
||||
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.
|
||||
/// 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 {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -41,7 +127,7 @@ impl<'v> FromFormValue<'v> for String {
|
|||
type Error = &'v str;
|
||||
|
||||
// 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 res = decoder.decode_utf8().map_err(|_| v).map(|s| s.into_owned());
|
||||
match res {
|
||||
|
@ -64,7 +150,7 @@ 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> {
|
||||
fn from_form_value(v: &'v str) -> Result<Self, Self::Error> {
|
||||
match v {
|
||||
"on" | "true" => Ok(true),
|
||||
"off" | "false" => Ok(false),
|
||||
|
@ -77,7 +163,7 @@ macro_rules! impl_with_fromstr {
|
|||
($($T:ident),+) => ($(
|
||||
impl<'v> FromFormValue<'v> for $T {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -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> {
|
||||
type Error = Error;
|
||||
|
||||
fn parse(v: &'v str) -> Result<Self, Self::Error> {
|
||||
match T::parse(v) {
|
||||
fn from_form_value(v: &'v str) -> Result<Self, Self::Error> {
|
||||
match T::from_form_value(v) {
|
||||
Ok(v) => Ok(Some(v)),
|
||||
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> {
|
||||
type Error = Error;
|
||||
|
||||
fn parse(v: &'v str) -> Result<Self, Self::Error> {
|
||||
match T::parse(v) {
|
||||
fn from_form_value(v: &'v str) -> Result<Self, Self::Error> {
|
||||
match T::from_form_value(v) {
|
||||
ok@Ok(_) => Ok(ok),
|
||||
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);
|
||||
|
||||
impl<'f> Iterator for FormItems<'f> {
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
//! chapter](https://rocket.rs/guide/getting_started) of the guide.
|
||||
//!
|
||||
//! 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.
|
||||
|
||||
extern crate term_painter;
|
||||
|
@ -22,6 +22,7 @@ extern crate url;
|
|||
extern crate mime;
|
||||
#[macro_use] extern crate log;
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_use]
|
||||
pub mod logger;
|
||||
pub mod form;
|
||||
|
@ -32,30 +33,33 @@ pub mod content_type;
|
|||
|
||||
mod method;
|
||||
mod error;
|
||||
mod param;
|
||||
mod router;
|
||||
mod rocket;
|
||||
mod codegen;
|
||||
mod catcher;
|
||||
|
||||
#[doc(hidden)]
|
||||
/// Defines the types for request and error handlers.
|
||||
pub mod handler {
|
||||
use super::{Request, Response, Error};
|
||||
|
||||
/// The type of a request handler.
|
||||
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>;
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use logger::{RocketLogger, LoggingLevel};
|
||||
pub use content_type::ContentType;
|
||||
pub use codegen::{StaticRouteInfo, StaticCatchInfo};
|
||||
pub use request::Request;
|
||||
pub use method::Method;
|
||||
#[doc(inline)]
|
||||
pub use response::{Response, Responder};
|
||||
pub use error::Error;
|
||||
pub use param::{FromParam, FromSegments};
|
||||
pub use router::{Router, Route};
|
||||
pub use catcher::Catcher;
|
||||
pub use rocket::Rocket;
|
||||
#[doc(inline)]
|
||||
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 term_painter::Color::*;
|
||||
use term_painter::ToStyle;
|
||||
|
||||
pub struct RocketLogger(LoggingLevel);
|
||||
struct RocketLogger(LoggingLevel);
|
||||
|
||||
/// Defines the different levels for log messages.
|
||||
#[derive(PartialEq)]
|
||||
pub enum LoggingLevel {
|
||||
/// Only shows errors and warning.
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
//! Types and traits that deal with request parsing and handling.
|
||||
|
||||
mod request;
|
||||
mod param;
|
||||
mod from_request;
|
||||
|
||||
pub use self::request::Request;
|
||||
pub use self::from_request::FromRequest;
|
||||
|
||||
#[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 self::param::{FromParam, FromSegments};
|
||||
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;
|
||||
pub type Cookies = CookieJar<'static>;
|
||||
|
|
|
@ -1,34 +1,36 @@
|
|||
use std::str::FromStr;
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6, SocketAddr};
|
||||
use std::path::PathBuf;
|
||||
use router::Segments;
|
||||
|
||||
use router::Segments;
|
||||
use url;
|
||||
|
||||
use error::Error;
|
||||
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
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 {
|
||||
($($T:ident),+) => ($(
|
||||
impl<'a> FromParam<'a> for $T {
|
||||
fn from_param(param: &'a str) -> Result<Self, Error> {
|
||||
$T::from_str(param).map_err(|_| Error::BadParse)
|
||||
type Error = &'a str;
|
||||
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);
|
||||
|
||||
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> {
|
||||
fn from_segments(segments: Segments<'a>) -> Result<Segments<'a>, Error> {
|
||||
type Error = ();
|
||||
fn from_segments(segments: Segments<'a>) -> Result<Segments<'a>, ()> {
|
||||
Ok(segments)
|
||||
}
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@ use term_painter::Color::*;
|
|||
use term_painter::ToStyle;
|
||||
|
||||
use error::Error;
|
||||
use param::{FromParam, FromSegments};
|
||||
use super::{FromParam, FromSegments};
|
||||
use method::Method;
|
||||
|
||||
use content_type::ContentType;
|
||||
|
@ -36,7 +36,7 @@ impl<'a> Request<'a> {
|
|||
debug!("{} is >= param count {}", n, params.as_ref().unwrap().len());
|
||||
Err(Error::NoKey)
|
||||
} 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.
|
||||
let mut segments = self.uri.segments();
|
||||
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