http_types_rs/cache/cache_control/
cache_directive.rs1use crate::headers::HeaderValue;
2use crate::Status;
3
4use std::time::Duration;
5
6#[non_exhaustive]
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum CacheDirective {
10 Immutable,
12 MaxAge(Duration),
14 MaxStale(Option<Duration>),
16 MinFresh(Duration),
18 MustRevalidate,
20 NoCache,
22 NoStore,
24 NoTransform,
27 OnlyIfCached,
29 Private,
32 ProxyRevalidate,
34 Public,
37 SMaxAge(Duration),
39 StaleIfError(Duration),
41 StaleWhileRevalidate(Duration),
44}
45
46impl CacheDirective {
47 pub fn valid_in_req(&self) -> bool {
49 use CacheDirective::*;
50 matches!(
51 self,
52 MaxAge(_) | MaxStale(_) | MinFresh(_) | NoCache | NoStore | NoTransform | OnlyIfCached
53 )
54 }
55
56 pub fn valid_in_res(&self) -> bool {
58 use CacheDirective::*;
59 matches!(
60 self,
61 MustRevalidate
62 | NoCache
63 | NoStore
64 | NoTransform
65 | Public
66 | Private
67 | ProxyRevalidate
68 | MaxAge(_)
69 | SMaxAge(_)
70 | StaleIfError(_)
71 | StaleWhileRevalidate(_)
72 )
73 }
74
75 pub(crate) fn from_str(s: &str) -> crate::Result<Option<Self>> {
82 use CacheDirective::*;
83
84 let s = s.trim();
85
86 if s.is_empty() {
88 return Ok(None);
89 }
90
91 let s = s.to_lowercase();
92 let mut parts = s.split('=');
93 let next = parts.next().unwrap();
94
95 let mut get_dur = || -> crate::Result<Duration> {
96 let dur = parts.next().status(400)?;
97 let dur: u64 = dur.parse::<u64>().status(400)?;
98 Ok(Duration::new(dur, 0))
99 };
100
101 let res = match next {
103 "immutable" => Some(Immutable),
104 "no-cache" => Some(NoCache),
105 "no-store" => Some(NoStore),
106 "no-transform" => Some(NoTransform),
107 "only-if-cached" => Some(OnlyIfCached),
108 "must-revalidate" => Some(MustRevalidate),
109 "public" => Some(Public),
110 "private" => Some(Private),
111 "proxy-revalidate" => Some(ProxyRevalidate),
112 "max-age" => Some(MaxAge(get_dur()?)),
113 "max-stale" => match parts.next() {
114 Some(secs) => {
115 let dur: u64 = secs.parse::<u64>().status(400)?;
116 Some(MaxStale(Some(Duration::new(dur, 0))))
117 }
118 None => Some(MaxStale(None)),
119 },
120 "min-fresh" => Some(MinFresh(get_dur()?)),
121 "s-maxage" => Some(SMaxAge(get_dur()?)),
122 "stale-if-error" => Some(StaleIfError(get_dur()?)),
123 "stale-while-revalidate" => Some(StaleWhileRevalidate(get_dur()?)),
124 _ => None,
125 };
126 Ok(res)
127 }
128}
129
130impl From<CacheDirective> for HeaderValue {
131 fn from(directive: CacheDirective) -> Self {
132 use CacheDirective::*;
133 let h = |s: String| unsafe { HeaderValue::from_bytes_unchecked(s.into_bytes()) };
134
135 match directive {
136 Immutable => h("immutable".to_string()),
137 MaxAge(dur) => h(format!("max-age={}", dur.as_secs())),
138 MaxStale(dur) => match dur {
139 Some(dur) => h(format!("max-stale={}", dur.as_secs())),
140 None => h("max-stale".to_string()),
141 },
142 MinFresh(dur) => h(format!("min-fresh={}", dur.as_secs())),
143 MustRevalidate => h("must-revalidate".to_string()),
144 NoCache => h("no-cache".to_string()),
145 NoStore => h("no-store".to_string()),
146 NoTransform => h("no-transform".to_string()),
147 OnlyIfCached => h("only-if-cached".to_string()),
148 Private => h("private".to_string()),
149 ProxyRevalidate => h("proxy-revalidate".to_string()),
150 Public => h("public".to_string()),
151 SMaxAge(dur) => h(format!("s-max-age={}", dur.as_secs())),
152 StaleIfError(dur) => h(format!("stale-if-error={}", dur.as_secs())),
153 StaleWhileRevalidate(dur) => h(format!("stale-while-revalidate={}", dur.as_secs())),
154 }
155 }
156}