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:
Sergio Benitez 2016-09-30 01:25:07 -07:00
parent 76cbc14d23
commit 008605bec7
12 changed files with 281 additions and 62 deletions

View File

@ -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:

View File

@ -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
} }
};
}, },
)); ));
} }

View File

@ -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
}));
}

View File

@ -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."),
}; };

View File

@ -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,

View File

@ -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> {

View File

@ -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> {

View File

@ -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;

View File

@ -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.

View File

@ -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>;

View File

@ -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())
} }
} }

View File

@ -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)
} }
} }