http_types_rs/cache/
expires.rs1use crate::headers::{Header, HeaderName, HeaderValue, Headers, EXPIRES};
2use crate::utils::{fmt_http_date, parse_http_date};
3
4use std::fmt::Debug;
5use std::time::{Duration, SystemTime};
6
7#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)]
37pub struct Expires {
38 instant: SystemTime,
39}
40
41impl Expires {
42 pub fn new(dur: Duration) -> Self {
44 let instant = SystemTime::now() + dur;
45 Self { instant }
46 }
47
48 pub fn new_at(instant: SystemTime) -> Self {
50 Self { instant }
51 }
52
53 pub fn expiration(&self) -> SystemTime {
55 self.instant
56 }
57
58 pub fn from_headers(headers: impl AsRef<Headers>) -> crate::Result<Option<Self>> {
60 let headers = match headers.as_ref().get(EXPIRES) {
61 Some(headers) => headers,
62 None => return Ok(None),
63 };
64
65 let header = headers.iter().last().unwrap();
68
69 let instant = parse_http_date(header.as_str())?;
70 Ok(Some(Self { instant }))
71 }
72}
73
74impl Header for Expires {
75 fn header_name(&self) -> HeaderName {
76 EXPIRES
77 }
78 fn header_value(&self) -> HeaderValue {
79 let output = fmt_http_date(self.instant);
80
81 unsafe { HeaderValue::from_bytes_unchecked(output.into()) }
83 }
84}
85
86#[cfg(test)]
87mod test {
88 use super::*;
89 use crate::headers::Headers;
90
91 #[test]
92 fn smoke() -> crate::Result<()> {
93 let time = SystemTime::now() + Duration::from_secs(5 * 60);
94 let expires = Expires::new_at(time);
95
96 let mut headers = Headers::new();
97 expires.apply_header(&mut headers);
98
99 let expires = Expires::from_headers(headers)?.unwrap();
100
101 let elapsed = time.duration_since(expires.expiration())?;
103 assert_eq!(elapsed.as_secs(), 0);
104 Ok(())
105 }
106
107 #[test]
108 fn bad_request_on_parse_error() {
109 let mut headers = Headers::new();
110 headers.insert(EXPIRES, "<nori ate the tag. yum.>").unwrap();
111 let err = Expires::from_headers(headers).unwrap_err();
112 assert_eq!(err.status(), 400);
113 }
114}