pub fn name_fix(text: &str) -> String { let mut result = String::with_capacity(text.len()); let mut last_was_underscore = false; for c in text.chars() { // Keep only ASCII characters if c.is_ascii() { // Replace specific characters with underscore if c.is_whitespace() || c == ',' || c == '-' || c == '"' || c == '\'' || c == '#' || c == '!' || c == '(' || c == ')' || c == '[' || c == ']' || c == '=' || c == '+' || c == '<' || c == '>' || c == '@' || c == '$' || c == '%' || c == '^' || c == '&' || c == '*' { // Only add underscore if the last character wasn't an underscore if !last_was_underscore { result.push('_'); last_was_underscore = true; } } else { // Add the character as is (will be converted to lowercase later) result.push(c); last_was_underscore = false; } } // Non-ASCII characters are simply skipped } // Convert to lowercase return result.to_lowercase(); } pub fn path_fix(text: &str) -> String { // If path ends with '/', return as is if text.ends_with('/') { return text.to_string(); } // Find the last '/' to extract the filename part match text.rfind('/') { Some(pos) => { // Extract the path and filename parts let path = &text[..=pos]; let filename = &text[pos + 1..]; // Apply name_fix to the filename part only return format!("{}{}", path, name_fix(filename)); } None => { // No '/' found, so the entire text is a filename return name_fix(text); } } } #[cfg(test)] mod tests { use super::*; #[test] fn test_name_fix() { // Test ASCII conversion and special character replacement assert_eq!(name_fix("Hello World"), "hello_world"); assert_eq!(name_fix("File-Name.txt"), "file_name.txt"); assert_eq!(name_fix("Test!@#$%^&*()"), "test_"); assert_eq!(name_fix("Space, Tab\t, Comma,"), "space_tab_comma_"); assert_eq!(name_fix("Quotes\"'"), "quotes_"); assert_eq!(name_fix("Brackets[]<>"), "brackets_"); assert_eq!(name_fix("Operators=+-"), "operators_"); // Test non-ASCII characters removal assert_eq!(name_fix("Café"), "caf"); assert_eq!(name_fix("Résumé"), "rsum"); assert_eq!(name_fix("Über"), "ber"); // Test lowercase conversion assert_eq!(name_fix("UPPERCASE"), "uppercase"); assert_eq!(name_fix("MixedCase"), "mixedcase"); } #[test] fn test_path_fix() { // Test path ending with / assert_eq!(path_fix("/path/to/directory/"), "/path/to/directory/"); // Test single filename assert_eq!(path_fix("filename.txt"), "filename.txt"); assert_eq!(path_fix("UPPER-file.md"), "upper_file.md"); // Test path with filename assert_eq!(path_fix("/path/to/File Name.txt"), "/path/to/file_name.txt"); assert_eq!( path_fix("./relative/path/to/DOCUMENT-123.pdf"), "./relative/path/to/document_123.pdf" ); assert_eq!( path_fix("/absolute/path/to/Résumé.doc"), "/absolute/path/to/rsum.doc" ); // Test path with special characters in filename assert_eq!( path_fix("/path/with/[special].txt"), "/path/with/_special_chars_.txt" ); } }