use std::borrow::Cow;
fn is_extension(ext: &str) -> bool {
!ext.is_empty() && ext.len() <= 16 && ext.chars().all(|c| c.is_ascii_graphic())
}
fn best_ext(content_type: &str) -> Option<&str> {
let mime_type = content_type.split(';').next().map(|s| s.trim())?;
match mime_type {
"application/octet-stream" => None,
_ => mime_guess::get_mime_extensions_str(mime_type).and_then(min_ext),
}
}
fn min_ext<'a>(extensions: &[&'a str]) -> Option<&'a str> {
extensions
.iter()
.copied()
.min_by(|a, b| a.len().cmp(&b.len()).then_with(|| a.cmp(b)))
}
pub fn auto_ext<'a>(file_name: &'a str, content_type: Option<&str>) -> Cow<'a, str> {
let file_name = file_name.trim_end_matches('.');
let has_valid_ext = file_name
.rfind('.')
.is_some_and(|pos| is_extension(&file_name[pos + 1..]));
if has_valid_ext {
return Cow::Borrowed(file_name);
}
if let Some(ct) = content_type
&& let Some(ext) = best_ext(ct)
{
return Cow::Owned(format!("{}.{}", file_name, ext));
}
Cow::Borrowed(file_name)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_auto_ext() {
let res = auto_ext("document.pdf", Some("application/pdf"));
assert_eq!("document.pdf", res);
let res = auto_ext("avatar", Some("image/jpeg"));
assert_eq!("avatar.jpe", res);
let res = auto_ext("index", Some("text/html; charset=utf-8"));
assert_eq!("index.htm", res);
let res = auto_ext("unknown_file", None);
assert_eq!("unknown_file", res);
let res = auto_ext("some_file", Some("not-a-valid-mime"));
assert_eq!("some_file", res);
let res = auto_ext(".gitignore", Some("text/plain"));
assert_eq!(".gitignore", res);
let res = auto_ext("1.这是一个视频", Some("video/mp4"));
assert_eq!("1.这是一个视频.mp4", res);
let res = auto_ext("视频.mp4", None);
assert_eq!("视频.mp4", res);
let res = auto_ext("视频", Some("application/octet-stream"));
assert_eq!("视频", res);
}
}