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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use std::collections::HashMap;
use std::fmt;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use Value;
use Key;
lazy_static! {
pub static ref CITRUSLEAF_EPOCH: SystemTime = UNIX_EPOCH + Duration::new(1262304000, 0);
}
#[derive(Debug)]
pub struct Record {
pub key: Option<Key>,
pub bins: HashMap<String, Value>,
pub generation: u32,
expiration: u32,
}
impl Record {
#[doc(hidden)]
pub fn new(key: Option<Key>,
bins: HashMap<String, Value>,
generation: u32,
expiration: u32)
-> Self {
Record {
key: key,
bins: bins,
generation: generation,
expiration: expiration,
}
}
pub fn time_to_live(&self) -> Option<Duration> {
match self.expiration {
0 => None,
secs_since_epoch @ _ => {
let expiration = *CITRUSLEAF_EPOCH + Duration::new(secs_since_epoch as u64, 0);
match expiration.duration_since(SystemTime::now()) {
Ok(d) => Some(d),
_ => Some(Duration::new(1u64, 0))
}
}
}
}
}
impl fmt::Display for Record {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
try!(write!(f, "key: {:?}", self.key));
try!(write!(f, ", bins: {{"));
for (i, (k, v)) in self.bins.iter().enumerate() {
if i > 0 {
try!(write!(f, ", "))
}
try!(write!(f, "{}: {}", k, v));
}
try!(write!(f, "}}, generation: {}", self.generation));
try!(write!(f, ", ttl: "));
match self.time_to_live() {
None => "none".fmt(f),
Some(duration) => duration.as_secs().fmt(f),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::{Duration, SystemTime};
use std::collections::HashMap;
#[test]
fn ttl_expiration_future() {
let expiration = SystemTime::now() + Duration::new(1000, 0);
let secs_since_epoch = expiration.duration_since(*CITRUSLEAF_EPOCH).unwrap().as_secs();
let record = Record::new(None, HashMap::new(), 0, secs_since_epoch as u32);
let ttl = record.time_to_live();
assert!(ttl.is_some());
assert!(1000 - ttl.unwrap().as_secs() <= 1);
}
#[test]
fn ttl_expiration_past() {
let record = Record::new(None, HashMap::new(), 0, 0x0d00d21c);
assert_eq!(record.time_to_live(), Some(Duration::new(1u64, 0)));
}
#[test]
fn ttl_never_expires() {
let record = Record::new(None, HashMap::new(), 0, 0);
assert_eq!(record.time_to_live(), None);
}
}