1use serve_net::Outcome;
5
6use crate::RouteId;
7
8#[non_exhaustive]
11#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
12pub enum CachePolicy {
13 #[default]
15 Unset,
16 NoStore,
18 Private {
20 max_age: u32,
22 },
23 Public {
25 max_age: u32,
27 },
28 Guests {
33 max_age: u32,
35 },
36}
37
38impl CachePolicy {
39 pub(crate) fn value(self, guest: bool) -> Option<String> {
41 match self {
42 Self::Unset => None,
43 Self::NoStore => Some("no-store".into()),
44 Self::Private { max_age } => Some(format!("private, max-age={max_age}")),
45 Self::Public { max_age } => Some(format!("public, max-age={max_age}")),
46 Self::Guests { max_age } if guest => Some(format!("public, max-age={max_age}")),
47 Self::Guests { .. } => Some("private, no-store".into()),
48 }
49 }
50}
51
52#[non_exhaustive]
54#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
55pub enum Cacheable {
56 #[default]
58 No,
59 Guests {
64 max_age: u32,
66 },
67}
68
69#[non_exhaustive]
71#[derive(Clone, Copy, Debug, PartialEq, Eq)]
72pub struct Served {
73 pub outcome: Outcome,
75 pub route: Option<RouteId>,
77 pub cache: Cacheable,
79}
80
81#[derive(Clone, Copy, Debug, PartialEq, Eq)]
89pub struct Compression {
90 pub(crate) min_bytes: usize,
91 pub(crate) level: u8,
92}
93
94impl Default for Compression {
95 fn default() -> Self {
96 Self {
97 min_bytes: 1024,
98 level: 6,
99 }
100 }
101}
102
103impl Compression {
104 #[must_use]
106 pub fn min_bytes(mut self, bytes: usize) -> Self {
107 self.min_bytes = bytes;
108 self
109 }
110
111 #[must_use]
113 pub fn level(mut self, level: u8) -> Self {
114 self.level = level.clamp(1, 9);
115 self
116 }
117}
118
119pub(crate) fn compressible(content_type: &[u8]) -> bool {
121 let t = content_type.split(|&b| b == b';').next().unwrap_or(b"");
122 let t = t.trim_ascii();
123 t.starts_with(b"text/")
124 || matches!(
125 t,
126 b"application/json"
127 | b"application/javascript"
128 | b"application/xml"
129 | b"application/manifest+json"
130 | b"application/rss+xml"
131 | b"application/atom+xml"
132 | b"image/svg+xml"
133 )
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139
140 #[test]
141 fn test_policy_values() {
142 assert_eq!(CachePolicy::Unset.value(true), None);
143 assert_eq!(
144 CachePolicy::Guests { max_age: 60 }.value(true).unwrap(),
145 "public, max-age=60"
146 );
147 assert_eq!(
148 CachePolicy::Guests { max_age: 60 }.value(false).unwrap(),
149 "private, no-store"
150 );
151 assert_eq!(
152 CachePolicy::Private { max_age: 5 }.value(false).unwrap(),
153 "private, max-age=5"
154 );
155 }
156
157 #[test]
158 fn test_compressible() {
159 assert!(compressible(b"text/html; charset=utf-8"));
160 assert!(compressible(b"application/json"));
161 assert!(!compressible(b"image/png"));
162 assert!(!compressible(b""));
163 }
164}