use serialize::{Serialize, Serializer};
use mime::{self, Mime};
use serialize::PairMap;
type Multipart = ::multipart::client::lazy::Multipart<'static, 'static>;
type PreparedFields = ::multipart::client::lazy::PreparedFields<'static>;
use url::form_urlencoded::Serializer as FormUrlEncoder;
use std::borrow::Borrow;
use std::fmt;
use std::io::{self, Cursor, Read};
use std::path::PathBuf;
use ::Result;
pub type ReadableResult<T> = Result<Readable<T>>;
#[derive(Debug)]
pub struct Readable<R> {
pub readable: R,
pub content_type: Option<Mime>,
_private: (),
}
impl<R: Read> Readable<R> {
pub fn new_ok<C: Into<Option<Mime>>>(readable: R, content_type: C) -> Result<Self> {
Ok(Self::new(readable, content_type))
}
pub fn new<C: Into<Option<Mime>>>(readable: R, content_type: C) -> Self {
Readable {
readable: readable,
content_type: content_type.into(),
_private: (),
}
}
}
pub trait Body: Send + 'static {
type Readable: Read + 'static;
fn into_readable<S>(self, ser: &S) -> ReadableResult<Self::Readable>
where S: Serializer;
}
impl<B: EagerBody + Send + 'static> Body for B {
type Readable = <B as EagerBody>::Readable;
fn into_readable<S>(self, ser: &S) -> ReadableResult<Self::Readable> where S: Serializer {
<B as EagerBody>::into_readable(self, ser)
}
}
pub trait EagerBody {
type Readable: Read + Send + 'static;
fn into_readable<S>(self, ser: &S) -> ReadableResult<Self::Readable>
where S: Serializer;
}
impl<B: Serialize> EagerBody for B {
type Readable = Cursor<Vec<u8>>;
fn into_readable<S>(self, ser: &S) -> ReadableResult<Self::Readable> where S: Serializer {
let mut buf = Vec::new();
try!(ser.serialize(&self, &mut buf));
Readable::new_ok(Cursor::new(buf), ser.content_type())
}
}
#[derive(Debug)]
pub struct RawBody<R>(Readable<R>);
impl<R: Read> RawBody<R> {
pub fn new<C: Into<Option<Mime>>>(readable: R, content_type: C) -> Self {
RawBody(Readable::new(readable, content_type))
}
}
impl<T: AsRef<[u8]>> RawBody<Cursor<T>> {
pub fn bytes(bytes: T) -> Self {
RawBody::new(Cursor::new(bytes), mime::octet_stream())
}
pub fn text(text: T) -> Self where T: Borrow<str> {
RawBody::new(Cursor::new(text), mime::text_plain_utf8())
}
}
impl RawBody<Cursor<String>> {
pub fn display<T: ToString>(text: &T) -> Self {
RawBody::text(text.to_string())
}
}
impl RawBody<Cursor<Vec<u8>>> {
pub fn serialize_now<S, T>(ser: &S, val: &T) -> Result<Self>
where S: Serializer, T: Serialize {
let mut buf: Vec<u8> = Vec::new();
try!(ser.serialize(val, &mut buf));
Ok(RawBody::new(Cursor::new(buf), ser.content_type()))
}
}
impl<R: Read + Send + 'static> EagerBody for RawBody<R> {
type Readable = R;
fn into_readable<S>(self, _ser: &S) -> ReadableResult<Self::Readable> where S: Serializer {
Ok(self.0)
}
}
impl<R> From<Readable<R>> for RawBody<R> {
fn from(readable: Readable<R>) -> Self {
RawBody(readable)
}
}
pub type RawBytesBody = RawBody<Cursor<Vec<u8>>>;
pub trait Fields {
type WithText: Fields;
fn with_text<K: ToString, V: ToString>(self, key: K, val: V) -> Self::WithText;
fn with_file<K: ToString>(self, key: K, file: FileField) -> MultipartFields;
}
#[derive(Debug)]
pub struct EmptyFields;
impl Fields for EmptyFields {
type WithText = TextFields;
fn with_text<K: ToString, V: ToString>(self, key: K, val: V) -> TextFields {
TextFields::new().with_text(key, val)
}
fn with_file<K: ToString>(self, key: K, file: FileField) -> MultipartFields {
MultipartFields::new().with_file(key, file)
}
}
impl Body for EmptyFields {
type Readable = io::Empty;
fn into_readable<S>(self, _ser: &S) -> ReadableResult<Self::Readable>
where S: Serializer {
Readable::new_ok(io::empty(), None)
}
}
#[derive(Debug)]
pub struct TextFields(PairMap<String, String>);
impl TextFields {
fn new() -> TextFields {
TextFields(PairMap::new())
}
fn push<K: ToString, V: ToString>(&mut self, key: K, val: V) {
self.0.insert(key.to_string(), val.to_string());
}
}
impl Fields for TextFields {
type WithText = Self;
fn with_text<K: ToString, V: ToString>(mut self, key: K, val: V) -> Self {
self.push(key, val);
self
}
fn with_file<K: ToString>(self, key: K, file: FileField) -> MultipartFields {
MultipartFields::from_text(self).with_file(key, file)
}
}
impl Body for TextFields {
type Readable = Cursor<String>;
fn into_readable<S>(self, _ser: &S) -> ReadableResult<Self::Readable> where S: Serializer {
let readable = Cursor::new(
FormUrlEncoder::new(String::new())
.extend_pairs(self.0.into_pairs())
.finish()
);
Readable::new_ok(readable, mime::form_urlencoded())
}
}
#[derive(Debug)]
pub struct MultipartFields {
text: PairMap<String, String>,
files: PairMap<String, FileField>,
}
impl MultipartFields {
fn new() -> Self {
Self::from_text(TextFields::new())
}
fn from_text(text: TextFields) -> Self {
MultipartFields {
text: text.0,
files: PairMap::new(),
}
}
}
impl Fields for MultipartFields {
type WithText = Self;
fn with_text<K: ToString, V: ToString>(mut self, key: K, val: V) -> Self::WithText {
self.text.insert(key.to_string(), val.to_string());
self
}
fn with_file<K: ToString>(mut self, key: K, file: FileField) -> MultipartFields {
self.files.insert(key.to_string(), file);
self
}
}
impl Body for MultipartFields {
type Readable = PreparedFields;
fn into_readable<S>(self, _ser: &S) -> ReadableResult<Self::Readable> where S: Serializer {
use self::FileField_::*;
let mut multipart = Multipart::new();
for (key, val) in self.text.into_pairs() {
multipart.add_text(key, val);
}
for (key, file) in self.files.into_pairs() {
match file.0 {
Stream {
stream,
filename,
content_type
} => {
stream.add_self(key, filename, content_type, &mut multipart);
},
Path(path) => {
multipart.add_file(key, path);
}
}
}
let prepared = try!(multipart.prepare());
let content_type = mime::formdata(prepared.boundary());
Readable::new_ok(prepared, content_type)
}
}
pub struct FileField(FileField_);
impl FileField {
pub fn from_stream<S: Read + Send + 'static>(stream: S, filename: Option<String>, content_type: Option<Mime>) -> Self {
FileField(FileField_::Stream {
stream: Box::new(stream),
filename: filename,
content_type: content_type
})
}
pub fn from_path<P: Into<PathBuf>>(path: P) -> Self {
FileField(FileField_::Path(path.into()))
}
}
impl fmt::Debug for FileField {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.0 {
FileField_::Stream {
ref content_type, ref filename, ..
} => f.debug_struct("FileField::Stream")
.field("stream", &"Box<Read + Send + 'static>")
.field("content_type", &content_type)
.field("filename", filename)
.finish(),
FileField_::Path(ref path) =>
f.debug_tuple("FileField::Path").field(path).finish()
}
}
}
enum FileField_ {
Stream {
stream: Box<StreamField>,
filename: Option<String>,
content_type: Option<Mime>,
},
Path(PathBuf),
}
trait StreamField: Read + Send + 'static {
fn add_self(self: Self, name: String, filename: Option<String>, content_type: Option<Mime>, to: &mut Multipart);
}
impl<T> StreamField for T where T: Read + Send + 'static {
fn add_self(self: Self, name: String, filename: Option<String>, content_type: Option<Mime>, to: &mut Multipart) {
to.add_stream(name, self, filename, content_type);
}
}
#[doc(hidden)]
pub trait AddField<F> {
type Output: Fields;
fn add_to<K: ToString>(self, key: K, to: F) -> Self::Output;
}
impl<F: Fields, T: ToString> AddField<F> for T {
type Output = <F as Fields>::WithText;
fn add_to<K: ToString>(self, key: K, to: F) -> F::WithText {
to.with_text(key, self)
}
}
impl<F: Fields> AddField<F> for FileField {
type Output = MultipartFields;
fn add_to<K: ToString>(self, key: K, to: F) -> MultipartFields {
to.with_file(key, self)
}
}