Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ReadableStream::from(async_iterable) #23

Merged
merged 9 commits into from
Oct 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

## Unreleased

* Added `ReadableStream::from(async_iterable)` and `try_from(async_iterable)`. ([#23](https://github.com/MattiasBuelens/wasm-streams/pull/23))
* Stop calling `byobRequest.respond(0)` on cancel ([#16](https://github.com/MattiasBuelens/wasm-streams/pull/16))
* ⚠ **Breaking change:** The system modules (`readable::sys`, `writable::sys` and `transform::sys`) now re-export directly from [the `web-sys` crate](https://docs.rs/web-sys/latest/web_sys/). This should make it easier to use `from_raw()`, `as_raw()` and `into_raw()`. ([#22](https://github.com/MattiasBuelens/wasm-streams/pull/22/))
* ⚠ **Breaking change:** The system modules (`readable::sys`, `writable::sys` and `transform::sys`) now re-export directly from [the `web-sys` crate](https://docs.rs/web-sys/latest/web_sys/). This should make it easier to use `from_raw()`, `as_raw()` and `into_raw()`. ([#22](https://github.com/MattiasBuelens/wasm-streams/pull/22))

## v0.3.0 (2022-10-16)

Expand Down
42 changes: 40 additions & 2 deletions src/readable/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
//! [readable streams](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream).
use futures_util::io::AsyncRead;
use futures_util::Stream;
use js_sys::Object;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;

Expand Down Expand Up @@ -72,7 +73,7 @@ impl ReadableStream {
let strategy = QueuingStrategy::new(0.0);
let raw = sys::ReadableStreamExt::new_with_into_underlying_source(source, strategy)
.unchecked_into();
Self { raw }
Self::from_raw(raw)
}

/// Creates a new `ReadableStream` from an [`AsyncRead`].
Expand All @@ -95,7 +96,44 @@ impl ReadableStream {
let raw = sys::ReadableStreamExt::new_with_into_underlying_byte_source(source)
.expect_throw("readable byte streams not supported")
.unchecked_into();
Self { raw }
Self::from_raw(raw)
}

/// Creates a new `ReadableStream` wrapping the provided [iterable] or [async iterable].
///
/// This can be used to adapt various kinds of objects into a readable stream,
/// such as an [array], an [async generator] or a [Node.js readable stream][Readable].
///
/// **Panics** if `ReadableStream.from()` is not supported by the browser,
/// or if the given object is not a valid iterable or async iterable.
/// For a non-panicking variant, use [`try_from`](Self::try_from).
///
/// [iterable]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterable_protocol
/// [async iterable]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_async_iterator_and_async_iterable_protocols
/// [array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
/// [async generator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncGenerator
/// [Readable]: https://nodejs.org/api/stream.html#class-streamreadable
pub fn from(async_iterable: Object) -> Self {
Self::try_from(async_iterable).unwrap_throw()
}

/// Try to create a new `ReadableStream` wrapping the provided [iterable] or [async iterable].
///
/// This can be used to adapt various kinds of objects into a readable stream,
/// such as an [array], an [async generator] or a [Node.js readable stream][Readable].
///
/// If `ReadableStream.from()` is not supported by the browser,
/// or if the given object is not a valid iterable or async iterable,
/// then this returns an error.
///
/// [iterable]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_iterable_protocol
/// [async iterable]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_async_iterator_and_async_iterable_protocols
/// [array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
/// [async generator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncGenerator
/// [Readable]: https://nodejs.org/api/stream.html#class-streamreadable
pub fn try_from(async_iterable: Object) -> Result<Self, js_sys::Error> {
let raw = sys::ReadableStreamExt::from_async_iterable(&async_iterable)?.unchecked_into();
Ok(Self::from_raw(raw))
}

/// Acquires a reference to the underlying [JavaScript stream](sys::ReadableStream).
Expand Down
3 changes: 3 additions & 0 deletions src/readable/sys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ extern "C" {

#[wasm_bindgen(method, catch, js_class = ReadableStream, js_name = tee)]
pub(crate) fn try_tee(this: &ReadableStreamExt) -> Result<Array, Error>;

#[wasm_bindgen(catch, static_method_of = ReadableStreamExt, js_class = ReadableStream, js_name = from)]
pub(crate) fn from_async_iterable(async_iterable: &Object) -> Result<ReadableStreamExt, Error>;
}

#[wasm_bindgen]
Expand Down
2 changes: 1 addition & 1 deletion src/writable/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ impl WritableStream {
// 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();
WritableStream { raw }
Self::from_raw(raw)
}

/// Acquires a reference to the underlying [JavaScript stream](sys::WritableStream).
Expand Down
25 changes: 25 additions & 0 deletions tests/tests/readable_stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,3 +298,28 @@ async fn test_readable_stream_into_stream_then_into_async_read() {
assert_eq!(async_read.read(&mut buf).await.unwrap(), 0);
assert_eq!(&buf, &[4, 5, 6]);
}

#[wasm_bindgen_test]
async fn test_readable_stream_from_js_array() {
let js_array =
js_sys::Array::from_iter([JsValue::from_str("Hello"), JsValue::from_str("world!")]);
let mut readable = match ReadableStream::try_from(js_array.unchecked_into()) {
Ok(readable) => readable,
Err(err) => {
// ReadableStream.from() is not yet supported in all browsers.
assert_eq!(err.name(), "TypeError");
assert_eq!(
err.message().as_string().unwrap(),
"ReadableStream.from is not a function"
);
return;
}
};
assert!(!readable.is_locked());

let mut reader = readable.get_reader();
assert_eq!(reader.read().await.unwrap(), Some(JsValue::from("Hello")));
assert_eq!(reader.read().await.unwrap(), Some(JsValue::from("world!")));
assert_eq!(reader.read().await.unwrap(), None);
reader.closed().await.unwrap();
}
Loading