#![feature(if_let, slicing_syntax, default_type_params, phase, unboxed_closures, macro_rules)]
extern crate hyper;
#[phase(plugin, link)] extern crate log;
extern crate mime;
extern crate serialize;
use self::mime::Mime;
use std::fmt::{Formatter, Show};
use std::fmt::Error as FormatError;
use std::io::{File, IoErrorKind, IoResult, TempDir};
use std::io::fs::PathExtensions;
pub mod client;
pub mod server;
pub mod mime_guess;
pub struct MultipartFile<'a> {
filename: Option<String>,
content_type: Mime,
reader: &'a mut Reader + 'a,
tmp_dir: Option<&'a str>,
}
impl<'a> MultipartFile<'a> {
fn from_octet(filename: Option<String>, reader: &'a mut Reader, cont_type: &str, tmp_dir: &'a str) -> MultipartFile<'a> {
MultipartFile {
filename: filename,
reader: reader,
content_type: from_str(cont_type).unwrap_or_else(mime_guess::octet_stream),
tmp_dir: Some(tmp_dir),
}
}
fn from_file(filename: Option<String>, reader: &'a mut File, mime: Mime) -> MultipartFile<'a> {
MultipartFile {
filename: filename,
reader: reader,
content_type: mime,
tmp_dir: None,
}
}
pub fn save_as(&mut self, path: &Path) -> IoResult<()> {
let mut file = try!(File::create(path));
ref_copy(self.reader, &mut file)
}
pub fn save_in(&mut self, dir: &Path) -> IoResult<Path> {
assert!(dir.is_dir(), "Given path is not a directory!");
let path = dir.join(self.dest_filename());
try!(self.save_as(&path));
Ok(path)
}
pub fn save_temp(&mut self, tmp_dir: Option<&TempDir>) -> IoResult<Path> {
use std::os;
let dir = match tmp_dir {
Some(tmp_dir) => tmp_dir.path().clone(),
None => os::tmpdir().join(self.tmp_dir.unwrap()),
};
self.save_in(&dir)
}
fn dest_filename(&self) -> String {
self.filename.as_ref().map_or_else(|| random_alphanumeric(10), |s| s.clone())
}
pub fn filename(&self) -> Option<&str> {
self.filename.as_ref().map(|s| s[])
}
pub fn content_type(&self) -> Mime {
self.content_type.clone()
}
}
impl<'a> Show for MultipartFile<'a> {
fn fmt(&self, fmt: &mut Formatter) -> Result<(), FormatError> {
write!(fmt, "Filename: {} Content-Type: {}", self.filename, self.content_type)
}
}
#[deriving(Show)]
pub enum MultipartField<'a> {
Text(String),
File(MultipartFile<'a>),
}
impl<'a> MultipartField<'a> {
pub fn as_text<'a>(&'a self) -> Option<&'a str> {
match *self {
MultipartField::Text(ref s) => Some(s[]),
_ => None,
}
}
pub fn to_text(self) -> Result<String, MultipartField<'a>> {
match self {
MultipartField::Text(s) => Ok(s),
_ => Err(self),
}
}
pub fn as_file<'b>(&'b mut self) -> Option<&'b mut MultipartFile<'a>> {
match *self {
MultipartField::File(ref mut file) => Some(file),
_ => None,
}
}
pub fn to_file(self) -> Result<MultipartFile<'a>, MultipartField<'a>> {
match self {
MultipartField::File(file) => Ok(file),
_ => Err(self),
}
}
}
impl<'a> Reader for MultipartFile<'a> {
fn read(&mut self, buf: &mut [u8]) -> IoResult<uint>{
self.reader.read(buf)
}
}
pub fn ref_copy(r: &mut Reader, w: &mut Writer) -> IoResult<()> {
let mut buf = [0, ..1024 * 64];
loop {
let len = match r.read(&mut buf) {
Ok(len) => len,
Err(ref e) if e.kind == IoErrorKind::EndOfFile => return Ok(()),
Err(e) => return Err(e),
};
try!(w.write(buf[..len]));
}
}
fn random_alphanumeric(len: uint) -> String {
use std::rand::{task_rng, Rng};
task_rng().gen_ascii_chars().map(|ch| ch.to_lowercase()).take(len).collect()
}