Documentation
use js_sys::{IntoIter, try_iter};
use std::marker::PhantomData;
use wasm_bindgen::prelude::*;

pub struct JsCastIter<T: JsCast> {
  iter: Option<IntoIter>,
  phantom: PhantomData<T>,
}

impl<T: JsCast> JsCastIter<T> {
  pub fn new(value: &JsValue) -> Self {
    Self {
      iter: try_iter(value).unwrap_or_default(),
      phantom: PhantomData,
    }
  }

  pub fn empty() -> Self {
    Self { iter: None, phantom: PhantomData }
  }
}

impl<T: JsCast> Iterator for JsCastIter<T> {
  type Item = T;

  fn next(&mut self) -> Option<Self::Item> {
    if let Some(iter) = &mut self.iter {
      for value in iter.by_ref() {
        if let Ok(value) = value.and_then(JsCast::dyn_into) {
          return Some(value);
        }
      }
    }

    None
  }
}

pub fn cast_iter<T: JsCast>(value: &JsValue) -> JsCastIter<T> {
  JsCastIter::new(value)
}