aboutsummaryrefslogtreecommitdiff
path: root/src/history.rs
blob: a579a8ceae1fb5e5a9796747bd5fe138601197c8 (plain)
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
// god is dead and we have killed him and consequently std::time::Instant doesn't work on wasm

use chrono::prelude::*;
use wasm_bindgen::prelude::*;

struct Reading {
    time: DateTime<Local>,
    queue_size: u32,
}

#[derive(Default)]
pub struct History {
    data: Vec<Reading>,
}

pub enum NoETA {
    Pending,
    Fuck,
}

// Duration doesn't impl Mul<f32> so we have to do percents
const RECENCY_PERCENT: i32 = 20;

impl History {
    pub fn record(&mut self, queue_size: u32) {
        let redundant = match self.data.last() {
            Some(last) => queue_size == last.queue_size,
            None => false,
        };
        if !redundant {
            self.data.push(Reading {
                time: Local::now(),
                queue_size,
            });
        }
    }

    pub fn completion_time(&self) -> Result<js_sys::Date, NoETA> {
        let weighted_average_step_time = self
            .data
            .iter()
            .zip(self.data.iter().skip(1))
            .map(|(one, two)| {
                // queue velocity should ideally be measured in distance per time,
                // but the units wind up fucky (reciprocal duration not implemented)
                // so instead we use time per distance
                if one.time > two.time {
                    return Err(NoETA::Fuck);
                }
                let delta_time = two.time - one.time;
                let delta_queue = match one.queue_size.checked_sub(two.queue_size) {
                    Some(d) => d,
                    None => return Err(NoETA::Fuck),
                };
                Ok(delta_time / delta_queue as i32)
            })
            .reduce(|previous, current| match (previous, current) {
                (Ok(previous), Ok(current)) => {
                    let previous_term = previous * (100 - RECENCY_PERCENT) / 100;
                    let current_term = current * RECENCY_PERCENT / 100;
                    Ok(previous_term + current_term)
                }
                (Err(e), _) => Err(e),
                (_, Err(e)) => Err(e),
            })
            .ok_or(NoETA::Pending)??;
        let &Reading {
            time: last_time,
            queue_size: last_size,
        } = self.data.last().ok_or(NoETA::Pending)?;
        let remaining_duration = weighted_average_step_time * last_size as i32;
        let completion_time = last_time + remaining_duration;
        let completion_time_gmt = completion_time.timestamp_millis();
        Ok(js_sys::Date::new(&JsValue::from(
            completion_time_gmt as f64,
        )))
    }
}