use std::{collections::HashSet, str::FromStr};
use bytes::{Bytes, BytesMut};
pub use http::HeaderName;
use http::{HeaderMap, header::InvalidHeaderName};
use crate::Error;
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct VaryRule {
headers: Vec<HeaderName>,
}
impl FromStr for VaryRule {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let headers: Result<Vec<HeaderName>, InvalidHeaderName> =
s.split(" ").map(HeaderName::try_from).collect();
Ok(VaryRule::new(headers?.iter()))
}
}
impl VaryRule {
pub fn new<'a>(headers: impl IntoIterator<Item = &'a HeaderName>) -> VaryRule {
let headers: HashSet<HeaderName> = headers.into_iter().cloned().collect();
let mut headers: Vec<HeaderName> = headers.into_iter().collect();
headers.sort_by(|a, b| a.as_str().cmp(b.as_str()));
VaryRule { headers }
}
pub fn variant(&self, headers: &HeaderMap) -> Variant {
let mut buf = BytesMut::new();
buf.extend_from_slice(format!("[headers: {}]", self.headers.len()).as_bytes());
for header in self.headers.iter() {
buf.extend_from_slice(format!("[header: {}]", header.as_str().len()).as_bytes());
buf.extend_from_slice(header.as_str().as_bytes());
let values = headers.get_all(header);
buf.extend_from_slice(format!("[values: {}]", values.iter().count()).as_bytes());
for value in values.iter() {
buf.extend_from_slice(format!("[value: {}]", value.as_bytes().len()).as_bytes());
buf.extend_from_slice(value.as_bytes());
}
}
Variant {
signature: buf.into(),
}
}
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default, Clone)]
pub struct Variant {
signature: Bytes,
}
#[cfg(test)]
mod tests {
use super::VaryRule;
#[test]
fn vary_rule_unique_sorted() {
let vary1: VaryRule = "unknown-header Accept content-type".parse().unwrap();
let vary2: VaryRule = "content-type unknown-header unknown-header Accept"
.parse()
.unwrap();
assert_eq!(vary1, vary2);
}
}