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 #[inline]
136 pub fn ok_or<E>(self, err: E) -> Result<T, E> {
137 self.into_timed_value().ok_or(err)
138 }
139
140 #[inline]
141 pub fn ok_or_else<E>(self, err: impl FnOnce(Option<T>) -> E) -> Result<T, E> {
142 self.into_timed_value().ok_or_else(err)
143 }
144
145 #[inline]
146 pub fn map<U>(self, f: impl FnOnce(T) -> U) -> TimedOption<U, B> {
147 TimedOption {
148 value: self.value.map(f),
149 ttl: self.ttl,
150 }
151 }
152}
153
154#[derive(Debug, Copy, Clone, PartialEq, Eq)]
162#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
163pub enum TimedValue<T> {
164 Valid(T),
166 Expired(T),
168 None,
170}
171
172impl<T> TimedValue<T> {
173 #[inline]
175 pub const fn is_valid(&self) -> bool {
176 match self {
177 TimedValue::Valid(_) => true,
178 TimedValue::Expired(_) => false,
179 TimedValue::None => false,
180 }
181 }
182
183 #[inline]
185 pub const fn is_expired(&self) -> bool {
186 match self {
187 TimedValue::Valid(_) => false,
188 TimedValue::Expired(_) => true,
189 TimedValue::None => false,
190 }
191 }
192
193 #[inline]
195 pub const fn is_none(&self) -> bool {
196 match self {
197 TimedValue::Valid(_) => false,
198 TimedValue::Expired(_) => false,
199 TimedValue::None => true,
200 }
201 }
202
203 #[inline]
205 pub const fn has_value(&self) -> bool {
206 match self {
207 TimedValue::Valid(_) => true,
208 TimedValue::Expired(_) => true,
209 TimedValue::None => false,
210 }
211 }
212
213 #[inline]
215 pub const fn as_ref(&self) -> TimedValue<&T> {
216 match *self {
217 TimedValue::Valid(ref inner) => TimedValue::Valid(inner),
218 TimedValue::Expired(ref inner) => TimedValue::Expired(inner),
219 TimedValue::None => TimedValue::None,
220 }
221 }
222
223 #[inline]
224 pub fn ok_or<E>(self, err: E) -> Result<T, E> {
225 match self {
226 TimedValue::Valid(inner) => Ok(inner),
227 TimedValue::Expired(_) => Err(err),
228 TimedValue::None => Err(err),
229 }
230 }
231
232 #[inline]
233 pub fn ok_or_else<E>(self, err: impl FnOnce(Option<T>) -> E) -> Result<T, E> {
234 match self {
235 TimedValue::Valid(inner) => Ok(inner),
236 TimedValue::Expired(inner) => Err(err(Some(inner))),
237 TimedValue::None => Err(err(None)),
238 }
239 }
240
241 #[inline]
242 pub fn map<U>(self, f: impl FnOnce(T) -> U) -> TimedValue<U> {
243 match self {
244 TimedValue::Valid(inner) => TimedValue::Valid(f(inner)),
245 TimedValue::Expired(inner) => TimedValue::Expired(f(inner)),
246 TimedValue::None => TimedValue::None,
247 }
248 }
249}
250
251impl<T, B> From<TimedOption<T, B>> for Option<T>
256where
257 B: TtlBackend,
258{
259 #[inline]
260 fn from(value: TimedOption<T, B>) -> Self {
261 value.into_option()
262 }
263}
264
265impl<T, B> From<TimedOption<T, B>> for TimedValue<T>
266where
267 B: TtlBackend,
268{
269 #[inline]
270 fn from(value: TimedOption<T, B>) -> Self {
271 value.into_timed_value()
272 }
273}
274
275pub trait TtlBackend: Clone {
280 type Duration;
281
282 fn now() -> Self;
283 fn expired() -> Self;
284 fn add(self, dt: Self::Duration) -> Self;
285 fn is_valid(&self) -> bool;
286 fn is_expired(&self) -> bool;
287}
288
289impl TtlBackend for std::time::Instant {
290 type Duration = std::time::Duration;
291
292 #[inline]
293 fn now() -> Self {
294 std::time::Instant::now()
295 }
296
297 #[inline]
298 fn expired() -> Self {
299 std::time::Instant::now()
300 }
301
302 #[inline]
303 fn add(self, dt: Self::Duration) -> Self {
304 self + dt
305 }
306
307 #[inline]
308 fn is_valid(&self) -> bool {
309 *self > std::time::Instant::now()
310 }
311
312 #[inline]
313 fn is_expired(&self) -> bool {
314 *self <= std::time::Instant::now()
315 }
316}
317
318#[cfg(feature = "chrono")]
319impl TtlBackend for chrono::DateTime<chrono::Utc> {
320 type Duration = chrono::Duration;
321
322 #[inline]
323 fn now() -> Self {
324 chrono::Utc::now()
325 }
326
327 #[inline]
328 fn expired() -> Self {
329 chrono::Utc::now()
330 }
331
332 #[inline]
333 fn add(self, dt: Self::Duration) -> Self {
334 self + dt
335 }
336
337 #[inline]
338 fn is_valid(&self) -> bool {
339 *self > chrono::Utc::now()
340 }
341
342 #[inline]
343 fn is_expired(&self) -> bool {
344 *self <= chrono::Utc::now()
345 }
346}