gloo_utils/
iter.rs

1use wasm_bindgen::{JsValue, UnwrapThrowExt};
2
3/// A wrapper around JS Iterator so it can be consumed from Rust.
4///
5/// This type implements [`Iterator`] trait and will keep yielding [`JsValue`]
6/// until the underlying [`js_sys::Iterator`] is exuasted.
7///
8/// This type is called `UncheckedIter` because it does no checking for
9/// the underlying type of the [`js_sys::Iterator`] and yields [`JsValue`]s.
10///
11/// # Example
12///
13/// ```rust
14/// use gloo_utils::iter::UncheckedIter;
15/// use wasm_bindgen::{JsCast, JsValue, UnwrapThrowExt};
16///
17/// # fn no_run() {
18/// let map = js_sys::Map::new();
19/// map.set(&JsValue::from("one"), &JsValue::from(1_f64));
20///
21/// let mut iter = UncheckedIter::from(map.entries()).map(|js_value| {
22///     let array: js_sys::Array = js_value.unchecked_into();
23///     (
24///         array.get(0).as_string().unwrap_throw(),
25///         array.get(1).as_f64().unwrap_throw(),
26///     )
27/// });
28///
29/// assert_eq!(iter.next(), Some((String::from("one"), 1_f64)));
30/// assert_eq!(iter.next(), None);
31/// # }
32/// ```
33pub struct UncheckedIter(js_sys::Iterator);
34
35impl UncheckedIter {
36    /// Obtain the raw [`js_sys::Iterator`]
37    pub fn into_raw(self) -> js_sys::Iterator {
38        self.0
39    }
40}
41
42impl From<js_sys::Iterator> for UncheckedIter {
43    fn from(iter: js_sys::Iterator) -> Self {
44        Self(iter)
45    }
46}
47
48impl Iterator for UncheckedIter {
49    type Item = JsValue;
50
51    fn next(&mut self) -> Option<Self::Item> {
52        // we don't check for errors. Only use this type on things we know conform to the iterator
53        // interface.
54        let next = self.0.next().unwrap_throw();
55        if next.done() {
56            None
57        } else {
58            Some(next.value())
59        }
60    }
61}
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66    use wasm_bindgen_test::*;
67
68    wasm_bindgen_test_configure!(run_in_browser);
69
70    #[wasm_bindgen_test]
71    fn it_works() {
72        let map = js_sys::Map::new();
73        macro_rules! map_set {
74            ($key:expr => $value:expr) => {
75                map.set(&JsValue::from($key), &JsValue::from($value));
76            };
77        }
78
79        map_set!("one" => 1_f64);
80        map_set!("two" => 2_f64);
81        map_set!("three" => 3_f64);
82
83        let mut iter = UncheckedIter::from(map.entries()).map(|js_value| {
84            let array = js_sys::Array::from(&js_value);
85            let array = array.to_vec();
86            (
87                array[0].as_string().expect_throw("not string"),
88                array[1].as_f64().expect_throw("not f64"),
89            )
90        });
91
92        assert_eq!(iter.next(), Some((String::from("one"), 1_f64)));
93        assert_eq!(iter.next(), Some((String::from("two"), 2_f64)));
94        assert_eq!(iter.next(), Some((String::from("three"), 3_f64)));
95        assert_eq!(iter.next(), None);
96    }
97}