1use futures::task::{Context, Poll};
8use std::cell::RefCell;
9use std::future::Future;
10use std::pin::Pin;
11use std::rc::Rc;
12use wasm_bindgen::prelude::*;
13use wasm_bindgen::JsCast;
14use web_sys::HtmlImageElement;
15
16pub struct ImageFuture {
34 image: Option<HtmlImageElement>,
35 load_failed: Rc<RefCell<bool>>,
36}
37
38impl ImageFuture {
39 #[must_use]
42 pub fn new(src: &str, srcset: Option<&str>) -> Self {
43 let image = HtmlImageElement::new().unwrap();
44 image.set_src(src);
45 if let Some(srcset) = srcset {
46 image.set_srcset(srcset);
47 }
48 Self {
49 image: Some(image),
50 load_failed: Rc::new(RefCell::new(false)),
51 }
52 }
53}
54
55impl Future for ImageFuture {
56 type Output = Result<HtmlImageElement, ()>;
57
58 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
59 match &self.image {
60 Some(image) => {
61 return if image.complete() {
62 let image = self.image.take().unwrap();
63 let failed = *self.load_failed.borrow();
64
65 if failed {
66 Poll::Ready(Err(()))
67 } else {
68 Poll::Ready(Ok(image))
69 }
70 } else {
71 let waker = cx.waker().clone();
72 let on_load_closure = Closure::wrap(Box::new(move || {
73 waker.wake_by_ref();
74 }) as Box<dyn FnMut()>);
75 image.set_onload(Some(on_load_closure.as_ref().unchecked_ref()));
76 on_load_closure.forget();
77
78 let waker = cx.waker().clone();
79 let failed_flag = self.load_failed.clone();
80 let on_error_closure = Closure::wrap(Box::new(move || {
81 *failed_flag.borrow_mut() = true;
82 waker.wake_by_ref();
83 })
84 as Box<dyn FnMut()>);
85 image.set_onerror(Some(on_error_closure.as_ref().unchecked_ref()));
86 on_error_closure.forget();
87
88 Poll::Pending
89 };
90 }
91 _ => Poll::Ready(Err(())),
92 }
93 }
94}