iop_morpheus_node/
time_series.rs

1use super::*;
2
3#[derive(Debug, Clone)]
4struct TimeSeriesPoint<T> {
5    height: BlockHeight,
6    value: T,
7}
8
9#[derive(Debug, Clone)]
10pub struct TimeSeries<T: fmt::Display + PartialEq<T>> {
11    initial_value: T,
12    points: Vec<TimeSeriesPoint<T>>,
13}
14
15impl<T: fmt::Display + PartialEq<T>> TimeSeries<T> {
16    pub fn new(initial_value: T) -> Self {
17        Self { initial_value, points: Default::default() }
18    }
19
20    pub fn iter(&self) -> impl Iterator<Item = (Option<BlockHeight>, &T)> {
21        std::iter::once((None, &self.initial_value))
22            .chain(self.points.iter().map(|p| (Some(p.height), &p.value)))
23    }
24
25    pub fn get(&self, height: BlockHeight) -> &T {
26        self.points
27            .iter()
28            .rev()
29            .find(|p| p.height <= height)
30            .map(|p| &p.value)
31            .unwrap_or_else(|| &self.initial_value)
32    }
33
34    pub fn is_empty(&self) -> bool {
35        self.points.is_empty()
36    }
37
38    pub fn latest_value(&self) -> &T {
39        self.points.last().map(|p| &p.value).unwrap_or_else(|| &self.initial_value)
40    }
41
42    pub fn latest_height(&self) -> Option<BlockHeight> {
43        self.points.last().map(|p| p.height)
44    }
45
46    pub fn apply<D: fmt::Display>(
47        &mut self, height: BlockHeight, value: T, context: impl FnOnce() -> D,
48    ) -> Result<()> {
49        if let Some(last) = self.points.last() {
50            ensure!(last.height < height, "{} was already set at height {}", context(), height);
51        }
52        ensure!(
53            self.latest_value() != &value,
54            "{} was already set to {} at height {}",
55            context(),
56            value,
57            height
58        );
59        self.points.push(TimeSeriesPoint { height, value });
60        Ok(())
61    }
62
63    pub fn revert<D: fmt::Display>(
64        &mut self, height: BlockHeight, value: T, context: impl FnOnce() -> D,
65    ) -> Result<()> {
66        if let Some(last) = self.points.pop() {
67            ensure!(
68                last.height == height,
69                "{} was set at height {}, cannot unset at height {}",
70                context(),
71                last.height,
72                height
73            );
74            ensure!(
75                last.value == value,
76                "{} was set to {} at height {}, cannot unset it from {}",
77                context(),
78                last.value,
79                last.height,
80                value
81            );
82            Ok(())
83        } else {
84            bail!("{} has nothing to unset", context());
85        }
86    }
87}