cookie_monster/cookie/expires/
mod.rs1use std::fmt::{Debug, Write};
2
3use super::Cookie;
4
5#[cfg(feature = "time")]
6pub mod dep_time;
7
8#[cfg(feature = "chrono")]
9pub mod dep_chrono;
10
11#[cfg(feature = "jiff")]
12pub mod dep_jiff;
13
14const REMOVE: &str = "Thu, 01 Jan 1970 00:00:00 GMT";
15
16#[derive(Clone, Default)]
18pub enum Expires {
19 Remove,
21 #[default]
23 Session,
24 Exp(ExpVal),
25}
26
27#[derive(Clone, Default)]
28pub struct ExpVal {
29 #[cfg(feature = "time")]
30 time: Option<time::OffsetDateTime>,
31 #[cfg(feature = "chrono")]
32 chrono: Option<chrono::DateTime<chrono::Utc>>,
33 #[cfg(feature = "jiff")]
34 jiff: Option<jiff::Zoned>,
35}
36
37impl Expires {
38 pub fn remove() -> Self {
42 #![allow(unreachable_code)]
43
44 #[cfg(feature = "jiff")]
45 return Self::remove_jiff();
46
47 #[cfg(feature = "chrono")]
48 return Self::remove_chrono();
49
50 #[cfg(feature = "time")]
51 return Self::remove_time();
52
53 Self::Remove
54 }
55}
56
57impl Cookie {
58 pub fn expires_is_set(&self) -> bool {
61 !matches!(self.expires, Expires::Session)
62 }
63
64 pub(crate) fn serialize_expire(&self, buf: &mut String) -> crate::Result<()> {
65 match &self.expires {
67 #[cfg(feature = "time")]
68 Expires::Exp(ExpVal { time: Some(t), .. }) => dep_time::ser_expires(t, buf),
69 #[cfg(feature = "chrono")]
70 Expires::Exp(ExpVal {
71 chrono: Some(c), ..
72 }) => dep_chrono::ser_expires(c, buf),
73 #[cfg(feature = "jiff")]
74 Expires::Exp(ExpVal { jiff: Some(j), .. }) => dep_jiff::ser_expires(j, buf),
75 Expires::Remove => {
76 let _ = write!(buf, "; Expires={REMOVE}");
77 Ok(())
78 }
79
80 _ => Ok(()),
81 }
82 }
83}
84
85impl PartialEq for Expires {
86 fn eq(&self, other: &Self) -> bool {
87 match (self, other) {
88 (Expires::Remove, Expires::Remove) => true,
89 (Expires::Session, Expires::Session) => true,
90 (Expires::Exp(_s), Expires::Exp(_o)) => {
91 #[cfg(feature = "time")]
95 if _s.time.is_some() || _o.time.is_some() {
96 return _s.time == _o.time;
97 }
98
99 #[cfg(feature = "chrono")]
100 if _s.chrono.is_some() || _o.chrono.is_some() {
101 return _s.chrono == _o.chrono;
102 }
103
104 #[cfg(feature = "jiff")]
105 if _s.jiff.is_some() || _o.jiff.is_some() {
106 return _s.jiff == _o.jiff;
107 }
108
109 true
111 }
112 _ => false,
113 }
114 }
115}
116
117#[cfg(all(test, feature = "time", feature = "jiff"))]
118mod eq_tests {
119 use crate::Cookie;
120 use jiff::{civil::datetime, tz::TimeZone};
121
122 fn cookie_expiring(year: i16) -> Cookie {
123 Cookie::build("foo", "bar")
124 .expires(
125 datetime(year, 1, 1, 0, 0, 0, 0)
126 .to_zoned(TimeZone::UTC)
127 .unwrap(),
128 )
129 .build()
130 }
131
132 #[test]
136 fn different_expiry_is_not_equal() {
137 assert_ne!(cookie_expiring(2020), cookie_expiring(2099));
138 }
139
140 #[test]
141 fn same_expiry_is_equal() {
142 assert_eq!(cookie_expiring(2020), cookie_expiring(2020));
143 }
144}
145
146impl Debug for Expires {
147 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
148 match self {
149 Self::Remove => write!(f, "{REMOVE}"),
150 Self::Session => write!(f, "Session"),
151 Self::Exp(_exp) => {
152 let mut debug = f.debug_struct("Exp");
153
154 #[cfg(feature = "time")]
155 let debug = debug.field("expires_time", &_exp.time);
156
157 #[cfg(feature = "chrono")]
158 let debug = debug.field("expires_chrono", &_exp.chrono);
159
160 #[cfg(feature = "jiff")]
161 let debug = debug.field("expires_jiff", &_exp.jiff);
162
163 debug.finish()
164 }
165 }
166 }
167}