use crate::timer::{TimerStruct, TimerTrait};
use wasm_bindgen::prelude::*;
#[cfg(feature = "console_error_panic_hook")]
pub fn set_panic_hook() {
console_error_panic_hook::set_once();
}
#[wasm_bindgen]
pub struct Timer {
inner: TimerStruct,
}
#[wasm_bindgen]
impl Timer {
#[wasm_bindgen(constructor)]
pub fn new(hours: u32, minutes: u32, seconds: u32) -> Result<Timer, JsValue> {
#[cfg(feature = "console_error_panic_hook")]
set_panic_hook();
match TimerStruct::new(hours, minutes, seconds) {
Ok(timer) => Ok(Timer { inner: timer }),
Err(e) => Err(JsValue::from_str(e)),
}
}
#[wasm_bindgen(getter)]
pub fn duration(&self) -> u32 {
self.inner.duration
}
#[wasm_bindgen(getter)]
pub fn hours(&self) -> u32 {
self.inner.hours
}
#[wasm_bindgen(getter)]
pub fn minutes(&self) -> u32 {
self.inner.minutes
}
#[wasm_bindgen(getter)]
pub fn seconds(&self) -> u32 {
self.inner.seconds
}
pub fn start(&self) -> js_sys::Promise {
let duration = self.inner.duration;
let promise = js_sys::Promise::new(&mut |resolve, _reject| {
let window = web_sys::window().expect("should have a window in this context");
let resolve_fn = resolve.clone();
fn create_timeout(
window: &web_sys::Window,
remaining: u32,
callback: &js_sys::Function,
resolve_fn: &js_sys::Function,
) {
if remaining == 0 {
let _ = resolve_fn.call0(&JsValue::NULL);
return;
}
let hours = remaining / 3600;
let minutes = (remaining % 3600) / 60;
let seconds = remaining % 60;
web_sys::console::log_1(&JsValue::from_str(&format!(
"Timer: {}:{}:{}",
hours, minutes, seconds
)));
let window_clone = window.clone();
let callback_clone = callback.clone();
let resolve_clone = resolve_fn.clone();
let next_remaining = remaining - 1;
let next_callback = Closure::once_into_js(move || {
create_timeout(
&window_clone,
next_remaining,
&callback_clone,
&resolve_clone,
);
});
let _ = window
.set_timeout_with_callback_and_timeout_and_arguments_0(
next_callback.as_ref().unchecked_ref(),
1000,
)
.expect("failed to set timeout");
}
let callback = js_sys::Function::new_no_args("");
create_timeout(&window, duration, &callback, &resolve_fn);
});
promise
}
}
#[wasm_bindgen]
pub struct Stopwatch {
current_time: u32,
running: bool,
interval_id: Option<i32>,
}
#[wasm_bindgen]
impl Stopwatch {
#[wasm_bindgen(constructor)]
pub fn new() -> Stopwatch {
#[cfg(feature = "console_error_panic_hook")]
set_panic_hook();
Stopwatch {
current_time: 0,
running: false,
interval_id: None,
}
}
#[wasm_bindgen(getter)]
pub fn current_time(&self) -> u32 {
self.current_time
}
#[wasm_bindgen(getter)]
pub fn is_running(&self) -> bool {
self.running
}
pub fn start(&mut self) -> Result<(), JsValue> {
if self.running {
return Ok(());
}
self.running = true;
let window = web_sys::window().expect("should have a window in this context");
let closure = {
let mut time = self.current_time;
Closure::wrap(Box::new(move || {
time += 1;
let hours = time / 3600;
let minutes = (time % 3600) / 60;
let seconds = time % 60;
web_sys::console::log_1(&JsValue::from_str(&format!(
"Stopwatch: {}:{}:{}",
hours, minutes, seconds
)));
}) as Box<dyn FnMut()>)
};
let interval_id = window
.set_interval_with_callback_and_timeout_and_arguments_0(
closure.as_ref().unchecked_ref(),
1000,
)
.expect("failed to set interval");
self.interval_id = Some(interval_id);
closure.forget();
Ok(())
}
pub fn stop(&mut self) -> u32 {
if !self.running {
return self.current_time;
}
self.running = false;
if let Some(interval_id) = self.interval_id.take() {
let window = web_sys::window().expect("should have a window in this context");
window.clear_interval_with_handle(interval_id);
}
self.current_time
}
pub fn reset(&mut self) {
self.stop();
self.current_time = 0;
}
}