use std::cell::RefCell;
use std::fmt;
use term_painter::Color::*;
use term_painter::ToStyle;
use error::Error;
use super::{FromParam, FromSegments};
use router::Route;
use http::uri::{URI, Segments};
use http::{Method, ContentType, Header, HeaderMap, Cookies};
use http::hyper;
pub struct Request<'r> {
method: Method,
uri: URI<'r>,
headers: HeaderMap<'r>,
params: RefCell<Vec<(usize, usize)>>,
cookies: Cookies,
}
impl<'r> Request<'r> {
pub fn new<U: Into<URI<'r>>>(method: Method, uri: U) -> Request<'r> {
Request {
method: method,
uri: uri.into(),
headers: HeaderMap::new(),
params: RefCell::new(Vec::new()),
cookies: Cookies::new(&[]),
}
}
#[inline(always)]
pub fn method(&self) -> Method {
self.method
}
#[inline(always)]
pub fn set_method(&mut self, method: Method) {
self.method = method;
}
#[inline(always)]
pub fn uri(&self) -> &URI {
&self.uri
}
#[inline(always)]
pub fn set_uri<'u: 'r, U: Into<URI<'u>>>(&mut self, uri: U) {
self.uri = uri.into();
self.params = RefCell::new(Vec::new());
}
#[inline(always)]
pub fn headers(&self) -> &HeaderMap<'r> {
&self.headers
}
#[inline(always)]
pub fn add_header(&mut self, header: Header<'r>) {
self.headers.add(header);
}
#[inline(always)]
pub fn replace_header(&mut self, header: Header<'r>) {
self.headers.replace(header);
}
#[inline(always)]
pub fn cookies(&self) -> &Cookies {
&self.cookies
}
#[doc(hidden)]
#[inline(always)]
pub fn set_cookies(&mut self, cookies: Cookies) {
self.cookies = cookies;
}
#[inline(always)]
pub fn content_type(&self) -> ContentType {
self.headers().get_one("Content-Type")
.and_then(|value| value.parse().ok())
.unwrap_or(ContentType::Any)
}
pub fn get_param<'a, T: FromParam<'a>>(&'a self, n: usize) -> Result<T, Error> {
let param = self.get_param_str(n).ok_or(Error::NoKey)?;
T::from_param(param).map_err(|_| Error::BadParse)
}
#[doc(hidden)]
#[inline(always)]
pub fn set_params(&self, route: &Route) {
*self.params.borrow_mut() = route.get_param_indexes(self.uri());
}
#[doc(hidden)]
pub fn get_param_str(&self, n: usize) -> Option<&str> {
let params = self.params.borrow();
if n >= params.len() {
debug!("{} is >= param count {}", n, params.len());
return None;
}
let (i, j) = params[n];
let uri_str = self.uri.as_str();
if j > uri_str.len() {
error!("Couldn't retrieve parameter: internal count incorrect.");
return None;
}
Some(&uri_str[i..j])
}
pub fn get_segments<'a, T: FromSegments<'a>>(&'a self, i: usize)
-> Result<T, Error> {
let segments = self.get_raw_segments(i).ok_or(Error::NoKey)?;
T::from_segments(segments).map_err(|_| Error::BadParse)
}
#[doc(hidden)]
pub fn get_raw_segments(&self, i: usize) -> Option<Segments> {
if i >= self.uri.segment_count() {
debug!("{} is >= segment count {}", i, self.uri().segment_count());
None
} else {
let mut segments = self.uri.segments();
for _ in segments.by_ref().take(i) { }
Some(segments)
}
}
#[doc(hidden)]
pub fn from_hyp(h_method: hyper::Method,
h_headers: hyper::header::Headers,
h_uri: hyper::RequestUri)
-> Result<Request<'static>, String> {
let uri = match h_uri {
hyper::RequestUri::AbsolutePath(s) => s,
_ => return Err(format!("Bad URI: {}", h_uri)),
};
let method = match Method::from_hyp(&h_method) {
Some(method) => method,
None => return Err(format!("Invalid method: {}", h_method))
};
let mut request = Request::new(method, uri);
if let Some(cookies) = h_headers.get::<hyper::header::Cookie>() {
request.set_cookies(cookies.to_cookie_jar(&[]));
}
for hyp in h_headers.iter() {
let header = Header::new(hyp.name().to_string(), hyp.value_string());
request.add_header(header);
}
Ok(request)
}
}
impl<'r> fmt::Display for Request<'r> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {}", Green.paint(&self.method), Blue.paint(&self.uri))?;
if self.method.supports_payload() && !self.content_type().is_any() {
write!(f, " {}", Yellow.paint(self.content_type()))?;
}
Ok(())
}
}