cookie_monster/cookie/expires/
dep_jiff.rs

1use std::{fmt::Write, sync::LazyLock};
2
3use jiff::{SignedDuration, Span, Zoned, tz::TimeZone};
4
5use crate::{Cookie, Error, cookie::expires::ExpVal};
6
7use super::Expires;
8
9// Sun, 06 Nov 1994 08:49:37 GMT (RFC)
10static FMT: &str = "%a, %d %b %Y %T %Z";
11
12impl From<Zoned> for Expires {
13    fn from(value: Zoned) -> Self {
14        Self::Exp(super::ExpVal {
15            jiff: Some(value),
16            ..Default::default()
17        })
18    }
19}
20
21impl Cookie {
22    /// Returns the Expires attribute using a [`Zoned`](jiff::Zoned).
23    pub fn expires_jiff(&self) -> Option<&Zoned> {
24        match &self.expires {
25            Expires::Exp(ExpVal { jiff, .. }) => jiff.as_ref(),
26            _ => None,
27        }
28    }
29
30    /// Returns the Max-Age attribute using a [`SignedDuration`](jiff::SignedDuration).
31    pub fn max_age_jiff(&self) -> Option<SignedDuration> {
32        self.max_age_secs()
33            .map(|max_age| SignedDuration::from_secs(max_age as i64))
34    }
35}
36
37impl Expires {
38    /// Creates an [`Expires`] with a [`Zoned`](jiff::Zoned) one year in the past.
39    pub fn remove_jiff() -> Self {
40        Self::from(&Zoned::now() - Span::new().years(1))
41    }
42}
43
44pub(super) fn ser_expires(expires: &Zoned, buf: &mut String) -> crate::Result<()> {
45    static GMT: LazyLock<TimeZone> =
46        LazyLock::new(|| TimeZone::get("GMT").expect("Failed to look up `GMT` timezone"));
47
48    let zoned = expires.with_time_zone(GMT.clone());
49    write!(buf, "; Expires={}", zoned.strftime(FMT)).map_err(|_| Error::ExpiresFmt)
50}
51
52#[cfg(test)]
53mod test_jiff {
54    use crate::Cookie;
55    use jiff::{Zoned, civil::datetime};
56
57    #[test]
58    fn zoned() {
59        let now = Zoned::now();
60
61        let _ = Cookie::build("key", "value").expires(now);
62    }
63
64    #[test]
65    fn parse() {
66        let expires = datetime(2015, 10, 21, 7, 28, 0, 0).in_tz("GMT").unwrap();
67
68        let expected = Cookie::build("foo", "bar").expires(expires).build();
69
70        assert_eq!(
71            expected.serialize().as_deref(),
72            Ok("foo=bar; Expires=Wed, 21 Oct 2015 07:28:00 GMT")
73        )
74    }
75}