cookie_monster/cookie/expires/
dep_jiff.rs1use 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
9static 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 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 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 pub(crate) 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}