pub fn sanitize(filename: impl AsRef<str>) -> String {
let filename = filename.as_ref();
let options = sanitize_filename::Options {
windows: cfg!(windows),
truncate: false, replacement: "_",
};
let cleaned = sanitize_filename::sanitize_with_options(filename, options);
let (base, ext) = if cleaned.ends_with(".fdpart") {
let without_fdpart = &cleaned[..cleaned.len() - 7];
if let Some(pos) = without_fdpart.rfind('.') {
let mid_ext_candidate = &without_fdpart[pos..];
if mid_ext_candidate.len() > 16 {
(without_fdpart, ".fdpart")
} else {
(&cleaned[..pos], &cleaned[pos..])
}
} else {
(without_fdpart, ".fdpart")
}
} else if let Some(pos) = cleaned.rfind('.') {
let ext_candidate = &cleaned[pos..];
if ext_candidate.len() > 16 {
(&cleaned[..], "")
} else {
(&cleaned[..pos], ext_candidate)
}
} else {
(&cleaned[..], "")
};
const MAX_BYTES: usize = 255;
let ext_bytes = ext.len();
let max_base_bytes = MAX_BYTES.saturating_sub(ext_bytes);
let final_base = truncate_to_bytes(base, max_base_bytes);
format!("{}{}", final_base, ext)
}
fn truncate_to_bytes(s: &str, max_bytes: usize) -> &str {
if s.len() <= max_bytes {
return s;
}
let mut end = max_bytes;
while end > 0 && !s.is_char_boundary(end) {
end -= 1;
}
&s[..end]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sanitize() {
let long_stem = "这是一个非常".repeat(50);
let long_name = format!("{}.mp4.fdpart", long_stem);
let result = sanitize(&long_name);
assert!(result.ends_with(".mp4.fdpart"));
assert!(result.len() <= 255);
assert!(result.len() >= 252);
let long_stem = "这是一个非常".repeat(50);
let long_name = format!("{}.mp4", long_stem);
let result = sanitize(&long_name);
assert!(result.ends_with(".mp4"));
assert!(result.len() <= 255);
assert!(result.len() >= 252);
let long_stem = "这是一个非常".repeat(50);
let long_name = format!("1.{}", long_stem);
let result = sanitize(&long_name);
assert!(result.len() <= 255);
assert!(result.len() >= 252);
let long_stem = "这是一个非常".repeat(50);
let long_name = format!("1.{}.fdpart", long_stem);
let result = sanitize(&long_name);
assert!(result.ends_with(".fdpart"));
assert!(result.len() <= 255);
assert!(result.len() >= 252);
let normal_name = "我的文件.test.txt";
let result = sanitize(normal_name);
assert_eq!(result, "我的文件.test.txt");
let illegal = "test/\\:*?\"<>|.png";
let result = sanitize(illegal);
assert_eq!(result, "test_________.png");
}
}