1#[cfg(all(not(feature = "std"), feature = "alloc"))]
24use alloc::{
25 string::{String, ToString},
26 vec::Vec,
27};
28use core::{fmt, str::FromStr};
29#[cfg(feature = "serde")]
30use serde::{Deserialize, Serialize};
31
32#[derive(Debug, Clone, PartialEq, Eq)]
42#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
43#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
44#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
45pub enum Vary {
46 Wildcard,
48 Headers(Vec<String>),
50}
51
52impl Vary {
53 #[must_use]
55 pub fn new() -> Self {
56 Self::Headers(Vec::new())
57 }
58
59 #[must_use]
61 pub fn wildcard() -> Self {
62 Self::Wildcard
63 }
64
65 #[must_use]
67 pub fn is_wildcard(&self) -> bool {
68 matches!(self, Self::Wildcard)
69 }
70
71 #[must_use]
73 pub fn headers(&self) -> Option<&[String]> {
74 match self {
75 Self::Wildcard => None,
76 Self::Headers(h) => Some(h),
77 }
78 }
79
80 pub fn add(&mut self, name: impl Into<String>) {
94 if let Self::Headers(headers) = self {
95 let name = name.into();
96 let lower = name.to_lowercase();
97 if !headers.iter().any(|h| h.to_lowercase() == lower) {
98 headers.push(name);
99 }
100 }
101 }
102
103 pub fn remove(&mut self, name: &str) -> bool {
117 if let Self::Headers(headers) = self {
118 let lower = name.to_lowercase();
119 let before = headers.len();
120 headers.retain(|h| h.to_lowercase() != lower);
121 return headers.len() < before;
122 }
123 false
124 }
125
126 #[must_use]
140 pub fn contains(&self, name: &str) -> bool {
141 match self {
142 Self::Wildcard => false,
143 Self::Headers(headers) => {
144 let lower = name.to_lowercase();
145 headers.iter().any(|h| h.to_lowercase() == lower)
146 }
147 }
148 }
149
150 #[must_use]
152 pub fn len(&self) -> Option<usize> {
153 match self {
154 Self::Wildcard => None,
155 Self::Headers(h) => Some(h.len()),
156 }
157 }
158
159 #[must_use]
161 pub fn is_empty(&self) -> bool {
162 match self {
163 Self::Wildcard => false,
164 Self::Headers(h) => h.is_empty(),
165 }
166 }
167}
168
169impl Default for Vary {
170 fn default() -> Self {
171 Self::new()
172 }
173}
174
175impl fmt::Display for Vary {
180 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
181 match self {
182 Self::Wildcard => f.write_str("*"),
183 Self::Headers(headers) => {
184 for (i, h) in headers.iter().enumerate() {
185 if i > 0 {
186 f.write_str(", ")?;
187 }
188 f.write_str(h)?;
189 }
190 Ok(())
191 }
192 }
193 }
194}
195
196#[derive(Debug, Clone, PartialEq, Eq)]
198pub struct ParseVaryError;
199
200impl fmt::Display for ParseVaryError {
201 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
202 f.write_str("invalid Vary header")
203 }
204}
205
206#[cfg(feature = "std")]
207impl std::error::Error for ParseVaryError {}
208
209impl FromStr for Vary {
210 type Err = ParseVaryError;
211
212 fn from_str(s: &str) -> Result<Self, Self::Err> {
224 let s = s.trim();
225 if s == "*" {
226 return Ok(Self::Wildcard);
227 }
228 let mut vary = Self::new();
229 for part in s.split(',') {
230 let part = part.trim();
231 if !part.is_empty() {
232 vary.add(part.to_string());
233 }
234 }
235 Ok(vary)
236 }
237}
238
239#[cfg(test)]
244mod tests {
245 use super::*;
246
247 #[test]
248 fn new_is_empty() {
249 let v = Vary::new();
250 assert!(v.is_empty());
251 assert!(!v.is_wildcard());
252 assert_eq!(v.to_string(), "");
253 }
254
255 #[test]
256 fn wildcard() {
257 let v = Vary::wildcard();
258 assert!(v.is_wildcard());
259 assert!(!v.is_empty());
260 assert_eq!(v.to_string(), "*");
261 }
262
263 #[test]
264 fn add_and_contains() {
265 let mut v = Vary::new();
266 v.add("Accept");
267 v.add("Accept-Encoding");
268 assert!(v.contains("Accept"));
269 assert!(v.contains("accept"));
270 assert!(v.contains("ACCEPT-ENCODING"));
271 assert!(!v.contains("Content-Type"));
272 assert_eq!(v.len(), Some(2));
273 }
274
275 #[test]
276 fn add_deduplicates() {
277 let mut v = Vary::new();
278 v.add("Accept");
279 v.add("accept");
280 assert_eq!(v.len(), Some(1));
281 assert_eq!(v.to_string(), "Accept");
282 }
283
284 #[test]
285 fn remove_present() {
286 let mut v = Vary::new();
287 v.add("Accept");
288 v.add("Content-Type");
289 assert!(v.remove("accept"));
290 assert!(!v.contains("Accept"));
291 assert_eq!(v.len(), Some(1));
292 }
293
294 #[test]
295 fn remove_absent_returns_false() {
296 let mut v = Vary::new();
297 v.add("Accept");
298 assert!(!v.remove("Content-Type"));
299 }
300
301 #[test]
302 fn add_on_wildcard_is_noop() {
303 let mut v = Vary::wildcard();
304 v.add("Accept");
305 assert!(v.is_wildcard());
306 }
307
308 #[test]
309 fn remove_on_wildcard_returns_false() {
310 let mut v = Vary::wildcard();
311 assert!(!v.remove("Accept"));
312 }
313
314 #[test]
315 fn display_multiple() {
316 let mut v = Vary::new();
317 v.add("Accept");
318 v.add("Accept-Encoding");
319 assert_eq!(v.to_string(), "Accept, Accept-Encoding");
320 }
321
322 #[test]
323 fn parse_wildcard() {
324 let v: Vary = "*".parse().unwrap();
325 assert!(v.is_wildcard());
326 }
327
328 #[test]
329 fn parse_header_list() {
330 let v: Vary = "Accept, Accept-Encoding".parse().unwrap();
331 assert!(v.contains("Accept"));
332 assert!(v.contains("Accept-Encoding"));
333 assert_eq!(v.len(), Some(2));
334 }
335
336 #[test]
337 fn roundtrip() {
338 let mut v = Vary::new();
339 v.add("Accept");
340 v.add("Origin");
341 let s = v.to_string();
342 let parsed: Vary = s.parse().unwrap();
343 assert_eq!(parsed, v);
344 }
345
346 #[test]
349 fn default_is_empty_headers() {
350 let v = Vary::default();
351 assert!(v.is_empty());
352 assert!(!v.is_wildcard());
353 }
354
355 #[test]
358 fn headers_returns_slice_for_headers_variant() {
359 let mut v = Vary::new();
360 v.add("Accept");
361 v.add("Content-Type");
362 let h = v.headers().unwrap();
363 assert_eq!(h.len(), 2);
364 assert_eq!(h[0], "Accept");
365 assert_eq!(h[1], "Content-Type");
366 }
367
368 #[test]
369 fn headers_returns_none_for_wildcard() {
370 let v = Vary::wildcard();
371 assert!(v.headers().is_none());
372 }
373
374 #[test]
377 fn parse_vary_error_display() {
378 let e = ParseVaryError;
379 assert_eq!(e.to_string(), "invalid Vary header");
380 }
381
382 #[test]
385 fn len_returns_none_for_wildcard() {
386 assert_eq!(Vary::wildcard().len(), None);
387 }
388
389 #[test]
392 fn contains_returns_false_for_wildcard() {
393 let v = Vary::wildcard();
394 assert!(!v.contains("Accept"));
395 }
396}