1#![doc = include_str!("../README.md")]
2
3#[derive(Debug, Copy, Clone)]
9#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
10pub struct TimedOption<T, Ttl> {
11 value: Option<T>,
12 ttl: Ttl,
13}
14
15impl<T, B> TimedOption<T, B>
16where
17 B: TtlBackend,
18{
19 #[inline]
21 pub fn new(value: T, ttl: B::Duration) -> Self {
22 TimedOption {
23 value: Some(value),
24 ttl: B::now().add(ttl),
25 }
26 }
27
28 #[inline]
30 pub fn empty() -> Self {
31 TimedOption {
32 value: None,
33 ttl: B::expired(),
34 }
35 }
36
37 #[inline]
43 pub fn into_option(self) -> Option<T> {
44 match self.ttl.is_valid() {
45 true => self.value,
46 false => None,
47 }
48 }
49
50 #[inline]
52 pub fn as_option(&self) -> Option<&T> {
53 self.as_ref().into_option()
54 }
55
56 #[inline]
58 pub fn into_timed_value(self) -> TimedValue<T> {
59 match (self.value, self.ttl.is_valid()) {
60 (Some(value), true) => TimedValue::Valid(value),
61 (Some(value), false) => TimedValue::Expired(value),
62 (None, _) => TimedValue::None,
63 }
64 }
65
66 #[inline]
68 pub fn as_timed_value(&self) -> TimedValue<&T> {
69 self.as_ref().into_timed_value()
70 }
71
72 #[inline]
74 pub fn as_ref(&self) -> TimedOption<&T, B> {
75 TimedOption {
76 value: self.value.as_ref(),
77 ttl: self.ttl.clone(),
78 }
79 }
80
81 #[inline]
87 pub fn expire(&mut self) {
88 self.ttl = B::expired();
89 }
90
91 #[inline]
93 pub fn clear(&mut self) {
94 self.value = None;
95 }
96
97 #[inline]
100 pub fn take(&mut self) -> Option<T> {
101 let value = self.value.take();
102 match self.ttl.is_valid() {
103 true => value,
104 false => None,
105 }
106 }
107
108 #[inline]
111 pub fn take_timed_value(&mut self) -> TimedValue<T> {
112 match (self.value.take(), self.ttl.is_valid()) {
113 (Some(value), true) => TimedValue::Valid(value),
114 (Some(value), false) => TimedValue::Expired(value),
115 (None, _) => TimedValue::None,
116 }
117 }
118
119 #[inline]
125 pub fn is_some(&self) -> bool {
126 self.value.is_some() & self.ttl.is_valid()
127 }
128
129 #[inline]
131 pub fn is_none(&self) -> bool {
132 self.value.is_none() | self.ttl.is_expired()
133 }
134}
135
136#[derive(Debug, Copy, Clone, PartialEq, Eq)]
144#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
145pub enum TimedValue<T> {
146 Valid(T),
148 Expired(T),
150 None,
152}
153
154impl<T> TimedValue<T> {
155 #[inline]
157 pub const fn is_valid(&self) -> bool {
158 match self {
159 TimedValue::Valid(_) => true,
160 TimedValue::Expired(_) => false,
161 TimedValue::None => false,
162 }
163 }
164
165 #[inline]
167 pub const fn is_expired(&self) -> bool {
168 match self {
169 TimedValue::Valid(_) => false,
170 TimedValue::Expired(_) => true,
171 TimedValue::None => false,
172 }
173 }
174
175 #[inline]
177 pub const fn is_none(&self) -> bool {
178 match self {
179 TimedValue::Valid(_) => false,
180 TimedValue::Expired(_) => false,
181 TimedValue::None => true,
182 }
183 }
184
185 #[inline]
187 pub const fn has_value(&self) -> bool {
188 match self {
189 TimedValue::Valid(_) => true,
190 TimedValue::Expired(_) => true,
191 TimedValue::None => false,
192 }
193 }
194
195 #[inline]
197 pub const fn as_ref(&self) -> TimedValue<&T> {
198 match *self {
199 TimedValue::Valid(ref inner) => TimedValue::Valid(inner),
200 TimedValue::Expired(ref inner) => TimedValue::Expired(inner),
201 TimedValue::None => TimedValue::None,
202 }
203 }
204}
205
206impl<T, B> From<TimedOption<T, B>> for Option<T>
211where
212 B: TtlBackend,
213{
214 #[inline]
215 fn from(value: TimedOption<T, B>) -> Self {
216 value.into_option()
217 }
218}
219
220impl<T, B> From<TimedOption<T, B>> for TimedValue<T>
221where
222 B: TtlBackend,
223{
224 #[inline]
225 fn from(value: TimedOption<T, B>) -> Self {
226 value.into_timed_value()
227 }
228}
229
230pub trait TtlBackend: Clone {
235 type Duration;
236
237 fn now() -> Self;
238 fn expired() -> Self;
239 fn add(self, dt: Self::Duration) -> Self;
240 fn is_valid(&self) -> bool;
241 fn is_expired(&self) -> bool;
242}
243
244impl TtlBackend for std::time::Instant {
245 type Duration = std::time::Duration;
246
247 #[inline]
248 fn now() -> Self {
249 std::time::Instant::now()
250 }
251
252 #[inline]
253 fn expired() -> Self {
254 std::time::Instant::now()
255 }
256
257 #[inline]
258 fn add(self, dt: Self::Duration) -> Self {
259 self + dt
260 }
261
262 #[inline]
263 fn is_valid(&self) -> bool {
264 *self > std::time::Instant::now()
265 }
266
267 #[inline]
268 fn is_expired(&self) -> bool {
269 *self <= std::time::Instant::now()
270 }
271}
272
273#[cfg(feature = "chrono")]
274impl TtlBackend for chrono::DateTime<chrono::Utc> {
275 type Duration = chrono::Duration;
276
277 #[inline]
278 fn now() -> Self {
279 chrono::Utc::now()
280 }
281
282 #[inline]
283 fn expired() -> Self {
284 chrono::Utc::now()
285 }
286
287 #[inline]
288 fn add(self, dt: Self::Duration) -> Self {
289 self + dt
290 }
291
292 #[inline]
293 fn is_valid(&self) -> bool {
294 *self > chrono::Utc::now()
295 }
296
297 #[inline]
298 fn is_expired(&self) -> bool {
299 *self <= chrono::Utc::now()
300 }
301}