pub fn mime_type_matches(actual: &str, pattern: &str) -> bool {
if actual == pattern {
return true;
}
if pattern.ends_with("/*") {
let prefix = &pattern[..pattern.len() - 1]; if actual.starts_with(prefix) {
return true;
}
}
if actual.eq_ignore_ascii_case(pattern) {
return true;
}
if pattern.ends_with("/*") {
let prefix = pattern[..pattern.len() - 1].to_lowercase();
if actual.to_lowercase().starts_with(&prefix) {
return true;
}
}
false
}
pub fn mime_type_matches_any(actual: &str, patterns: &[String]) -> bool {
if patterns.is_empty() {
return true;
}
patterns.iter().any(|p| mime_type_matches(actual, p))
}
pub fn normalize_mime_type(mime: &str) -> &str {
mime.split(';').next().unwrap_or(mime).trim()
}
pub fn is_valid_mime_type(mime: &str) -> bool {
let normalized = normalize_mime_type(mime);
let slash_count = normalized.chars().filter(|&c| c == '/').count();
if slash_count != 1 {
return false;
}
let parts: Vec<&str> = normalized.split('/').collect();
if parts.len() != 2 {
return false;
}
let type_part = parts[0];
let subtype_part = parts[1];
if type_part.is_empty() || subtype_part.is_empty() {
return false;
}
let is_valid_token = |s: &str| {
!s.is_empty()
&& s.chars()
.all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '.' || c == '+' || c == '_')
};
if subtype_part == "*" {
return is_valid_token(type_part);
}
is_valid_token(type_part) && is_valid_token(subtype_part)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_exact_match() {
assert!(mime_type_matches("image/png", "image/png"));
assert!(mime_type_matches("text/plain", "text/plain"));
assert!(!mime_type_matches("image/png", "image/jpeg"));
}
#[test]
fn test_wildcard_match() {
assert!(mime_type_matches("image/png", "image/*"));
assert!(mime_type_matches("image/jpeg", "image/*"));
assert!(mime_type_matches("image/gif", "image/*"));
assert!(!mime_type_matches("video/mp4", "image/*"));
assert!(!mime_type_matches("text/plain", "image/*"));
}
#[test]
fn test_case_insensitive() {
assert!(mime_type_matches("IMAGE/PNG", "image/png"));
assert!(mime_type_matches("image/png", "IMAGE/PNG"));
assert!(mime_type_matches("Image/Png", "image/*"));
}
#[test]
fn test_matches_any() {
let patterns = vec![
"image/png".to_string(),
"image/jpeg".to_string(),
"image/gif".to_string(),
];
assert!(mime_type_matches_any("image/png", &patterns));
assert!(mime_type_matches_any("image/jpeg", &patterns));
assert!(!mime_type_matches_any("image/webp", &patterns));
}
#[test]
fn test_matches_any_wildcard() {
let patterns = vec!["image/*".to_string(), "video/*".to_string()];
assert!(mime_type_matches_any("image/png", &patterns));
assert!(mime_type_matches_any("video/mp4", &patterns));
assert!(!mime_type_matches_any("text/plain", &patterns));
}
#[test]
fn test_matches_any_empty() {
let patterns: Vec<String> = vec![];
assert!(mime_type_matches_any("image/png", &patterns));
assert!(mime_type_matches_any("application/json", &patterns));
}
#[test]
fn test_normalize_mime_type() {
assert_eq!(normalize_mime_type("text/plain"), "text/plain");
assert_eq!(
normalize_mime_type("text/plain; charset=utf-8"),
"text/plain"
);
assert_eq!(
normalize_mime_type("text/html; charset=utf-8; boundary=something"),
"text/html"
);
assert_eq!(normalize_mime_type(" text/plain "), "text/plain");
}
#[test]
fn test_is_valid_mime_type() {
assert!(is_valid_mime_type("text/plain"));
assert!(is_valid_mime_type("image/png"));
assert!(is_valid_mime_type("application/json"));
assert!(is_valid_mime_type("application/vnd.api+json"));
assert!(is_valid_mime_type("image/*"));
assert!(is_valid_mime_type("application/x-custom"));
assert!(!is_valid_mime_type("text"));
assert!(!is_valid_mime_type("text/"));
assert!(!is_valid_mime_type("/plain"));
assert!(!is_valid_mime_type("text/plain/extra"));
assert!(!is_valid_mime_type(""));
assert!(!is_valid_mime_type("invalid"));
}
#[test]
fn test_common_mime_types() {
let image_types = vec!["image/*".to_string()];
assert!(mime_type_matches_any("image/png", &image_types));
assert!(mime_type_matches_any("image/jpeg", &image_types));
assert!(mime_type_matches_any("image/gif", &image_types));
assert!(mime_type_matches_any("image/webp", &image_types));
assert!(!mime_type_matches_any("video/mp4", &image_types));
let video_types = vec!["video/*".to_string()];
assert!(mime_type_matches_any("video/mp4", &video_types));
assert!(mime_type_matches_any("video/webm", &video_types));
assert!(!mime_type_matches_any("image/png", &video_types));
}
}