cookie_monster/cookie/expires/
mod.rs

1use 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/// The Expires attribute.
17#[derive(Clone, Default)]
18pub enum Expires {
19    // So a user can still remove a cookie without needing any of the datetime features.
20    Remove,
21    // No expiry time.
22    #[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 {
39        #![allow(unreachable_code)]
40
41        #[cfg(feature = "jiff")]
42        return Self::remove_jiff();
43
44        #[cfg(feature = "chrono")]
45        return Self::remove_chrono();
46
47        #[cfg(feature = "time")]
48        return Self::remove_time();
49
50        Self::Remove
51    }
52}
53
54impl Cookie {
55    /// If the Expires attribute is not set, the expiration of the cookie is tied to the session
56    /// with the user-agent.
57    pub fn is_expires_set(&self) -> bool {
58        matches!(self.expires, Expires::Session)
59    }
60
61    pub(crate) fn serialize_expire(&self, buf: &mut String) -> crate::Result<()> {
62        // Only one can be set at all times, except while parsing but then the first match is used.
63        match &self.expires {
64            #[cfg(feature = "time")]
65            Expires::Exp(ExpVal { time: Some(t), .. }) => dep_time::ser_expires(t, buf),
66            #[cfg(feature = "chrono")]
67            Expires::Exp(ExpVal {
68                chrono: Some(c), ..
69            }) => dep_chrono::ser_expires(c, buf),
70            #[cfg(feature = "jiff")]
71            Expires::Exp(ExpVal { jiff: Some(j), .. }) => dep_jiff::ser_expires(j, buf),
72            Expires::Remove => {
73                let _ = write!(buf, "; Expires={REMOVE}");
74                Ok(())
75            }
76
77            _ => Ok(()),
78        }
79    }
80}
81
82impl PartialEq for Expires {
83    fn eq(&self, other: &Self) -> bool {
84        match (self, other) {
85            (Expires::Remove, Expires::Remove) => true,
86            (Expires::Session, Expires::Session) => true,
87            (Expires::Exp(_s), Expires::Exp(_o)) => {
88                // TODO: double check this.
89                #[cfg(feature = "time")]
90                if _s.time == _o.time {
91                    return true;
92                }
93
94                #[cfg(feature = "chrono")]
95                if _s.chrono == _o.chrono {
96                    return true;
97                }
98                #[cfg(feature = "jiff")]
99                if _s.jiff == _o.jiff {
100                    return true;
101                }
102
103                false
104            }
105            _ => false,
106        }
107    }
108}
109
110impl Debug for Expires {
111    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112        match self {
113            Self::Remove => write!(f, "{REMOVE}"),
114            Self::Session => write!(f, "Session"),
115            Self::Exp(_exp) => {
116                let mut debug = f.debug_struct("Exp");
117
118                #[cfg(feature = "time")]
119                let debug = debug.field("expires_time", &_exp.time);
120
121                #[cfg(feature = "chrono")]
122                let debug = debug.field("expires_chrono", &_exp.chrono);
123
124                #[cfg(feature = "jiff")]
125                let debug = debug.field("expires_jiff", &_exp.jiff);
126
127                debug.finish()
128            }
129        }
130    }
131}