architect_api/utils/
half_open_range.rs

1use chrono::{DateTime, Utc};
2#[cfg(feature = "netidx")]
3use derive::FromValue;
4#[cfg(feature = "netidx")]
5use netidx_derive::Pack;
6use schemars::JsonSchema;
7use serde_derive::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
10#[cfg_attr(feature = "netidx", derive(Pack, FromValue))]
11pub struct HalfOpenRange<T: 'static> {
12    pub from_inclusive: T,
13    pub to_exclusive: T,
14}
15
16impl<T> HalfOpenRange<T> {
17    pub fn new(from_inclusive: T, to_exclusive: T) -> Self {
18        Self { from_inclusive, to_exclusive }
19    }
20}
21
22impl<T: Copy> HalfOpenRange<T> {
23    pub fn infinitesimal(at: T) -> Self {
24        Self { from_inclusive: at, to_exclusive: at }
25    }
26}
27
28impl<T: Ord> HalfOpenRange<T> {
29    pub fn contains(&self, x: T) -> bool {
30        self.from_inclusive <= x && x < self.to_exclusive
31    }
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
35#[cfg_attr(feature = "netidx", derive(Pack, FromValue))]
36pub enum ClampSign {
37    Forwards,  // fix [from_inclusive], clamp by reducing [to_exclusive]
38    Backwards, // fix [to_exclusive], clamp by increasing [from_inclusive]
39}
40
41impl HalfOpenRange<DateTime<Utc>> {
42    pub fn clamp(&self, max_width: chrono::Duration, sign: ClampSign) -> Self {
43        match sign {
44            ClampSign::Forwards => self.clamp_to(max_width),
45            ClampSign::Backwards => self.clamp_from(max_width),
46        }
47    }
48
49    fn clamp_from(&self, max_width: chrono::Duration) -> Self {
50        Self {
51            from_inclusive: std::cmp::max(
52                self.to_exclusive - max_width,
53                self.from_inclusive,
54            ),
55            ..*self
56        }
57    }
58
59    fn clamp_to(&self, max_width: chrono::Duration) -> Self {
60        Self {
61            to_exclusive: std::cmp::min(
62                self.from_inclusive + max_width,
63                self.to_exclusive,
64            ),
65            ..*self
66        }
67    }
68}
69
70impl HalfOpenRange<Option<DateTime<Utc>>> {
71    pub fn clamp(&self, max_width: chrono::Duration, sign: ClampSign) -> Self {
72        match sign {
73            ClampSign::Forwards => self.clamp_to(max_width),
74            ClampSign::Backwards => self.clamp_from(max_width),
75        }
76    }
77
78    fn clamp_from(&self, max_width: chrono::Duration) -> Self {
79        match (self.from_inclusive, self.to_exclusive) {
80            (Some(l), Some(r)) => {
81                Self { from_inclusive: Some(std::cmp::max(r - max_width, l)), ..*self }
82            }
83            _ => *self,
84        }
85    }
86
87    fn clamp_to(&self, max_width: chrono::Duration) -> Self {
88        match (self.from_inclusive, self.to_exclusive) {
89            (Some(l), Some(r)) => {
90                Self { to_exclusive: Some(std::cmp::min(l + max_width, r)), ..*self }
91            }
92            _ => *self,
93        }
94    }
95}