use crate::fnc::script::fetch::{stream::ReadableStream, RequestError};
use bytes::{Bytes, BytesMut};
use futures::{future, Stream, TryStreamExt};
use js::{ArrayBuffer, Class, Ctx, Error, Exception, FromJs, Result, Type, TypedArray, Value};
use std::{
cell::{Cell, RefCell},
result::Result as StdResult,
};
use super::classes::Blob;
pub type StreamItem = StdResult<Bytes, RequestError>;
#[derive(Clone)]
pub enum BodyKind {
Buffer,
String,
Blob(String),
}
pub enum BodyData {
Buffer(Bytes),
Stream(RefCell<ReadableStream<StreamItem>>),
Used,
}
pub struct Body {
pub kind: BodyKind,
pub data: Cell<BodyData>,
}
impl Default for Body {
fn default() -> Self {
Body::new()
}
}
impl Body {
pub fn new() -> Self {
Body {
kind: BodyKind::Buffer,
data: Cell::new(BodyData::Used),
}
}
pub fn used(&self) -> bool {
match self.data.replace(BodyData::Used) {
BodyData::Used => true,
x => {
self.data.set(x);
false
}
}
}
pub fn buffer<B>(kind: BodyKind, buffer: B) -> Self
where
B: Into<Bytes>,
{
let bytes = buffer.into();
Body {
kind,
data: Cell::new(BodyData::Buffer(bytes)),
}
}
pub fn stream<S>(kind: BodyKind, stream: S) -> Self
where
S: Stream<Item = StreamItem> + Send + Sync + 'static,
{
Body {
kind,
data: Cell::new(BodyData::Stream(RefCell::new(ReadableStream::new(stream)))),
}
}
pub async fn to_buffer(&self) -> StdResult<Option<Bytes>, RequestError> {
match self.data.replace(BodyData::Used) {
BodyData::Buffer(x) => Ok(Some(x)),
BodyData::Stream(stream) => {
let stream = stream.into_inner();
let mut res = BytesMut::new();
stream
.try_for_each(|x| {
res.extend_from_slice(&x);
future::ready(Ok(()))
})
.await?;
Ok(Some(res.freeze()))
}
BodyData::Used => Ok(None),
}
}
pub fn clone_js(&self, ctx: Ctx<'_>) -> Self {
let data = match self.data.replace(BodyData::Used) {
BodyData::Buffer(x) => {
let res = BodyData::Buffer(x.clone());
self.data.set(BodyData::Buffer(x));
res
}
BodyData::Stream(stream) => {
let (tee, drive) = stream.borrow_mut().tee();
ctx.spawn(drive);
self.data.set(BodyData::Stream(stream));
BodyData::Stream(RefCell::new(tee))
}
BodyData::Used => BodyData::Used,
};
Self {
kind: self.kind.clone(),
data: Cell::new(data),
}
}
}
impl<'js> FromJs<'js> for Body {
fn from_js(ctx: &Ctx<'js>, value: Value<'js>) -> Result<Self> {
let object = match value.type_of() {
Type::String => {
let string = value.as_string().unwrap().to_string()?;
return Ok(Body::buffer(BodyKind::String, string));
}
Type::Object => value.as_object().unwrap(),
x => {
return Err(Error::FromJs {
from: x.as_str(),
to: "Blob, TypedArray, FormData, URLSearchParams, or String",
message: None,
})
}
};
if let Some(x) = Class::<Blob>::from_object(object.clone()) {
let borrow = x.borrow();
return Ok(Body::buffer(BodyKind::Blob(borrow.mime.clone()), borrow.data.clone()));
}
if let Ok(x) = TypedArray::<i8>::from_object(object.clone()) {
let bytes = x
.as_bytes()
.ok_or_else(|| Exception::throw_type(ctx, "Buffer is already detached"))?;
return Ok(Body::buffer(BodyKind::Buffer, Bytes::copy_from_slice(bytes)));
}
if let Ok(x) = TypedArray::<u8>::from_object(object.clone()) {
let bytes = x
.as_bytes()
.ok_or_else(|| Exception::throw_type(ctx, "Buffer is already detached"))?;
return Ok(Body::buffer(BodyKind::Buffer, Bytes::copy_from_slice(bytes)));
}
if let Ok(x) = TypedArray::<i16>::from_object(object.clone()) {
let bytes = x
.as_bytes()
.ok_or_else(|| Exception::throw_type(ctx, "Buffer is already detached"))?;
return Ok(Body::buffer(BodyKind::Buffer, Bytes::copy_from_slice(bytes)));
}
if let Ok(x) = TypedArray::<u16>::from_object(object.clone()) {
let bytes = x
.as_bytes()
.ok_or_else(|| Exception::throw_type(ctx, "Buffer is already detached"))?;
return Ok(Body::buffer(BodyKind::Buffer, Bytes::copy_from_slice(bytes)));
}
if let Ok(x) = TypedArray::<i32>::from_object(object.clone()) {
let bytes = x
.as_bytes()
.ok_or_else(|| Exception::throw_type(ctx, "Buffer is already detached"))?;
return Ok(Body::buffer(BodyKind::Buffer, Bytes::copy_from_slice(bytes)));
}
if let Ok(x) = TypedArray::<u32>::from_object(object.clone()) {
let bytes = x
.as_bytes()
.ok_or_else(|| Exception::throw_type(ctx, "Buffer is already detached"))?;
return Ok(Body::buffer(BodyKind::Buffer, Bytes::copy_from_slice(bytes)));
}
if let Ok(x) = TypedArray::<i64>::from_object(object.clone()) {
let bytes = x
.as_bytes()
.ok_or_else(|| Exception::throw_type(ctx, "Buffer is already detached"))?;
return Ok(Body::buffer(BodyKind::Buffer, Bytes::copy_from_slice(bytes)));
}
if let Ok(x) = TypedArray::<u64>::from_object(object.clone()) {
let bytes = x
.as_bytes()
.ok_or_else(|| Exception::throw_type(ctx, "Buffer is already detached"))?;
return Ok(Body::buffer(BodyKind::Buffer, Bytes::copy_from_slice(bytes)));
}
if let Some(x) = ArrayBuffer::from_object(object.clone()) {
let bytes = x
.as_bytes()
.ok_or_else(|| Exception::throw_type(ctx, "Buffer is already detached"))?;
return Ok(Body::buffer(BodyKind::Buffer, Bytes::copy_from_slice(bytes)));
}
Err(Error::FromJs {
from: "object",
to: "Blob, TypedArray, FormData, URLSearchParams, or String",
message: None,
})
}
}