use std::fmt;
use std::io::{self, Read};
use std::sync::{Arc, Mutex};
pub enum Body {
Bytes(Vec<u8>),
Stream(Arc<Mutex<Box<dyn Read + Send>>>),
}
impl Body {
pub fn empty() -> Self {
Body::Bytes(Vec::new())
}
pub fn stream(reader: impl Read + Send + 'static) -> Self {
Body::Stream(Arc::new(Mutex::new(Box::new(reader))))
}
pub fn is_stream(&self) -> bool {
matches!(self, Body::Stream(_))
}
pub fn as_bytes(&self) -> &[u8] {
match self {
Body::Bytes(bytes) => bytes,
Body::Stream(_) => &[],
}
}
pub fn body_bytes(&mut self) -> io::Result<&[u8]> {
if let Body::Stream(stream) = self {
let mut bytes = Vec::new();
stream.lock().map_err(|_| io::Error::other("body stream poisoned"))?.read_to_end(&mut bytes)?;
*self = Body::Bytes(bytes);
}
Ok(self.as_bytes())
}
pub fn into_reader(self) -> Box<dyn Read + Send> {
match self {
Body::Bytes(bytes) => Box::new(io::Cursor::new(bytes)),
Body::Stream(stream) => Box::new(SharedReader(stream)),
}
}
pub fn into_vec(mut self) -> io::Result<Vec<u8>> {
self.body_bytes()?;
match self {
Body::Bytes(bytes) => Ok(bytes),
Body::Stream(_) => unreachable!("body_bytes turns a stream into bytes"),
}
}
pub fn bytes_mut(&mut self) -> io::Result<&mut Vec<u8>> {
self.body_bytes()?;
match self {
Body::Bytes(bytes) => Ok(bytes),
Body::Stream(_) => unreachable!("body_bytes turns a stream into bytes"),
}
}
}
struct SharedReader(Arc<Mutex<Box<dyn Read + Send>>>);
impl Read for SharedReader {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.0.lock().map_err(|_| io::Error::other("body stream poisoned"))?.read(buf)
}
}
impl Read for &Body {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match self {
Body::Bytes(_) => Ok(0),
Body::Stream(stream) => stream.lock().map_err(|_| io::Error::other("body stream poisoned"))?.read(buf),
}
}
}
impl Default for Body {
fn default() -> Self {
Body::empty()
}
}
impl std::ops::Deref for Body {
type Target = [u8];
fn deref(&self) -> &[u8] {
self.as_bytes()
}
}
impl AsRef<[u8]> for Body {
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl Clone for Body {
fn clone(&self) -> Self {
match self {
Body::Bytes(bytes) => Body::Bytes(bytes.clone()),
Body::Stream(stream) => Body::Stream(Arc::clone(stream)),
}
}
}
impl PartialEq for Body {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Body::Bytes(a), Body::Bytes(b)) => a == b,
(Body::Stream(a), Body::Stream(b)) => Arc::ptr_eq(a, b),
_ => false,
}
}
}
impl Eq for Body {}
impl PartialEq<Vec<u8>> for Body {
fn eq(&self, other: &Vec<u8>) -> bool {
matches!(self, Body::Bytes(bytes) if bytes == other)
}
}
impl PartialEq<[u8]> for Body {
fn eq(&self, other: &[u8]) -> bool {
matches!(self, Body::Bytes(bytes) if bytes.as_slice() == other)
}
}
impl PartialEq<Body> for Vec<u8> {
fn eq(&self, other: &Body) -> bool {
other == self
}
}
impl PartialEq<Body> for &[u8] {
fn eq(&self, other: &Body) -> bool {
other == *self
}
}
impl PartialEq<Body> for [u8] {
fn eq(&self, other: &Body) -> bool {
other == self
}
}
impl fmt::Debug for Body {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Body::Bytes(bytes) => f.debug_tuple("Bytes").field(&bytes.len()).finish(),
Body::Stream(_) => f.write_str("Stream"),
}
}
}
impl From<Vec<u8>> for Body {
fn from(bytes: Vec<u8>) -> Self {
Body::Bytes(bytes)
}
}
impl From<&[u8]> for Body {
fn from(bytes: &[u8]) -> Self {
Body::Bytes(bytes.to_vec())
}
}
impl<const N: usize> From<&[u8; N]> for Body {
fn from(bytes: &[u8; N]) -> Self {
Body::Bytes(bytes.to_vec())
}
}
impl From<String> for Body {
fn from(text: String) -> Self {
Body::Bytes(text.into_bytes())
}
}
impl From<&str> for Body {
fn from(text: &str) -> Self {
Body::Bytes(text.as_bytes().to_vec())
}
}
#[cfg(test)]
mod tests {
use super::Body;
use std::io::Read;
#[test]
fn bytes_deref_compare_and_convert() {
let body: Body = b"abc".to_vec().into();
assert_eq!(3, body.len());
assert_eq!(b"abc", &body[..]);
assert_eq!(b"abc".to_vec(), body);
assert!(body == b"abc".to_vec());
assert_eq!(Body::from("abc"), Body::from(&b"abc"[..]));
assert!(!body.is_stream());
assert_eq!(b"abc".to_vec(), body.clone().into_vec().unwrap());
assert!(Body::default().is_empty());
}
#[test]
fn a_stream_reads_once_and_becomes_bytes() {
let mut body = Body::stream(std::io::Cursor::new(b"streamed".to_vec()));
assert!(body.is_stream());
assert!(body.is_empty(), "a stream derefs to nothing until read");
assert_eq!(b"streamed", body.body_bytes().unwrap());
assert!(!body.is_stream());
assert_eq!(b"streamed", &body[..]);
}
#[test]
fn clones_of_a_stream_share_it() {
let body = Body::stream(std::io::Cursor::new(b"0123456789".to_vec()));
let other = body.clone();
assert_eq!(body, other);
let mut first = [0u8; 4];
(&body).read_exact(&mut first).unwrap();
let mut rest = Vec::new();
other.into_reader().read_to_end(&mut rest).unwrap();
assert_eq!(b"0123", &first);
assert_eq!(b"456789".to_vec(), rest);
assert_ne!(Body::stream(std::io::empty()), Body::stream(std::io::empty()));
}
#[test]
fn a_failing_stream_is_an_error() {
struct Broken;
impl Read for Broken {
fn read(&mut self, _: &mut [u8]) -> std::io::Result<usize> {
Err(std::io::Error::other("client went away"))
}
}
let mut body = Body::stream(Broken);
assert!(body.body_bytes().is_err());
}
}