1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
//! Time related things
//!
//! [std::time] is not working on web so use this instead
#![warn(missing_docs)]

/// A measurement of a monotonically nondecreasing clock.
///
/// Alternative of [std::time::Instant]
#[derive(Copy, Clone)]
pub struct Instant {
    #[cfg(target_arch = "wasm32")]
    value: f64,
    #[cfg(not(target_arch = "wasm32"))]
    inner: std::time::Instant,
}

impl Instant {
    /// Returns an instant corresponding to "now".
    pub fn now() -> Self {
        Self {
            #[cfg(target_arch = "wasm32")]
            value: {
                thread_local! {
                    static PERFORMANCE: web_sys::Performance = web_sys::window()
                        .expect("no window")
                        .performance()
                        .expect("no performance");
                }
                PERFORMANCE.with(|performance| performance.now() / 1000.0)
            },
            #[cfg(not(target_arch = "wasm32"))]
            inner: std::time::Instant::now(),
        }
    }

    /// Returns the amount of time elapsed from another instant to this one
    pub fn duration_since(&self, earlier: Self) -> Duration {
        #[cfg(target_arch = "wasm32")]
        return Duration::from_secs_f64(self.value - earlier.value);
        #[cfg(not(target_arch = "wasm32"))]
        return Duration::from_secs_f64(self.inner.duration_since(earlier.inner).as_secs_f64());
    }

    /// Returns the amount of time elapsed since this instant
    pub fn elapsed(&self) -> Duration {
        Self::now().duration_since(*self)
    }
}

/// Represents a span of time.
///
/// Alternative of [std::time::Duration]
#[derive(Copy, Clone)]
pub struct Duration {
    secs: f64,
}

impl std::ops::Add for Duration {
    type Output = Self;
    fn add(self, rhs: Self) -> Self {
        Self {
            secs: self.secs + rhs.secs,
        }
    }
}

impl std::fmt::Debug for Duration {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        std::time::Duration::from(*self).fmt(f)
    }
}

impl Duration {
    /// Creates a new Duration from the specified number of seconds represented as f64.
    pub fn from_secs_f64(secs: f64) -> Self {
        Self { secs }
    }

    /// Returns the number of seconds contained by this Duration as f64
    pub fn as_secs_f64(&self) -> f64 {
        self.secs
    }
}

impl From<Duration> for std::time::Duration {
    fn from(value: Duration) -> Self {
        std::time::Duration::from_secs_f64(value.as_secs_f64())
    }
}

impl From<std::time::Duration> for Duration {
    fn from(value: std::time::Duration) -> Self {
        Duration::from_secs_f64(value.as_secs_f64())
    }
}

/// Timer can be used to track time since some instant
pub struct Timer {
    start: Instant,
}

impl Timer {
    #[allow(clippy::new_without_default)]
    /// Constructs a new timer.
    pub fn new() -> Self {
        Self {
            start: Instant::now(),
        }
    }

    /// Reset timer
    pub fn reset(&mut self) {
        self.start = Instant::now();
    }

    /// Get duration elapsed since last reset.
    pub fn elapsed(&self) -> Duration {
        self.start.elapsed()
    }

    /// Reset, and get time elapsed since last reset.
    pub fn tick(&mut self) -> Duration {
        let now = Instant::now();
        let duration = now.duration_since(self.start);
        self.start = now;
        duration
    }
}

/// Sleep for specified duration
pub async fn sleep(duration: Duration) {
    #[cfg(target_arch = "wasm32")]
    {
        let promise = js_sys::Promise::new(&mut |resolve, _reject| {
            web_sys::window()
                .unwrap()
                .set_timeout_with_callback_and_timeout_and_arguments_0(
                    &resolve,
                    (duration.as_secs_f64() * 1000.0).round() as _,
                )
                .unwrap();
        });
        let future = wasm_bindgen_futures::JsFuture::from(promise);
        future.await.unwrap();
    }
    #[cfg(not(target_arch = "wasm32"))]
    {
        async_std::task::sleep(duration.into()).await;
    }
}

#[test]
fn test() {
    let mut timer = Timer::new();
    timer.elapsed();
    for _ in 0..100 {
        timer.tick();
    }
}