cw_utils/
scheduled.rs

1use crate::Duration;
2use cosmwasm_schema::cw_serde;
3use cosmwasm_std::{BlockInfo, StdError, StdResult, Timestamp};
4use std::cmp::Ordering;
5use std::fmt;
6use std::ops::Add;
7
8/// Scheduled represents a point in time when an event happens.
9/// It can compare with a BlockInfo and will return is_triggered() == true
10/// once the condition is hit (and for every block in the future)
11#[cw_serde]
12#[derive(Copy)]
13pub enum Scheduled {
14    /// AtHeight will schedule when `env.block.height` >= height
15    AtHeight(u64),
16    /// AtTime will schedule when `env.block.time` >= time
17    AtTime(Timestamp),
18}
19
20impl fmt::Display for Scheduled {
21    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
22        match self {
23            Scheduled::AtHeight(height) => write!(f, "scheduled height: {}", height),
24            Scheduled::AtTime(time) => write!(f, "scheduled time: {}", time),
25        }
26    }
27}
28
29impl Scheduled {
30    #[allow(dead_code)]
31    pub fn is_triggered(&self, block: &BlockInfo) -> bool {
32        match self {
33            Scheduled::AtHeight(height) => block.height >= *height,
34            Scheduled::AtTime(time) => block.time >= *time,
35        }
36    }
37}
38
39impl Add<Duration> for Scheduled {
40    type Output = StdResult<Scheduled>;
41
42    fn add(self, duration: Duration) -> StdResult<Scheduled> {
43        match (self, duration) {
44            (Scheduled::AtTime(t), Duration::Time(delta)) => {
45                Ok(Scheduled::AtTime(t.plus_seconds(delta)))
46            }
47            (Scheduled::AtHeight(h), Duration::Height(delta)) => Ok(Scheduled::AtHeight(h + delta)),
48            _ => Err(StdError::msg("Cannot add height and time")),
49        }
50    }
51}
52
53impl PartialOrd for Scheduled {
54    fn partial_cmp(&self, other: &Scheduled) -> Option<Ordering> {
55        match (self, other) {
56            // compare if both height or both time
57            (Scheduled::AtHeight(h1), Scheduled::AtHeight(h2)) => Some(h1.cmp(h2)),
58            (Scheduled::AtTime(t1), Scheduled::AtTime(t2)) => Some(t1.cmp(t2)),
59            _ => None,
60        }
61    }
62}
63
64#[cfg(test)]
65mod test {
66    use super::*;
67
68    #[test]
69    fn compare_schedules() {
70        // matching pairs
71        assert!(Scheduled::AtHeight(5) < Scheduled::AtHeight(10));
72        assert!(Scheduled::AtHeight(8) > Scheduled::AtHeight(7));
73        assert!(
74            Scheduled::AtTime(Timestamp::from_seconds(555))
75                < Scheduled::AtTime(Timestamp::from_seconds(777))
76        );
77        assert!(
78            Scheduled::AtTime(Timestamp::from_seconds(86))
79                < Scheduled::AtTime(Timestamp::from_seconds(100))
80        );
81
82        // what happens for the uncomparables?? all compares are false
83        assert_eq!(
84            None,
85            Scheduled::AtTime(Timestamp::from_seconds(1000)).partial_cmp(&Scheduled::AtHeight(230))
86        );
87        assert_eq!(
88            Scheduled::AtTime(Timestamp::from_seconds(1000)).partial_cmp(&Scheduled::AtHeight(230)),
89            None
90        );
91        assert_eq!(
92            Scheduled::AtTime(Timestamp::from_seconds(1000)).partial_cmp(&Scheduled::AtHeight(230)),
93            None
94        );
95        assert!(!(Scheduled::AtTime(Timestamp::from_seconds(1000)) == Scheduled::AtHeight(230)));
96    }
97
98    #[test]
99    fn schedule_addition() {
100        // height
101        let end = Scheduled::AtHeight(12345) + Duration::Height(400);
102        assert_eq!(end.unwrap(), Scheduled::AtHeight(12745));
103
104        // time
105        let end = Scheduled::AtTime(Timestamp::from_seconds(55544433)) + Duration::Time(40300);
106        assert_eq!(
107            end.unwrap(),
108            Scheduled::AtTime(Timestamp::from_seconds(55584733))
109        );
110
111        // mismatched
112        let end = Scheduled::AtHeight(12345) + Duration::Time(1500);
113        end.unwrap_err();
114    }
115}