bare 0.0.1

a bare web framework
use std::fmt;
use std::old_io::{ IoResult, MemReader};
use std::old_io as io;

use hyper::net::Fresh;
pub use hyper::server::Response as HttpResponse;
pub use hyper::header as headers;

use status;

pub struct Response {
    pub content_type: Option<headers::ContentType>,
    pub body: Option<Box<Reader + Send>>,
    pub status: Option<status::Status>
}

pub trait IntoReader {
    type OutReader: Reader;

    fn into_reader(self) -> Self::OutReader;
}

impl Response {
    pub fn new() -> Response {
        Response { body: None, content_type: None, status: Some(status::Ok) }
    }

    pub fn set_body<I>(&mut self, data: I)
    where I: IntoReader, <I as IntoReader>::OutReader: Send + 'static {
        self.body = Some(Box::new(data.into_reader()));
    }

    pub fn with_body<I>(data: I) -> Response
    where I: IntoReader, <I as IntoReader>::OutReader: Send + 'static {
        let mut res = Response::new();
        res.body = Some(Box::new(data.into_reader()));
        res
    }

    pub fn write_back(self, mut http_res: HttpResponse<Fresh>) {
        // *http_res.headers_mut() = self.headers;

        // Default to a 404 if no response code was set
        *http_res.status_mut() = self.status.clone().unwrap_or(status::Ok);

        let out = match self.body {
            Some(body) => write_with_body(http_res, body),
            None => {
                http_res.headers_mut().set(headers::ContentLength(0));
                http_res.start().and_then(|res| res.end())
            }
        };

        match out {
            Err(e) => {
                error!("Error writing response: {}", e);
            },
            _ => {}
        }
    }
}

impl IntoReader for String {
   type OutReader = MemReader;

   fn into_reader(self) -> MemReader {
       MemReader::new(self.into_bytes())
   }
}

// impl IntoReader for Path {
//    type OutReader = File;

//    fn into_reader(self) -> File {
//        File::open(self).unwrap()
//    }
// }


impl fmt::Debug for Response {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(writeln!(f, "Response {{"));

        if let Some(ref content_type) = self.content_type {
            try!(writeln!(f, "    content_type: {:?}", content_type));
        }
        try!(writeln!(f, "    status: {:?}", self.status));
        try!(writeln!(f, "    body: {}", if self.body.is_some() { "{omitted}" } else { "{empty}"}));

        try!(write!(f, "}}"));
        Ok(())
    }
}

fn write_with_body(mut res: HttpResponse<Fresh>, mut body: Box<Reader + Send>) -> IoResult<()> {
    // let content_type = res.headers().get::<headers::ContentType>()
                           // .map(|cx| cx.clone())
                           // .unwrap_or_else(|| headers::ContentType("text/plain".parse().unwrap()));
    let content_type = headers::ContentType("application/json".parse().unwrap());
    res.headers_mut().set(content_type);

    let mut res = try!(res.start());

    // FIXME: Manually inlined io::util::copy
    // because Box<Reader + Send> does not impl Reader.
    //
    // Tracking issue: rust-lang/rust#18542
    let mut buf = &mut [0; 1024 * 64];
    loop {
        let len = match body.read(buf) {
            Ok(len) => len,
            Err(ref e) if e.kind == io::EndOfFile => break,
            Err(e) => { return Err(e) },
        };

        try!(res.write_all(&buf[..len]))
    }

    res.end()
}