use std::marker::PhantomData;
use js_sys::Uint8Array;
use wasm_bindgen::{throw_val, JsCast, JsValue};
use wasm_bindgen_futures::JsFuture;
use crate::util::{checked_cast_to_usize, clamp_to_u32, promise_to_void_future};
use super::{sys, IntoAsyncRead, ReadableStream};
#[derive(Debug)]
pub struct ReadableStreamBYOBReader<'stream> {
raw: sys::ReadableStreamBYOBReader,
_stream: PhantomData<&'stream mut ReadableStream>,
}
impl<'stream> ReadableStreamBYOBReader<'stream> {
pub(crate) fn new(stream: &mut ReadableStream) -> Result<Self, js_sys::Error> {
Ok(Self {
raw: stream.as_raw().get_reader_with_options(
sys::ReadableStreamGetReaderOptions::new(sys::ReadableStreamReaderMode::BYOB),
)?,
_stream: PhantomData,
})
}
#[inline]
pub fn as_raw(&self) -> &sys::ReadableStreamBYOBReader {
&self.raw
}
pub async fn closed(&self) -> Result<(), JsValue> {
promise_to_void_future(self.as_raw().closed()).await
}
pub async fn cancel(&mut self) -> Result<(), JsValue> {
promise_to_void_future(self.as_raw().cancel()).await
}
pub async fn cancel_with_reason(&mut self, reason: &JsValue) -> Result<(), JsValue> {
promise_to_void_future(self.as_raw().cancel_with_reason(reason)).await
}
pub async fn read(&mut self, dst: &mut [u8]) -> Result<usize, JsValue> {
let buffer = Uint8Array::new_with_length(clamp_to_u32(dst.len()));
let (bytes_read, _) = self.read_with_buffer(dst, buffer).await?;
Ok(bytes_read)
}
pub async fn read_with_buffer(
&mut self,
dst: &mut [u8],
buffer: Uint8Array,
) -> Result<(usize, Option<Uint8Array>), JsValue> {
let buffer_offset = buffer.byte_offset();
let buffer_len = buffer.byte_length();
let dst_len = clamp_to_u32(dst.len());
let view = buffer
.subarray(0, dst_len)
.unchecked_into::<sys::ArrayBufferView>();
let promise = self.as_raw().read(&view);
let js_value = JsFuture::from(promise).await?;
let result = sys::ReadableStreamBYOBReadResult::from(js_value);
let filled_view = match result.value() {
Some(view) => view,
None => {
assert!(result.is_done());
return Ok((0, None));
}
};
let filled_len = checked_cast_to_usize(filled_view.byte_length());
debug_assert!(filled_len <= dst.len());
let new_buffer = Uint8Array::new_with_byte_offset_and_length(
&filled_view.buffer(),
buffer_offset,
buffer_len,
);
if result.is_done() {
debug_assert_eq!(filled_len, 0);
} else {
filled_view.copy_to(&mut dst[0..filled_len]);
}
Ok((filled_len, Some(new_buffer)))
}
#[inline]
pub fn release_lock(mut self) {
self.release_lock_mut()
}
fn release_lock_mut(&mut self) {
self.as_raw()
.release_lock()
.unwrap_or_else(|error| throw_val(error.into()))
}
#[inline]
pub fn try_release_lock(self) -> Result<(), (js_sys::Error, Self)> {
self.as_raw().release_lock().map_err(|error| (error, self))
}
#[inline]
pub fn into_async_read(self) -> IntoAsyncRead<'stream> {
IntoAsyncRead::new(self, false)
}
}
impl Drop for ReadableStreamBYOBReader<'_> {
fn drop(&mut self) {
self.release_lock_mut();
}
}