cookie_store/
cookie_expiration.rs1#[cfg(feature = "serde")]
2use serde_derive::{Deserialize, Serialize};
3use time::{self, OffsetDateTime};
4
5#[derive(Eq, Clone, Debug)]
7#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
8pub enum CookieExpiration {
9 #[cfg_attr(feature = "serde", serde(with = "crate::rfc3339_fmt"))]
12 AtUtc(OffsetDateTime),
13 SessionEnd,
16}
17
18impl std::cmp::PartialEq for CookieExpiration {
20 fn eq(&self, other: &Self) -> bool {
21 match (self, other) {
22 (CookieExpiration::SessionEnd, CookieExpiration::SessionEnd) => true,
23 (CookieExpiration::AtUtc(this_offset), CookieExpiration::AtUtc(other_offset)) => {
24 this_offset.date() == other_offset.date()
26 && this_offset.time().hour() == other_offset.time().hour()
27 && this_offset.time().minute() == other_offset.time().minute()
28 && this_offset.time().second() == other_offset.time().second()
29 }
30 _ => false,
31 }
32 }
33}
34
35impl CookieExpiration {
36 pub fn is_expired(&self) -> bool {
38 self.expires_by(&time::OffsetDateTime::now_utc())
39 }
40
41 pub fn expires_by(&self, utc_tm: &time::OffsetDateTime) -> bool {
43 match *self {
44 CookieExpiration::AtUtc(ref expire_tm) => *expire_tm <= *utc_tm,
45 CookieExpiration::SessionEnd => false,
46 }
47 }
48}
49
50const MAX_RFC3339: time::OffsetDateTime = time::macros::date!(9999 - 12 - 31)
51 .with_time(time::macros::time!(23:59:59))
52 .assume_utc();
53impl From<u64> for CookieExpiration {
54 fn from(max_age: u64) -> CookieExpiration {
55 CookieExpiration::from(time::Duration::seconds(std::cmp::min(
58 time::Duration::MAX.whole_seconds() as u64,
59 max_age,
60 ) as i64))
61 }
62}
63
64impl From<time::OffsetDateTime> for CookieExpiration {
65 fn from(utc_tm: OffsetDateTime) -> CookieExpiration {
66 CookieExpiration::AtUtc(utc_tm.min(MAX_RFC3339))
67 }
68}
69
70impl From<cookie::Expiration> for CookieExpiration {
71 fn from(expiration: cookie::Expiration) -> CookieExpiration {
72 match expiration {
73 cookie::Expiration::DateTime(offset) => CookieExpiration::AtUtc(offset),
74 cookie::Expiration::Session => CookieExpiration::SessionEnd,
75 }
76 }
77}
78
79impl From<time::Duration> for CookieExpiration {
80 fn from(duration: time::Duration) -> Self {
81 let utc_tm = if duration.is_zero() {
85 time::OffsetDateTime::UNIX_EPOCH
86 } else {
87 let now_utc = time::OffsetDateTime::now_utc();
88 let d = (MAX_RFC3339 - now_utc).min(duration);
89 now_utc + d
90 };
91 CookieExpiration::from(utc_tm)
92 }
93}
94
95#[cfg(test)]
96mod tests {
97 use super::CookieExpiration;
98
99 use crate::utils::test::*;
100
101 #[test]
102 fn max_age_bounds() {
103 match CookieExpiration::from(time::Duration::MAX.whole_seconds() as u64 + 1) {
104 CookieExpiration::AtUtc(_) => {}
105 _ => unreachable!(),
106 }
107 }
108
109 #[test]
110 fn expired() {
111 let ma = CookieExpiration::from(0u64); assert!(ma.is_expired());
113 assert!(ma.expires_by(&in_days(-1)));
114 }
115
116 #[test]
117 fn max_age() {
118 let ma = CookieExpiration::from(60u64);
119 assert!(!ma.is_expired());
120 assert!(ma.expires_by(&in_minutes(2)));
121 }
122
123 #[test]
124 fn session_end() {
125 let se = CookieExpiration::SessionEnd;
127 assert!(!se.is_expired());
128 assert!(!se.expires_by(&in_days(1)));
129 assert!(!se.expires_by(&in_days(-1)));
130 }
131
132 #[test]
133 fn at_utc() {
134 {
135 let expire_tmrw = CookieExpiration::from(in_days(1));
136 assert!(!expire_tmrw.is_expired());
137 assert!(expire_tmrw.expires_by(&in_days(2)));
138 }
139 {
140 let expired_yest = CookieExpiration::from(in_days(-1));
141 assert!(expired_yest.is_expired());
142 assert!(!expired_yest.expires_by(&in_days(-2)));
143 }
144 }
145}