From abbf6556e33c8c2ae572bafcb51c53bde10396b7 Mon Sep 17 00:00:00 2001 From: Muhammad Hamza Date: Wed, 27 Apr 2022 03:20:53 +0500 Subject: [PATCH 1/2] move UncheckedIter to utils, use it in net::http::headers --- crates/net/src/http/headers.rs | 26 ++-------- crates/utils/Cargo.toml | 3 ++ crates/utils/src/iter.rs | 95 ++++++++++++++++++++++++++++++++++ crates/utils/src/lib.rs | 1 + 4 files changed, 104 insertions(+), 21 deletions(-) create mode 100644 crates/utils/src/iter.rs diff --git a/crates/net/src/http/headers.rs b/crates/net/src/http/headers.rs index c7fbac5b..0a9b1515 100644 --- a/crates/net/src/http/headers.rs +++ b/crates/net/src/http/headers.rs @@ -1,6 +1,7 @@ +use gloo_utils::iter::UncheckedIter; use js_sys::{Array, Map}; use std::fmt; -use wasm_bindgen::{JsCast, JsValue, UnwrapThrowExt}; +use wasm_bindgen::{JsCast, UnwrapThrowExt}; // I experimented with using `js_sys::Object` for the headers, since this object is marked // experimental in MDN. However it's in the fetch spec, and it's necessary for appending headers. @@ -68,7 +69,7 @@ impl Headers { // and everything works. Is there a better way? Should there be a `MapLike` or // `MapIterator` type in `js_sys`? let fake_map: &Map = self.raw.unchecked_ref(); - UncheckedIter(fake_map.entries()).map(|entry| { + UncheckedIter::from(fake_map.entries()).map(|entry| { let entry: Array = entry.unchecked_into(); let key = entry.get(0); let value = entry.get(1); @@ -82,13 +83,13 @@ impl Headers { /// Iterate over the names of the headers. pub fn keys(&self) -> impl Iterator { let fake_map: &Map = self.raw.unchecked_ref(); - UncheckedIter(fake_map.keys()).map(|key| key.as_string().unwrap_throw()) + UncheckedIter::from(fake_map.keys()).map(|key| key.as_string().unwrap_throw()) } /// Iterate over the values of the headers. pub fn values(&self) -> impl Iterator { let fake_map: &Map = self.raw.unchecked_ref(); - UncheckedIter(fake_map.values()).map(|v| v.as_string().unwrap_throw()) + UncheckedIter::from(fake_map.values()).map(|v| v.as_string().unwrap_throw()) } } @@ -101,20 +102,3 @@ impl fmt::Debug for Headers { dbg.finish() } } - -struct UncheckedIter(js_sys::Iterator); - -impl Iterator for UncheckedIter { - type Item = JsValue; - - fn next(&mut self) -> Option { - // we don't check for errors. Only use this type on things we know conform to the iterator - // interface. - let next = self.0.next().unwrap_throw(); - if next.done() { - None - } else { - Some(next.value()) - } - } -} diff --git a/crates/utils/Cargo.toml b/crates/utils/Cargo.toml index cd3d63d7..2e0faa33 100644 --- a/crates/utils/Cargo.toml +++ b/crates/utils/Cargo.toml @@ -26,3 +26,6 @@ features = [ "HtmlHeadElement", "Element", ] + +[dev-dependencies] +wasm-bindgen-test = "0.3" diff --git a/crates/utils/src/iter.rs b/crates/utils/src/iter.rs new file mode 100644 index 00000000..f878b0ea --- /dev/null +++ b/crates/utils/src/iter.rs @@ -0,0 +1,95 @@ +use wasm_bindgen::{JsValue, UnwrapThrowExt}; + +/// A wrapper around JS Iterator so it can be consumed from Rust. +/// +/// This type implements [`Iterator`] trait and will keep yielding [`JsValue`] +/// until the underlying [`js_sys::Iterator`] is exuasted. +/// +/// This type is called `UncheckedIter` because it does no checking for +/// the underlying type of the [`js_sys::Iterator`] and yields [`JsValue`]s. +/// +/// # Example +/// +/// ```rust +/// use gloo_utils::iter::UncheckedIter; +/// use wasm_bindgen::{JsCast, JsValue, UnwrapThrowExt}; +/// +/// let map = js_sys::Map::new(); +/// map.set(&JsValue::from("one"), &JsValue::from(1_f64)); +/// +/// let mut iter = UncheckedIter::from(map.entries()).map(|js_value| { +/// let array: js_sys::Array = js_value.unchecked_into(); +/// ( +/// array.get(0).as_string().unwrap_throw(), +/// array.get(1).as_f64().unwrap_throw(), +/// ) +/// }); +/// +/// assert_eq!(iter.next(), Some((String::from("one"), 1_f64))); +/// assert_eq!(iter.next(), None); +/// ``` +pub struct UncheckedIter(js_sys::Iterator); + +impl UncheckedIter { + /// Obtain the raw [`js_sys::Iterator`] + pub fn into_raw(self) -> js_sys::Iterator { + self.0 + } +} + +impl From for UncheckedIter { + fn from(iter: js_sys::Iterator) -> Self { + Self(iter) + } +} + +impl Iterator for UncheckedIter { + type Item = JsValue; + + fn next(&mut self) -> Option { + // we don't check for errors. Only use this type on things we know conform to the iterator + // interface. + let next = self.0.next().unwrap_throw(); + if next.done() { + None + } else { + Some(next.value()) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use wasm_bindgen_test::*; + + wasm_bindgen_test_configure!(run_in_browser); + + #[wasm_bindgen_test] + fn it_works() { + let map = js_sys::Map::new(); + macro_rules! map_set { + ($key:expr => $value:expr) => { + map.set(&JsValue::from($key), &JsValue::from($value)); + }; + } + + map_set!("one" => 1_f64); + map_set!("two" => 2_f64); + map_set!("three" => 3_f64); + + let mut iter = UncheckedIter::from(map.entries()).map(|js_value| { + let array = js_sys::Array::from(&js_value); + let array = array.to_vec(); + ( + array[0].as_string().expect_throw("not string"), + array[1].as_f64().expect_throw("not f64"), + ) + }); + + assert_eq!(iter.next(), Some((String::from("one"), 1_f64))); + assert_eq!(iter.next(), Some((String::from("two"), 2_f64))); + assert_eq!(iter.next(), Some((String::from("three"), 3_f64))); + assert_eq!(iter.next(), None); + } +} diff --git a/crates/utils/src/lib.rs b/crates/utils/src/lib.rs index ad69c636..f37ebb52 100644 --- a/crates/utils/src/lib.rs +++ b/crates/utils/src/lib.rs @@ -1,4 +1,5 @@ pub mod errors; +pub mod iter; use wasm_bindgen::UnwrapThrowExt; /// Convenience function to avoid repeating expect logic. From 6929cc91f9df2d2c19b2781cf1a5f9216a699c89 Mon Sep 17 00:00:00 2001 From: Muhammad Hamza Date: Wed, 27 Apr 2022 16:26:53 +0500 Subject: [PATCH 2/2] don't run the doc test --- crates/utils/src/iter.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/utils/src/iter.rs b/crates/utils/src/iter.rs index f878b0ea..c5eafeaa 100644 --- a/crates/utils/src/iter.rs +++ b/crates/utils/src/iter.rs @@ -14,6 +14,7 @@ use wasm_bindgen::{JsValue, UnwrapThrowExt}; /// use gloo_utils::iter::UncheckedIter; /// use wasm_bindgen::{JsCast, JsValue, UnwrapThrowExt}; /// +/// # fn no_run() { /// let map = js_sys::Map::new(); /// map.set(&JsValue::from("one"), &JsValue::from(1_f64)); /// @@ -27,6 +28,7 @@ use wasm_bindgen::{JsValue, UnwrapThrowExt}; /// /// assert_eq!(iter.next(), Some((String::from("one"), 1_f64))); /// assert_eq!(iter.next(), None); +/// # } /// ``` pub struct UncheckedIter(js_sys::Iterator);