use crate::transform::params::OutputFormat;
#[derive(Debug, Clone)]
pub struct ResponseMeta {
pub content_type: String,
pub cache_control: String,
pub etag: Option<String>,
pub vary: &'static str,
}
pub fn content_type_from_format(format: OutputFormat) -> &'static str {
format.content_type()
}
pub fn generate_etag(data: &[u8]) -> String {
let limit = data.len().min(8192);
let hash = xxhash_rust::xxh3::xxh3_64(&data[..limit]);
format!("{hash:016x}")
}
pub fn build_cache_control(max_age: u32, is_public: bool) -> String {
let visibility = if is_public { "public" } else { "private" };
format!("{visibility}, max-age={max_age}")
}
pub fn should_return_304(request_etag: Option<&str>, response_etag: &str) -> bool {
let Some(raw) = request_etag else {
return false;
};
strip_etag_decorations(raw) == strip_etag_decorations(response_etag)
}
fn strip_etag_decorations(etag: &str) -> &str {
let s = etag.strip_prefix("W/").unwrap_or(etag);
if s.len() >= 2 && s.starts_with('"') && s.ends_with('"') {
&s[1..s.len() - 1]
} else {
s
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn content_type_from_format_maps_jpeg() {
assert_eq!(content_type_from_format(OutputFormat::Jpeg), "image/jpeg");
}
#[test]
fn content_type_from_format_maps_png() {
assert_eq!(content_type_from_format(OutputFormat::Png), "image/png");
}
#[test]
fn content_type_from_format_maps_webp() {
assert_eq!(content_type_from_format(OutputFormat::Webp), "image/webp");
}
#[test]
fn content_type_from_format_maps_avif() {
assert_eq!(content_type_from_format(OutputFormat::Avif), "image/avif");
}
#[test]
fn content_type_from_format_maps_gif() {
assert_eq!(content_type_from_format(OutputFormat::Gif), "image/gif");
}
#[test]
fn content_type_from_format_maps_auto_to_octet_stream() {
assert_eq!(
content_type_from_format(OutputFormat::Auto),
"application/octet-stream"
);
}
#[test]
fn generate_etag_produces_consistent_output() {
let data = b"hello world";
assert_eq!(generate_etag(data), generate_etag(data));
}
#[test]
fn generate_etag_differs_for_different_data() {
assert_ne!(generate_etag(b"aaa"), generate_etag(b"bbb"));
}
#[test]
fn generate_etag_returns_16_character_hex_string() {
let etag = generate_etag(b"test data");
assert_eq!(etag.len(), 16);
assert!(
etag.chars()
.all(|c| c.is_ascii_hexdigit() && !c.is_ascii_uppercase())
);
}
#[test]
fn build_cache_control_public() {
assert_eq!(build_cache_control(3600, true), "public, max-age=3600");
}
#[test]
fn build_cache_control_private() {
assert_eq!(build_cache_control(60, false), "private, max-age=60");
}
#[test]
fn build_cache_control_zero_max_age() {
assert_eq!(build_cache_control(0, true), "public, max-age=0");
}
#[test]
fn should_return_304_exact_match_returns_true() {
let etag = generate_etag(b"some image data");
assert!(should_return_304(Some(&etag), &etag));
}
#[test]
fn should_return_304_no_request_etag_returns_false() {
let etag = generate_etag(b"some image data");
assert!(!should_return_304(None, &etag));
}
#[test]
fn should_return_304_mismatch_returns_false() {
let etag_a = generate_etag(b"data a");
let etag_b = generate_etag(b"data b");
assert!(!should_return_304(Some(&etag_a), &etag_b));
}
#[test]
fn should_return_304_quoted_etag_handling() {
let etag = generate_etag(b"image bytes");
let quoted = format!("\"{etag}\"");
assert!(should_return_304(Some("ed), &etag));
}
#[test]
fn should_return_304_weak_etag_handling() {
let etag = generate_etag(b"image bytes");
let weak = format!("W/\"{etag}\"");
assert!(should_return_304(Some(&weak), &etag));
}
#[test]
fn response_meta_default_vary_is_accept() {
let meta = ResponseMeta {
content_type: "image/png".to_string(),
cache_control: "public, max-age=3600".to_string(),
etag: None,
vary: "Accept",
};
assert_eq!(meta.vary, "Accept");
}
}