Skip to content

Commit e2d7630

Browse files
committed
Finalize From/Into conversions for Body
1 parent 3eeb898 commit e2d7630

File tree

2 files changed

+165
-104
lines changed

2 files changed

+165
-104
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ pin-project-lite = "0.1.0"
3838
url = "2.1.0"
3939
serde_json = "1.0.51"
4040
serde = { version = "1.0.106", features = ["derive"] }
41+
serde_urlencoded = "0.6.1"
4142

4243
[dev-dependencies]
4344
http = "0.2.0"

src/body.rs

Lines changed: 164 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,23 @@ impl Body {
113113
}
114114
}
115115

116+
/// Get the inner reader from the `Body`
117+
///
118+
/// # Examples
119+
///
120+
/// ```
121+
/// # use std::io::prelude::*;
122+
/// use http_types::Body;
123+
/// use async_std::io::Cursor;
124+
///
125+
/// let cursor = Cursor::new("Hello Nori");
126+
/// let body = Body::from_reader(cursor, None);
127+
/// let _ = body.into_reader();
128+
/// ```
129+
pub fn into_reader(self) -> Box<dyn BufRead + Unpin + Send + 'static> {
130+
self.reader
131+
}
132+
116133
/// Create a `Body` from a Vec of bytes.
117134
///
118135
/// The Mime type is set to `application/octet-stream` if no other mime type has been set or can
@@ -160,6 +177,54 @@ impl Body {
160177
Ok(buf)
161178
}
162179

180+
/// Create a `Body` from a String
181+
///
182+
/// The Mime type is set to `text/plain` if no other mime type has been set or can
183+
/// be sniffed. If a `Body` has no length, HTTP implementations will often switch over to
184+
/// framed messages such as [Chunked
185+
/// Encoding](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding).
186+
///
187+
/// # Examples
188+
///
189+
/// ```
190+
/// use http_types::{Body, Response, StatusCode};
191+
/// use async_std::io::Cursor;
192+
///
193+
/// let mut req = Response::new(StatusCode::Ok);
194+
///
195+
/// let input = String::from("hello Nori!");
196+
/// req.set_body(Body::from_bytes(input));
197+
/// ```
198+
pub fn from_string(s: String) -> Self {
199+
Self {
200+
mime: mime::PLAIN,
201+
length: Some(s.len()),
202+
reader: Box::new(io::Cursor::new(s.into_bytes())),
203+
}
204+
}
205+
206+
/// Read the body as a string
207+
///
208+
/// # Examples
209+
///
210+
/// ```
211+
/// # use std::io::prelude::*;
212+
/// # fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
213+
/// # async_std::task::block_on(async {
214+
/// use http_types::Body;
215+
/// use async_std::io::Cursor;
216+
///
217+
/// let cursor = Cursor::new("Hello Nori");
218+
/// let body = Body::from_reader(cursor, None);
219+
/// assert_eq!(&body.into_string().await.unwrap(), "Hello Nori");
220+
/// # Ok(()) }) }
221+
/// ```
222+
pub async fn into_string(mut self) -> io::Result<String> {
223+
let mut result = String::with_capacity(self.len().unwrap_or(0));
224+
self.read_to_string(&mut result).await?;
225+
Ok(result)
226+
}
227+
163228
/// Creates a `Body` from a type, serializing it as JSON.
164229
///
165230
/// # Mime
@@ -171,10 +236,10 @@ impl Body {
171236
/// ```
172237
/// use http_types::{Body, convert::json};
173238
///
174-
/// let body = Body::from_json(json!({ "name": "Chashu" }));
239+
/// let body = Body::from_json(&json!({ "name": "Chashu" }));
175240
/// # drop(body);
176241
/// ```
177-
pub fn from_json(json: impl Serialize) -> crate::Result<Self> {
242+
pub fn from_json(json: &impl Serialize) -> crate::Result<Self> {
178243
let bytes = serde_json::to_vec(&json)?;
179244
let body = Self {
180245
length: Some(bytes.len()),
@@ -184,6 +249,99 @@ impl Body {
184249
Ok(body)
185250
}
186251

252+
/// Parse the body as JSON, serializing it to a struct.
253+
///
254+
/// # Examples
255+
///
256+
/// ```
257+
/// # fn main() -> Result<(), http_types::Error> { async_std::task::block_on(async {
258+
/// use http_types::Body;
259+
/// use http_types::convert::{Serialize, Deserialize};
260+
///
261+
/// #[derive(Debug, Serialize, Deserialize)]
262+
/// struct Cat { name: String }
263+
///
264+
/// let cat = Cat { name: String::from("chashu") };
265+
/// let body = Body::from_json(cat)?;
266+
///
267+
/// let cat: Cat = body.into_json().await?;
268+
/// assert_eq!(&cat.name, "chashu");
269+
/// # Ok(()) }) }
270+
/// ```
271+
pub async fn into_json<T: DeserializeOwned>(mut self) -> crate::Result<T> {
272+
let mut buf = Vec::with_capacity(1024);
273+
self.read_to_end(&mut buf).await?;
274+
Ok(serde_json::from_slice(&buf).map_err(io::Error::from)?)
275+
}
276+
277+
/// Creates a `Body` from a type, serializing it using form encoding.
278+
///
279+
/// # Mime
280+
///
281+
/// The encoding is set to `application/x-www-form-urlencoded`.
282+
///
283+
/// # Errors
284+
///
285+
/// An error will be returned if the encoding failed.
286+
///
287+
/// # Examples
288+
///
289+
/// ```
290+
/// # fn main() -> Result<(), http_types::Error> { async_std::task::block_on(async {
291+
/// use http_types::Body;
292+
/// use http_types::convert::{Serialize, Deserialize};
293+
///
294+
/// #[derive(Debug, Serialize, Deserialize)]
295+
/// struct Cat { name: String }
296+
///
297+
/// let cat = Cat { name: String::from("chashu") };
298+
/// let body = Body::from_form(&cat)?;
299+
///
300+
/// let cat: Cat = body.into_form().await?;
301+
/// assert_eq!(&cat.name, "chashu");
302+
/// # Ok(()) }) }
303+
/// ```
304+
pub fn from_form(form: &impl Serialize) -> crate::Result<Self> {
305+
let query = serde_urlencoded::to_string(form)?;
306+
let bytes = query.into_bytes();
307+
308+
let body = Self {
309+
length: Some(bytes.len()),
310+
reader: Box::new(Cursor::new(bytes)),
311+
mime: mime::FORM,
312+
};
313+
Ok(body)
314+
}
315+
316+
/// Parse the body from form encoding into a type.
317+
///
318+
/// # Errors
319+
///
320+
/// An error is returned if the underlying IO stream errors, or if the body
321+
/// could not be deserialized into the type.
322+
///
323+
/// # Examples
324+
///
325+
/// ```
326+
/// # fn main() -> Result<(), http_types::Error> { async_std::task::block_on(async {
327+
/// use http_types::Body;
328+
/// use http_types::convert::{Serialize, Deserialize};
329+
///
330+
/// #[derive(Debug, Serialize, Deserialize)]
331+
/// struct Cat { name: String }
332+
///
333+
/// let cat = Cat { name: String::from("chashu") };
334+
/// let body = Body::from_form(cat)?;
335+
///
336+
/// let cat: Cat = body.into_form().await?;
337+
/// assert_eq!(&cat.name, "chashu");
338+
/// # Ok(()) }) }
339+
/// ```
340+
pub async fn into_form<T: DeserializeOwned>(self) -> crate::Result<T> {
341+
let s = self.into_string().await?;
342+
Ok(serde_urlencoded::from_str(&s)?)
343+
}
344+
187345
/// Create a `Body` from a file.
188346
///
189347
/// The Mime type set to `application/octet-stream` if no other mime type has
@@ -230,70 +388,6 @@ impl Body {
230388
self.length.map(|length| length == 0)
231389
}
232390

233-
/// Get the inner reader from the `Body`
234-
///
235-
/// # Examples
236-
///
237-
/// ```
238-
/// # use std::io::prelude::*;
239-
/// use http_types::Body;
240-
/// use async_std::io::Cursor;
241-
///
242-
/// let cursor = Cursor::new("Hello Nori");
243-
/// let body = Body::from_reader(cursor, None);
244-
/// let _ = body.into_reader();
245-
/// ```
246-
pub fn into_reader(self) -> Box<dyn BufRead + Unpin + Send + 'static> {
247-
self.reader
248-
}
249-
250-
/// Read the body as a string
251-
///
252-
/// # Examples
253-
///
254-
/// ```
255-
/// # use std::io::prelude::*;
256-
/// # fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
257-
/// # async_std::task::block_on(async {
258-
/// use http_types::Body;
259-
/// use async_std::io::Cursor;
260-
///
261-
/// let cursor = Cursor::new("Hello Nori");
262-
/// let body = Body::from_reader(cursor, None);
263-
/// assert_eq!(&body.into_string().await.unwrap(), "Hello Nori");
264-
/// # Ok(()) }) }
265-
/// ```
266-
pub async fn into_string(mut self) -> io::Result<String> {
267-
let mut result = String::with_capacity(self.len().unwrap_or(0));
268-
self.read_to_string(&mut result).await?;
269-
Ok(result)
270-
}
271-
272-
/// Parse the body as JSON, serializing it to a struct.
273-
///
274-
/// # Examples
275-
///
276-
/// ```
277-
/// # fn main() -> Result<(), http_types::Error> { async_std::task::block_on(async {
278-
/// use http_types::Body;
279-
/// use http_types::convert::{Serialize, Deserialize};
280-
///
281-
/// #[derive(Debug, Serialize, Deserialize)]
282-
/// struct Cat { name: String }
283-
///
284-
/// let cat = Cat { name: String::from("chashu") };
285-
/// let body = Body::from_json(cat)?;
286-
///
287-
/// let cat: Cat = body.into_json().await?;
288-
/// assert_eq!(&cat.name, "chashu");
289-
/// # Ok(()) }) }
290-
/// ```
291-
pub async fn into_json<T: DeserializeOwned>(mut self) -> crate::Result<T> {
292-
let mut buf = Vec::with_capacity(1024);
293-
self.read_to_end(&mut buf).await?;
294-
Ok(serde_json::from_slice(&buf).map_err(io::Error::from)?)
295-
}
296-
297391
pub(crate) fn mime(&self) -> &Mime {
298392
&self.mime
299393
}
@@ -310,59 +404,25 @@ impl Debug for Body {
310404

311405
impl From<String> for Body {
312406
fn from(s: String) -> Self {
313-
Self {
314-
length: Some(s.len()),
315-
reader: Box::new(Cursor::new(s.into_bytes())),
316-
mime: mime::PLAIN,
317-
}
407+
Self::from_string(s)
318408
}
319409
}
320410

321411
impl<'a> From<&'a str> for Body {
322412
fn from(s: &'a str) -> Self {
323-
Self {
324-
length: Some(s.len()),
325-
reader: Box::new(Cursor::new(s.to_owned().into_bytes())),
326-
mime: mime::PLAIN,
327-
}
413+
Self::from_string(s.to_owned())
328414
}
329415
}
330416

331417
impl From<Vec<u8>> for Body {
332418
fn from(b: Vec<u8>) -> Self {
333-
Self {
334-
length: Some(b.len()),
335-
reader: Box::new(Cursor::new(b)),
336-
mime: mime::BYTE_STREAM,
337-
}
419+
Self::from_bytes(b.to_owned())
338420
}
339421
}
340422

341423
impl<'a> From<&'a [u8]> for Body {
342424
fn from(b: &'a [u8]) -> Self {
343-
Self {
344-
length: Some(b.len()),
345-
reader: Box::new(io::Cursor::new(b.to_owned())),
346-
mime: mime::BYTE_STREAM,
347-
}
348-
}
349-
}
350-
351-
#[cfg(feature = "async_std")]
352-
impl From<async_std::fs::File> for Body {
353-
fn from(file: async_std::fs::File) -> Self {
354-
let length = async_std::task::block_on(async {
355-
file.metadata()
356-
.await
357-
.expect("unable to read file metadata")
358-
.len()
359-
});
360-
361-
Self {
362-
length: Some(length as usize),
363-
reader: Box::new(async_std::io::BufReader::new(file)),
364-
mime: mime::BYTE_STREAM,
365-
}
425+
Self::from_bytes(b.to_owned())
366426
}
367427
}
368428

0 commit comments

Comments
 (0)