1use crate::sql::statements::info::InfoStructure;
2use crate::sql::Value;
3use revision::revisioned;
4use revision::Error;
5use serde::{Deserialize, Serialize};
6use std::fmt::{self, Display};
7use std::ops::{Add, Sub};
8use std::time::Duration;
9use uuid::Uuid;
10
11#[revisioned(revision = 2)]
12#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
13#[non_exhaustive]
14pub struct Node {
15 #[revision(start = 2, default_fn = "default_id")]
16 pub id: Uuid,
17 #[revision(start = 2, default_fn = "default_hb")]
18 pub hb: Timestamp,
19 #[revision(start = 2, default_fn = "default_gc")]
20 pub gc: bool,
21 #[revision(end = 2, convert_fn = "convert_name")]
22 pub name: String,
23 #[revision(end = 2, convert_fn = "convert_heartbeat")]
24 pub heartbeat: Timestamp,
25}
26
27impl Node {
28 pub fn new(id: Uuid, hb: Timestamp, gc: bool) -> Self {
30 Self {
31 id,
32 hb,
33 gc,
34 ..Default::default()
35 }
36 }
37 pub fn archive(&self) -> Self {
39 Node {
40 gc: true,
41 ..self.to_owned()
42 }
43 }
44 pub fn id(&self) -> Uuid {
46 self.id
47 }
48 pub fn is_active(&self) -> bool {
50 !self.gc
51 }
52 pub fn is_archived(&self) -> bool {
54 self.gc
55 }
56 pub fn archived(&self) -> Option<Uuid> {
58 match self.is_archived() {
59 true => Some(self.id),
60 false => None,
61 }
62 }
63 fn default_id(_revision: u16) -> Result<Uuid, Error> {
65 Ok(Uuid::default())
66 }
67 fn default_hb(_revision: u16) -> Result<Timestamp, Error> {
69 Ok(Timestamp::default())
70 }
71 fn default_gc(_revision: u16) -> Result<bool, Error> {
73 Ok(true)
74 }
75 fn convert_name(&mut self, _revision: u16, value: String) -> Result<(), Error> {
77 self.id = Uuid::parse_str(&value).unwrap();
78 Ok(())
79 }
80 fn convert_heartbeat(&mut self, _revision: u16, value: Timestamp) -> Result<(), Error> {
82 self.hb = value;
83 Ok(())
84 }
85}
86
87impl Display for Node {
88 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
89 write!(f, "NODE {} SEEN {}", self.id, self.hb)?;
90 match self.gc {
91 true => write!(f, " ARCHIVED")?,
92 false => write!(f, " ACTIVE")?,
93 };
94 Ok(())
95 }
96}
97
98impl InfoStructure for Node {
99 fn structure(self) -> Value {
100 Value::from(map! {
101 "id".to_string() => Value::from(self.id),
102 "seen".to_string() => self.hb.structure(),
103 "active".to_string() => Value::from(!self.gc),
104 })
105 }
106}
107
108#[revisioned(revision = 1)]
112#[derive(Clone, Copy, Default, Debug, Eq, PartialEq, PartialOrd, Deserialize, Serialize, Hash)]
113#[non_exhaustive]
114pub struct Timestamp {
115 pub value: u64,
116}
117
118impl From<u64> for Timestamp {
119 fn from(value: u64) -> Self {
120 Timestamp {
121 value,
122 }
123 }
124}
125
126impl Add<Duration> for Timestamp {
127 type Output = Timestamp;
128 fn add(self, rhs: Duration) -> Self::Output {
129 Timestamp {
130 value: self.value.wrapping_add(rhs.as_millis() as u64),
131 }
132 }
133}
134
135impl Sub<Duration> for Timestamp {
136 type Output = Timestamp;
137 fn sub(self, rhs: Duration) -> Self::Output {
138 Timestamp {
139 value: self.value.wrapping_sub(rhs.as_millis() as u64),
140 }
141 }
142}
143
144impl Display for Timestamp {
145 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
146 write!(f, "{}", self.value)
147 }
148}
149
150impl InfoStructure for Timestamp {
151 fn structure(self) -> Value {
152 self.value.into()
153 }
154}
155
156#[cfg(test)]
157mod test {
158 use crate::dbs::node::Timestamp;
159 use chrono::prelude::Utc;
160 use chrono::TimeZone;
161 use std::time::Duration;
162
163 #[test]
164 fn timestamps_can_be_added_duration() {
165 let t = Utc.with_ymd_and_hms(2000, 1, 1, 12, 30, 0).unwrap();
166 let ts = Timestamp {
167 value: t.timestamp_millis() as u64,
168 };
169
170 let hour = Duration::from_secs(60 * 60);
171 let ts = ts + hour;
172 let ts = ts + hour;
173 let ts = ts + hour;
174
175 let end_time = Utc.timestamp_millis_opt(ts.value as i64).unwrap();
176 let expected_end_time = Utc.with_ymd_and_hms(2000, 1, 1, 15, 30, 0).unwrap();
177 assert_eq!(end_time, expected_end_time);
178 }
179
180 #[test]
181 fn timestamps_can_be_subtracted_duration() {
182 let t = Utc.with_ymd_and_hms(2000, 1, 1, 12, 30, 0).unwrap();
183 let ts = Timestamp {
184 value: t.timestamp_millis() as u64,
185 };
186
187 let hour = Duration::from_secs(60 * 60);
188 let ts = ts - hour;
189 let ts = ts - hour;
190 let ts = ts - hour;
191
192 let end_time = Utc.timestamp_millis_opt(ts.value as i64).unwrap();
193 let expected_end_time = Utc.with_ymd_and_hms(2000, 1, 1, 9, 30, 0).unwrap();
194 assert_eq!(end_time, expected_end_time);
195 }
196}