use codegen::CallSite;
use extract::{Context, Error, Extract, ExtractFuture};
use http::status::StatusCode;
use util::buf_stream::{self, BufStream};
use futures::{Future, Poll};
use headers::{ContentType, HeaderMapExt};
use mime::Mime;
use serde::de::DeserializeOwned;
use serde_urlencoded;
use serde_json;
#[derive(Debug)]
pub struct SerdeFuture<T, B> {
state: State<T, B>,
is_json: bool,
}
#[derive(Debug)]
enum State<T, B> {
Complete(Result<T, Option<Error>>),
Body(buf_stream::Collect<B, Vec<u8>>),
}
impl<B: BufStream> Extract<B> for serde_json::Value {
type Future = SerdeFuture<Self, B>;
fn extract(ctx: &Context) -> Self::Future {
Self::Future::new_extract(ctx)
}
fn extract_body(ctx: &Context, body: B) -> Self::Future {
Self::Future::new_extract_body(ctx, body)
}
fn requires_body(callsite: &CallSite) -> bool {
self::requires_body(callsite)
}
}
#[doc(hidden)]
pub fn requires_body(callsite: &CallSite) -> bool {
use codegen::Source::Body;
match callsite.source() {
Body => true,
_ => false,
}
}
impl<T, B> SerdeFuture<T, B>
where T: DeserializeOwned,
B: BufStream,
{
pub fn new_extract(ctx: &Context) -> Self {
use codegen::Source::*;
match ctx.callsite().source() {
Capture(_) => {
unimplemented!();
}
Header(_) => {
unimplemented!();
}
QueryString => {
let query = ctx.request().uri()
.path_and_query()
.and_then(|path_and_query| path_and_query.query())
.unwrap_or("");
let res = serde_urlencoded::from_str(query)
.map_err(|err| {
use std::error::Error as E;
if query.is_empty() {
Some(Error::missing_argument())
} else {
Some(Error::invalid_argument(&err.description()))
}
});
let state = State::Complete(res);
SerdeFuture { state, is_json: false }
}
Body => {
unimplemented!();
}
Unknown => {
unimplemented!();
}
}
}
pub fn new_extract_body(ctx: &Context, body: B) -> Self {
use codegen::Source::*;
match ctx.callsite().source() {
Capture(_) => {
unimplemented!("Capture");
}
Header(_) => {
unimplemented!("Header");
}
QueryString => {
unimplemented!("QueryString");
}
Body => {
if let Some(value) = ctx.request().headers().typed_get::<ContentType>() {
let mime = Mime::from(value);
match (mime.type_().as_str(), mime.subtype().as_str()) {
("application", "json") => {
let state = State::Body(body.collect());
SerdeFuture { state, is_json: true }
}
("application", "x-www-form-urlencoded") => {
let state = State::Body(body.collect());
SerdeFuture { state, is_json: false }
}
_ => {
let err = ::Error::from(StatusCode::BAD_REQUEST).into();
let state = State::Complete(Err(Some(err)));
SerdeFuture { state, is_json: false }
}
}
} else {
let err = ::Error::from(StatusCode::BAD_REQUEST).into();
let state = State::Complete(Err(Some(err)));
SerdeFuture { state, is_json: false }
}
}
Unknown => {
unimplemented!("Unknown");
}
}
}
}
impl<T, B> ExtractFuture for SerdeFuture<T, B>
where T: DeserializeOwned,
B: BufStream,
{
type Item = T;
fn poll(&mut self) -> Poll<(), Error> {
use self::State::*;
loop {
let res = match self.state {
Complete(Err(ref mut e)) => {
return Err(e.take().unwrap());
}
Complete(Ok(_)) => {
return Ok(().into());
}
Body(ref mut collect) => {
let res = collect.poll()
.map_err(|_| Error::internal_error());
let res = try_ready!(res);
if self.is_json == true {
::serde_json::from_slice(&res[..])
.map_err(|_| {
Some(Error::internal_error())
})
} else {
::serde_urlencoded::from_bytes(&res[..])
.map_err(|_| {
Some(Error::internal_error())
})
}
}
};
self.state = State::Complete(res);
}
}
fn extract(self) -> T {
use self::State::Complete;
match self.state {
Complete(Ok(res)) => res,
_ => panic!("invalid state"),
}
}
}