use ferrijs_std::stream_web::ReadableStream;
use ferrijs_std::utils::bytes::ObjectBytes;
use rquickjs::{Class, Coerced, Ctx, Value};
use ferrijs_std::url::url_search_params::URLSearchParams;
use ferrijs_std::web::blob_bytes::blob_parts;
use ferrijs_std::web::form_data::FormDataJs;
pub(crate) enum BodySource<'js> {
Bytes(Vec<u8>),
Stream(Class<'js, ReadableStream<'js>>),
}
pub(crate) struct ExtractedBody<'js> {
pub source: BodySource<'js>,
pub content_type: Option<String>,
pub forced: bool,
}
impl ExtractedBody<'_> {
fn bytes(bytes: Vec<u8>, content_type: Option<&str>) -> Self {
Self {
source: BodySource::Bytes(bytes),
content_type: content_type.map(ToString::to_string),
forced: false,
}
}
}
pub(crate) fn extract_body<'js>(ctx: &Ctx<'js>, v: &Value<'js>) -> rquickjs::Result<Option<ExtractedBody<'js>>> {
if v.is_undefined() || v.is_null() {
return Ok(None);
}
if let Some(s) = v.as_string().and_then(|s| s.to_string().ok()) {
return Ok(Some(ExtractedBody::bytes(
s.into_bytes(),
Some("text/plain;charset=UTF-8"),
)));
}
if let Ok(stream) = Class::<ReadableStream<'js>>::from_value(v) {
return Ok(Some(ExtractedBody {
source: BodySource::Stream(stream),
content_type: None,
forced: false,
}));
}
if let Ok(fd) = Class::<FormDataJs>::from_value(v) {
let (bytes, content_type) = super::multipart::form_data_to_multipart(&fd.borrow());
return Ok(Some(ExtractedBody {
source: BodySource::Bytes(bytes),
content_type: Some(content_type),
forced: true,
}));
}
if let Some((bytes, mime)) = blob_parts(v) {
let mime = (!mime.is_empty()).then_some(mime);
return Ok(Some(ExtractedBody::bytes(bytes, mime.as_deref())));
}
if let Ok(params) = Class::<URLSearchParams>::from_value(v) {
return Ok(Some(ExtractedBody::bytes(
params.borrow().to_string().into_bytes(),
Some("application/x-www-form-urlencoded;charset=UTF-8"),
)));
}
if let Some(obj) = v.as_object()
&& let Some(bytes) = ObjectBytes::from_array_buffer(obj)?
{
return Ok(Some(ExtractedBody::bytes(bytes.into_bytes(ctx)?, None)));
}
if v.is_object()
&& let Ok(json) = crate::value::serde_from_js::<serde_json::Value>(ctx, v.clone())
{
return Ok(Some(ExtractedBody::bytes(
json.to_string().into_bytes(),
Some("application/json"),
)));
}
let text: Coerced<String> = rquickjs::FromJs::from_js(ctx, v.clone())?;
Ok(Some(ExtractedBody::bytes(
text.0.into_bytes(),
Some("text/plain;charset=UTF-8"),
)))
}