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#[derive(Clone, Copy, Debug, PartialEq, Eq)]
71pub struct Served {
72 pub outcome: Outcome,
74 pub route: Option<RouteId>,
76 pub cache: Cacheable,
78}
79
80#[derive(Clone, Copy, Debug, PartialEq, Eq)]
88pub struct Compression {
89 pub(crate) min_bytes: usize,
90 pub(crate) level: u8,
91}
92
93impl Default for Compression {
94 fn default() -> Self {
95 Self {
96 min_bytes: 1024,
97 level: 6,
98 }
99 }
100}
101
102impl Compression {
103 #[must_use]
105 pub fn min_bytes(mut self, bytes: usize) -> Self {
106 self.min_bytes = bytes;
107 self
108 }
109
110 #[must_use]
112 pub fn level(mut self, level: u8) -> Self {
113 self.level = level.clamp(1, 9);
114 self
115 }
116}
117
118pub(crate) fn compressible(content_type: &[u8]) -> bool {
120 let t = content_type.split(|&b| b == b';').next().unwrap_or(b"");
121 let t = t.trim_ascii();
122 t.starts_with(b"text/")
123 || matches!(
124 t,
125 b"application/json"
126 | b"application/javascript"
127 | b"application/xml"
128 | b"application/manifest+json"
129 | b"application/rss+xml"
130 | b"application/atom+xml"
131 | b"image/svg+xml"
132 )
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138
139 #[test]
140 fn test_policy_values() {
141 assert_eq!(CachePolicy::Unset.value(true), None);
142 assert_eq!(
143 CachePolicy::Guests { max_age: 60 }.value(true).unwrap(),
144 "public, max-age=60"
145 );
146 assert_eq!(
147 CachePolicy::Guests { max_age: 60 }.value(false).unwrap(),
148 "private, no-store"
149 );
150 assert_eq!(
151 CachePolicy::Private { max_age: 5 }.value(false).unwrap(),
152 "private, max-age=5"
153 );
154 }
155
156 #[test]
157 fn test_compressible() {
158 assert!(compressible(b"text/html; charset=utf-8"));
159 assert!(compressible(b"application/json"));
160 assert!(!compressible(b"image/png"));
161 assert!(!compressible(b""));
162 }
163}