use std::any::Any;
use std::collections::BTreeMap;
use serde::Serialize;
use serde::de::{Deserialize, DeserializeOwned};
use hyper::header::Header;
use super::{Headers, Method, Uri};
use error::{Result, Error};
pub use hyper::Request as HyperRequest;
#[derive(Debug)]
pub struct Request {
pub(crate) method: Method,
pub(crate) query: String,
pub(crate) path_segs: Vec<String>,
pub(crate) headers: Headers,
pub(crate) body: Vec<u8>,
pub(crate) extra: BTreeMap<String, Box<Any>>,
}
impl Request {
pub fn new(method: Method) -> Request {
Request {
method: method,
query: String::new(),
path_segs: Vec::new(),
headers: Headers::new(),
body: Vec::new(),
extra: BTreeMap::new(),
}
}
pub fn method(&self) -> Method {
self.method.clone()
}
pub fn to_param<'de, T: Deserialize<'de>>(&'de self) -> Result<T> {
::serde_qs::from_str(&self.query)
.map_err(|e| Error::internal("Unable to deserialize URI query.").with_cause(e))
}
pub fn header<H: Header>(&self) -> Option<&H> {
self.headers.get::<H>()
}
pub fn body(&self) -> &[u8] {
&self.body
}
pub fn to_str(&self) -> Result<&str> {
::std::str::from_utf8(&self.body)
.map_err(|e| Error::internal("Unable to parse body as string.").with_cause(e))
}
pub fn to_json<T: 'static + DeserializeOwned>(&self) -> Result<T> {
use hyper::header::ContentType;
if let Some(&ContentType(ref ty)) = self.headers.get::<ContentType>() {
if ty.type_() != "application" || ty.subtype() != "json" {
let err = Error::bad_request("Content should be of type `application/json`.");
return Err(err)
}
}
::serde_json::from_slice::<T>(&self.body)
.map_err(|e| Error::internal("Unable to deserialize body as JSON.").with_cause(e))
}
pub fn to_form<'de, T: Deserialize<'de>>(&'de self) -> Result<T> {
use hyper::header::ContentType;
if let Some(&ContentType(ref ty)) = self.headers.get::<ContentType>() {
if ty.type_() != "application" || ty.subtype() != "x-www-form-urlencoded" {
let err = Error::bad_request("Content should be of type `application/x-www-form-urlencoded`.");
return Err(err)
}
}
::serde_qs::from_bytes(&self.body)
.map_err(|e| Error::internal("Unable to parse body as form data.").with_cause(e))
}
pub fn set_uri(&mut self, uri: &Uri) {
fn collect_path_segs(path: &str) -> Vec<String> {
let mut raw_rv = Vec::new();
if path.len() != 0 && path.as_bytes()[0] == b'/' {
let chars = if cfg!(windows) {
path[1..].replace("\\", "/")
} else {
path[1..].to_owned()
};
let iter = chars.split('/')
.map(|x| x.to_string());
raw_rv.extend(iter);
}
let mut rv = Vec::new();
for seg in raw_rv {
if seg == ".." {
rv.pop();
} else if seg == "." || seg == "" {
} else {
rv.push(seg);
}
}
rv
}
self.path_segs = collect_path_segs(uri.path());
self.query = uri.query().unwrap_or_default().to_owned();
}
pub fn set_query(&mut self, query: &str) {
self.query = query.to_owned();
}
pub fn set_path_segs(&mut self, path_segs: &[&str]) {
self.path_segs = path_segs.into_iter()
.map(|x| (*x).to_owned())
.collect();
}
pub fn set_header<H: Header>(&mut self, header: H) {
self.headers.set(header);
}
pub fn set_headers(&mut self, headers: Headers) {
self.headers = headers;
}
pub fn set_body<B>(&mut self, body: B) where B: Into<Vec<u8>> {
self.body = body.into();
}
pub fn set_json<T: Serialize>(&mut self, json: &T) -> Result<()> {
use hyper::header::ContentType;
::serde_json::to_vec(&json)
.map(|json| {
self.body = json;
self.headers.set(ContentType("application/json; charset=UTF-8".parse().unwrap()));
})
.map_err(|err| Error::internal("Unable to serialize data into JSON.").with_cause(err))
}
pub fn with_uri(mut self, uri: &Uri) -> Self {
self.set_uri(uri);
self
}
pub fn with_query(mut self, query: &str) -> Self {
self.query = query.to_owned();
self
}
pub fn with_path_segs(mut self, path_segs: &[&str]) -> Self {
self.set_path_segs(path_segs);
self
}
pub fn with_header<H: Header>(mut self, header: H) -> Self {
self.headers.set(header);
self
}
pub fn with_headers(mut self, headers: Headers) -> Self {
self.headers = headers;
self
}
pub fn with_body<B>(mut self, body: B) -> Self
where B: Into<Vec<u8>> {
self.body = body.into();
self
}
pub fn with_json<T: Serialize>(mut self, json: &T) -> Result<Self> {
self.set_json(json)
.map(|_| { self })
}
pub fn extra<T: 'static>(&self, key: &str) -> Option<&T> {
self.extra.get(key)
.and_then(|boxed| boxed.downcast_ref())
}
pub fn set_extra<T: 'static + Any + Sized>(&mut self, key: &str, val: T) {
self.extra.insert(key.to_string(), Box::new(val));
}
pub fn path_segs(&self) -> &[String] {
&self.path_segs[..]
}
pub fn match_seg(&mut self, seg: &str) -> bool {
if self.path_segs.len() > 0 &&
&self.path_segs[0] == seg {
self.path_segs.remove(0);
true
} else {
false
}
}
pub fn match_segs(&mut self, segs: &[&str]) -> bool {
if self.path_segs.len() < segs.len() {
return false
}
if self.path_segs.iter()
.zip(segs.iter())
.all(|(req_seg, api_seg)| req_seg == api_seg) {
self.path_segs.drain(..segs.len());
true
} else {
false
}
}
pub fn match_seg_if<F>(&mut self, pred: F) -> Option<String>
where F: Fn(&str) -> bool {
if self.path_segs.len() > 0 && pred(&self.path_segs[0]) {
Some(self.path_segs.remove(0))
} else {
None
}
}
pub fn match_segs_while<F>(&mut self, pred: F) -> Vec<String>
where F: Fn(&str) -> bool {
use std::iter::FromIterator;
if let Some(false_point) = self.path_segs.iter()
.position(|x| !pred(x)) {
Vec::from_iter(self.path_segs.drain(..false_point))
} else {
Vec::from_iter(self.path_segs.drain(..))
}
}
}