Repository: seanmonstar/reqwest (Rust)

Issue:
Part::stream takes hyper::Chunk instead of reqwest::r#async::Chunk
I'm writing an API client library that wraps a subset of reqwest's async multipart API (I can't re-export the entire thing because some of the methods don't make sense for my use case). Unfortunately, `Part::stream` takes a stream of items that implement `Into<hyper::Chunk>`. This means I have to explicitly depend on hyper to be able to write the wrapping function's signature. 

I propose changing `Part::stream` to instead take a stream of `Into<reqwest::r#async::Chunk>`s, and implementing all of `hyper::Chunk`'s `From` impls for `reqwest::r#async::Chunk`. I guess this would identify as a breaking change and would have to be postponed until reqwest v0.10.

If there are no arguments against my proposed solution, I'll gladly write a pull request myself. :-)


Discussion:
Woops, that's an oversight! The change would be a breaking one, so I'd group it with the upcoming 0.10 release.



Patch:
diff --git a/src/async_impl/body.rs b/src/async_impl/body.rs
index d118e6696..e73ac4ede 100644
--- a/src/async_impl/body.rs
+++ b/src/async_impl/body.rs
@@ -165,6 +165,7 @@ impl Chunk {
         }
     }
 }
+
 impl Buf for Chunk {
     fn bytes(&self) -> &[u8] {
         self.inner.bytes()
@@ -210,10 +211,40 @@ impl IntoIterator for Chunk {
     }
 }
 
+impl From<Vec<u8>> for Chunk {
+    fn from(v: Vec<u8>) -> Chunk {
+        Chunk { inner: v.into() }
+    }
+}
+
+impl From<&'static [u8]> for Chunk {
+    fn from(slice: &'static [u8]) -> Chunk {
+        Chunk { inner: slice.into() }
+    }
+}
+
+impl From<String> for Chunk {
+    fn from(s: String) -> Chunk {
+        Chunk { inner: s.into() }
+    }
+}
+
+impl From<&'static str> for Chunk {
+    fn from(slice: &'static str) -> Chunk {
+        Chunk { inner: slice.into() }
+    }
+}
+
+impl From<Bytes> for Chunk {
+    fn from(bytes: Bytes) -> Chunk {
+        Chunk { inner: bytes.into() }
+    }
+}
+
 impl From<Chunk> for hyper::Chunk {
-  fn from(val: Chunk) -> hyper::Chunk {
-    val.inner
-  }
+    fn from(val: Chunk) -> hyper::Chunk {
+        val.inner
+    }
 }
 
 impl fmt::Debug for Body {
diff --git a/src/async_impl/multipart.rs b/src/async_impl/multipart.rs
index 198a016af..fece365cc 100644
--- a/src/async_impl/multipart.rs
+++ b/src/async_impl/multipart.rs
@@ -9,7 +9,7 @@ use http::HeaderMap;
 
 use futures::Stream;
 
-use super::Body;
+use super::{Body, Chunk};
 
 /// An async multipart/form-data request.
 pub struct Form {
@@ -185,10 +185,10 @@ impl Part {
     pub fn stream<T>(value: T) -> Part
     where
         T: Stream + Send + 'static,
+        T::Item: Into<Chunk>,
         T::Error: std::error::Error + Send + Sync,
-        hyper::Chunk: std::convert::From<T::Item>,
     {
-        Part::new(Body::wrap(hyper::Body::wrap_stream(value)))
+        Part::new(Body::wrap(hyper::Body::wrap_stream(value.map(|chunk| chunk.into()))))
     }
 
     fn new(value: Body) -> Part {
@@ -477,13 +477,13 @@ mod tests {
     #[test]
     fn stream_to_end() {
         let mut form = Form::new()
-            .part("reader1", Part::stream(futures::stream::once::<_, hyper::Error>(Ok(hyper::Chunk::from("part1".to_owned())))))
+            .part("reader1", Part::stream(futures::stream::once::<_, hyper::Error>(Ok(Chunk::from("part1".to_owned())))))
             .part("key1", Part::text("value1"))
             .part(
                 "key2",
                 Part::text("value2").mime(mime::IMAGE_BMP),
             )
-            .part("reader2", Part::stream(futures::stream::once::<_, hyper::Error>(Ok(hyper::Chunk::from("part2".to_owned())))))
+            .part("reader2", Part::stream(futures::stream::once::<_, hyper::Error>(Ok(Chunk::from("part2".to_owned())))))
             .part(
                 "key3",
                 Part::text("value3").file_name("filename"),


Test patch:
diff --git a/tests/async.rs b/tests/async.rs
index f98996b1d..a0e95e110 100644
--- a/tests/async.rs
+++ b/tests/async.rs
@@ -14,7 +14,7 @@ use std::time::Duration;
 use futures::{Future, Stream};
 use tokio::runtime::current_thread::Runtime;
 
-use reqwest::r#async::Client;
+use reqwest::r#async::{Chunk, Client};
 use reqwest::r#async::multipart::{Form, Part};
 
 use bytes::Bytes;
@@ -105,7 +105,7 @@ fn response_json() {
 fn multipart() {
     let _ = env_logger::try_init();
 
-    let stream = futures::stream::once::<_, hyper::Error>(Ok(hyper::Chunk::from("part1 part2".to_owned())));
+    let stream = futures::stream::once::<_, hyper::Error>(Ok(Chunk::from("part1 part2".to_owned())));
     let part = Part::stream(stream);
 
     let form = Form::new()