Files
Notes/notes-service/vendor/wasm-streams/src/writable/mod.rs
T
2026-08-01 16:11:49 +03:00

191 lines
7.9 KiB
Rust

//! Bindings and conversions for
//! [writable streams](https://developer.mozilla.org/en-US/docs/Web/API/WritableStream).
use futures_util::Sink;
use wasm_bindgen::prelude::*;
pub use default_writer::WritableStreamDefaultWriter;
pub use into_async_write::IntoAsyncWrite;
pub use into_sink::IntoSink;
use into_underlying_sink::IntoUnderlyingSink;
use crate::util::promise_to_void_future;
mod default_writer;
mod into_async_write;
mod into_sink;
mod into_underlying_sink;
pub mod sys;
/// A [`WritableStream`](https://developer.mozilla.org/en-US/docs/Web/API/WritableStream).
///
/// `WritableStream`s can be created from a [raw JavaScript stream](sys::WritableStream) with
/// [`from_raw`](Self::from_raw), or from a Rust [`Sink`] with [`from_sink`](Self::from_sink).
///
/// They can be converted into a [raw JavaScript stream](sys::WritableStream) with
/// [`into_raw`](Self::into_raw), or into a Rust [`Sink`] with [`into_sink`](Self::into_sink).
///
/// [`Sink`]: https://docs.rs/futures/0.3.30/futures/sink/trait.Sink.html
#[derive(Debug)]
pub struct WritableStream {
raw: sys::WritableStream,
}
impl WritableStream {
/// Creates a new `WritableStream` from a [JavaScript stream](sys::WritableStream).
#[inline]
pub fn from_raw(raw: sys::WritableStream) -> Self {
Self { raw }
}
/// Creates a new `WritableStream` from a [`Sink`].
///
/// Items and errors must be represented as raw [`JsValue`]s.
/// Use [`with`] and/or [`sink_map_err`] to convert a sink's items to a `JsValue`
/// before passing it to this function.
///
/// [`Sink`]: https://docs.rs/futures/0.3.30/futures/sink/trait.Sink.html
/// [`with`]: https://docs.rs/futures/0.3.30/futures/sink/trait.SinkExt.html#method.with
/// [`sink_map_err`]: https://docs.rs/futures/0.3.30/futures/sink/trait.SinkExt.html#method.sink_map_err
pub fn from_sink<Si>(sink: Si) -> Self
where
Si: Sink<JsValue, Error = JsValue> + 'static,
{
let sink = IntoUnderlyingSink::new(Box::new(sink));
// Use the default queuing strategy (with a HWM of 1 chunk).
// We shouldn't set HWM to 0, since that would break piping to the writable stream.
let raw = sys::WritableStreamExt::new_with_into_underlying_sink(sink).unchecked_into();
Self::from_raw(raw)
}
/// Acquires a reference to the underlying [JavaScript stream](sys::WritableStream).
#[inline]
pub fn as_raw(&self) -> &sys::WritableStream {
&self.raw
}
/// Consumes this `WritableStream`, returning the underlying [JavaScript stream](sys::WritableStream).
#[inline]
pub fn into_raw(self) -> sys::WritableStream {
self.raw
}
/// Returns `true` if the stream is [locked to a writer](https://streams.spec.whatwg.org/#lock).
#[inline]
pub fn is_locked(&self) -> bool {
self.as_raw().locked()
}
/// [Aborts](https://streams.spec.whatwg.org/#abort-a-writable-stream) the stream,
/// signaling that the producer can no longer successfully write to the stream
/// and it is to be immediately moved to an errored state, with any queued-up writes discarded.
///
/// If the stream is currently locked to a writer, then this returns an error.
pub async fn abort(&mut self) -> Result<(), JsValue> {
promise_to_void_future(self.as_raw().abort()).await
}
/// [Aborts](https://streams.spec.whatwg.org/#abort-a-writable-stream) the stream with the
/// given `reason`, signaling that the producer can no longer successfully write to the stream
/// and it is to be immediately moved to an errored state, with any queued-up writes discarded.
///
/// If the stream is currently locked to a writer, then this returns an error.
pub async fn abort_with_reason(&mut self, reason: &JsValue) -> Result<(), JsValue> {
promise_to_void_future(self.as_raw().abort_with_reason(reason)).await
}
/// Creates a [writer](WritableStreamDefaultWriter) and
/// [locks](https://streams.spec.whatwg.org/#lock) the stream to the new writer.
///
/// While the stream is locked, no other writer can be acquired until this one is released.
///
/// **Panics** if the stream is already locked to a writer. For a non-panicking variant,
/// use [`try_get_writer`](Self::try_get_writer).
#[inline]
pub fn get_writer(&mut self) -> WritableStreamDefaultWriter<'_> {
self.try_get_writer()
.expect_throw("already locked to a writer")
}
/// Try to create a [writer](WritableStreamDefaultWriter) and
/// [lock](https://streams.spec.whatwg.org/#lock) the stream to the new writer.
///
/// While the stream is locked, no other writer can be acquired until this one is released.
///
/// If the stream is already locked to a writer, then this returns an error.
pub fn try_get_writer(&mut self) -> Result<WritableStreamDefaultWriter<'_>, js_sys::Error> {
WritableStreamDefaultWriter::new(self)
}
/// Converts this `WritableStream` into a [`Sink`].
///
/// Items and errors are represented by their raw [`JsValue`].
/// Use [`with`] and/or [`sink_map_err`] on the returned stream to convert them to a more
/// appropriate type.
///
/// **Panics** if the stream is already locked to a writer. For a non-panicking variant,
/// use [`try_into_sink`](Self::try_into_sink).
///
/// [`Sink`]: https://docs.rs/futures/0.3.30/futures/sink/trait.Sink.html
/// [`with`]: https://docs.rs/futures/0.3.30/futures/sink/trait.SinkExt.html#method.with
/// [`sink_map_err`]: https://docs.rs/futures/0.3.30/futures/sink/trait.SinkExt.html#method.sink_map_err
#[inline]
pub fn into_sink(self) -> IntoSink<'static> {
self.try_into_sink()
.expect_throw("already locked to a writer")
}
/// Try to convert this `WritableStream` into a [`Sink`].
///
/// Items and errors are represented by their raw [`JsValue`].
/// Use [`with`] and/or [`sink_map_err`] on the returned stream to convert them to a more
/// appropriate type.
///
/// If the stream is already locked to a writer, then this returns an error
/// along with the original `WritableStream`.
///
/// [`Sink`]: https://docs.rs/futures/0.3.30/futures/sink/trait.Sink.html
/// [`with`]: https://docs.rs/futures/0.3.30/futures/sink/trait.SinkExt.html#method.with
/// [`sink_map_err`]: https://docs.rs/futures/0.3.30/futures/sink/trait.SinkExt.html#method.sink_map_err
pub fn try_into_sink(mut self) -> Result<IntoSink<'static>, (js_sys::Error, Self)> {
let writer = WritableStreamDefaultWriter::new(&mut self).map_err(|err| (err, self))?;
Ok(writer.into_sink())
}
/// Converts this `WritableStream` into an [`AsyncWrite`].
///
/// The writable stream must accept [`Uint8Array`](js_sys::Uint8Array) chunks.
///
/// **Panics** if the stream is already locked to a writer. For a non-panicking variant,
/// use [`try_into_async_write`](Self::try_into_async_write).
///
/// [`AsyncWrite`]: https://docs.rs/futures/0.3.30/futures/io/trait.AsyncWrite.html
pub fn into_async_write(self) -> IntoAsyncWrite<'static> {
self.try_into_async_write()
.expect_throw("already locked to a writer")
}
/// Try to convert this `WritableStream` into an [`AsyncWrite`].
///
/// The writable stream must accept [`Uint8Array`](js_sys::Uint8Array) chunks.
///
/// If the stream is already locked to a writer, then this returns an error
/// along with the original `WritableStream`.
///
/// [`AsyncWrite`]: https://docs.rs/futures/0.3.30/futures/io/trait.AsyncWrite.html
pub fn try_into_async_write(self) -> Result<IntoAsyncWrite<'static>, (js_sys::Error, Self)> {
Ok(IntoAsyncWrite::new(self.try_into_sink()?))
}
}
impl<Si> From<Si> for WritableStream
where
Si: Sink<JsValue, Error = JsValue> + 'static,
{
/// Equivalent to [`from_sink`](Self::from_sink).
#[inline]
fn from(sink: Si) -> Self {
Self::from_sink(sink)
}
}