http_types/cache/
expires.rs1use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, EXPIRES};
2use crate::utils::{fmt_http_date, parse_http_date};
3
4use std::fmt::Debug;
5use std::option;
6use std::time::{Duration, SystemTime};
7
8#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)]
38pub struct Expires {
39 instant: SystemTime,
40}
41
42impl Expires {
43 pub fn new(dur: Duration) -> Self {
45 let instant = SystemTime::now() + dur;
46 Self { instant }
47 }
48
49 pub fn new_at(instant: SystemTime) -> Self {
51 Self { instant }
52 }
53
54 pub fn expiration(&self) -> SystemTime {
56 self.instant
57 }
58
59 pub fn from_headers(headers: impl AsRef<Headers>) -> crate::Result<Option<Self>> {
61 let headers = match headers.as_ref().get(EXPIRES) {
62 Some(headers) => headers,
63 None => return Ok(None),
64 };
65
66 let header = headers.iter().last().unwrap();
69
70 let instant = parse_http_date(header.as_str())?;
71 Ok(Some(Self { instant }))
72 }
73
74 pub fn apply(&self, mut headers: impl AsMut<Headers>) {
76 headers.as_mut().insert(EXPIRES, self.value());
77 }
78
79 pub fn name(&self) -> HeaderName {
81 EXPIRES
82 }
83
84 pub fn value(&self) -> HeaderValue {
86 let output = fmt_http_date(self.instant);
87
88 unsafe { HeaderValue::from_bytes_unchecked(output.into()) }
90 }
91}
92
93impl ToHeaderValues for Expires {
94 type Iter = option::IntoIter<HeaderValue>;
95 fn to_header_values(&self) -> crate::Result<Self::Iter> {
96 Ok(self.value().to_header_values().unwrap())
98 }
99}
100
101#[cfg(test)]
102mod test {
103 use super::*;
104 use crate::headers::Headers;
105
106 #[test]
107 fn smoke() -> crate::Result<()> {
108 let time = SystemTime::now() + Duration::from_secs(5 * 60);
109 let expires = Expires::new_at(time);
110
111 let mut headers = Headers::new();
112 expires.apply(&mut headers);
113
114 let expires = Expires::from_headers(headers)?.unwrap();
115
116 let elapsed = time.duration_since(expires.expiration())?;
118 assert_eq!(elapsed.as_secs(), 0);
119 Ok(())
120 }
121
122 #[test]
123 fn bad_request_on_parse_error() {
124 let mut headers = Headers::new();
125 headers.insert(EXPIRES, "<nori ate the tag. yum.>");
126 let err = Expires::from_headers(headers).unwrap_err();
127 assert_eq!(err.status(), 400);
128 }
129}