mirror of
https://github.com/Xevion/byte-me.git
synced 2025-12-06 01:14:33 -06:00
feat: filename truncation method
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
mod ff;
|
mod ff;
|
||||||
mod media;
|
mod media;
|
||||||
mod models;
|
mod models;
|
||||||
|
pub mod strings;
|
||||||
|
|
||||||
use ff::extract_streams;
|
use ff::extract_streams;
|
||||||
use media::{detect_media_type, is_media_file};
|
use media::{detect_media_type, is_media_file};
|
||||||
|
|||||||
122
src-tauri/src/strings.rs
Normal file
122
src-tauri/src/strings.rs
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
/// Transforms a filename to fit within a character limit while preserving the most useful context
|
||||||
|
///
|
||||||
|
/// This function prioritizes preserving:
|
||||||
|
/// 1. File extension (if reasonable length ≤ 5 chars including dot)
|
||||||
|
/// 2. Beginning of filename (for identification)
|
||||||
|
/// 3. End of filename before extension (often contains important info like numbers)
|
||||||
|
///
|
||||||
|
/// # Arguments
|
||||||
|
/// * `filename` - The filename to transform
|
||||||
|
/// * `limit` - Maximum number of characters
|
||||||
|
///
|
||||||
|
/// # Returns
|
||||||
|
/// * Transformed filename that fits within the limit, using ellipsis (...) to indicate truncation
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// use byte_me_lib::strings::transform_filename;
|
||||||
|
///
|
||||||
|
/// // Short filenames remain unchanged
|
||||||
|
/// assert_eq!(transform_filename("test.mp4", 20), "test.mp4");
|
||||||
|
///
|
||||||
|
/// // Long filename with extension - preserve extension and context
|
||||||
|
/// assert_eq!(transform_filename("very_long_video_file_name.mp4", 18), "ver...ile_name.mp4");
|
||||||
|
///
|
||||||
|
/// // Numeric sequences - preserve start and end numbers
|
||||||
|
/// assert_eq!(transform_filename("43509374693.TS.mp4", 15), "435...93.TS.mp4");
|
||||||
|
///
|
||||||
|
/// // No extension - preserve start and end of name
|
||||||
|
/// assert_eq!(transform_filename("very_long_document_name", 15), "ver...ment_name");
|
||||||
|
///
|
||||||
|
/// // Long extension treated as part of name
|
||||||
|
/// assert_eq!(transform_filename("file.verylongextension", 15), "fil...extension");
|
||||||
|
/// ```
|
||||||
|
pub fn transform_filename(filename: &str, limit: usize) -> String {
|
||||||
|
// Handle edge cases
|
||||||
|
if limit == 0 || filename.is_empty() {
|
||||||
|
return String::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
if filename.len() <= limit {
|
||||||
|
return filename.to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find potential extension (last dot, not at start or end)
|
||||||
|
let extension_start = filename
|
||||||
|
.rfind('.')
|
||||||
|
.filter(|&pos| pos > 0 && pos < filename.len() - 1);
|
||||||
|
|
||||||
|
let (name_part, extension_part) = if let Some(ext_pos) = extension_start {
|
||||||
|
let ext = &filename[ext_pos..];
|
||||||
|
// Only treat as extension if it's reasonable length (≤ 5 chars including dot)
|
||||||
|
// and doesn't contain additional dots (compound extensions like .TS.mp4)
|
||||||
|
if ext.len() <= 5 && !ext[1..].contains('.') {
|
||||||
|
(&filename[..ext_pos], ext)
|
||||||
|
} else {
|
||||||
|
(filename, "")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
(filename, "")
|
||||||
|
};
|
||||||
|
|
||||||
|
// If even just the extension is too long, truncate the whole thing
|
||||||
|
if extension_part.len() >= limit {
|
||||||
|
return truncate_string(filename, limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate space available for the name part
|
||||||
|
let name_limit = limit - extension_part.len();
|
||||||
|
|
||||||
|
// If name fits in available space, no truncation needed
|
||||||
|
if name_part.len() <= name_limit {
|
||||||
|
return filename.to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need to truncate the name part
|
||||||
|
let truncated_name = truncate_string(name_part, name_limit);
|
||||||
|
format!("{}{}", truncated_name, extension_part)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper function to truncate a string with ellipsis, preserving start and end context
|
||||||
|
pub fn truncate_string(s: &str, limit: usize) -> String {
|
||||||
|
if s.len() <= limit {
|
||||||
|
return s.to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
// For very small limits, just truncate without ellipsis
|
||||||
|
if limit < 5 {
|
||||||
|
return s.chars().take(limit).collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
// For limits 5 and above, use start + "..." + end pattern
|
||||||
|
// Strategy: Use 3 chars for ellipsis, split remaining between start and end
|
||||||
|
// But ensure we get meaningful chunks from both ends
|
||||||
|
|
||||||
|
let available_for_content = limit - 3; // Reserve 3 for "..."
|
||||||
|
|
||||||
|
// Determine start and end characters based on available space
|
||||||
|
let (start_chars, end_chars) = if available_for_content <= 4 {
|
||||||
|
// Very limited space: minimal start, rest for end
|
||||||
|
(1, available_for_content - 1)
|
||||||
|
} else if available_for_content <= 6 {
|
||||||
|
// Medium space: balanced approach
|
||||||
|
let start = available_for_content / 2;
|
||||||
|
(start, available_for_content - start)
|
||||||
|
} else {
|
||||||
|
// Plenty of space: cap start at 3, use more for end to preserve context
|
||||||
|
let start = 3;
|
||||||
|
(start, available_for_content - start)
|
||||||
|
};
|
||||||
|
|
||||||
|
let start: String = s.chars().take(start_chars).collect();
|
||||||
|
let end: String = s
|
||||||
|
.chars()
|
||||||
|
.rev()
|
||||||
|
.take(end_chars)
|
||||||
|
.collect::<String>()
|
||||||
|
.chars()
|
||||||
|
.rev()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
format!("{}...{}", start, end)
|
||||||
|
}
|
||||||
105
src-tauri/tests/strings.rs
Normal file
105
src-tauri/tests/strings.rs
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
use byte_me_lib::strings::{transform_filename, truncate_string};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_transform_filename() {
|
||||||
|
// Test cases focusing on practical, readable outputs
|
||||||
|
|
||||||
|
// 1. Short filenames should remain unchanged
|
||||||
|
assert_eq!(transform_filename("test.mp4", 20), "test.mp4");
|
||||||
|
assert_eq!(transform_filename("short.txt", 15), "short.txt");
|
||||||
|
assert_eq!(transform_filename("a.b", 10), "a.b");
|
||||||
|
|
||||||
|
// 2. No extension cases - preserve meaningful start and end
|
||||||
|
assert_eq!(transform_filename("short_name", 15), "short_name");
|
||||||
|
assert_eq!(
|
||||||
|
transform_filename("very_long_document_name", 15),
|
||||||
|
"ver...ment_name"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
transform_filename("medium_length_name", 13),
|
||||||
|
"med...th_name"
|
||||||
|
);
|
||||||
|
|
||||||
|
// 3. Normal extension cases (preserving extension)
|
||||||
|
assert_eq!(
|
||||||
|
transform_filename("very_long_video_file_name.mp4", 18),
|
||||||
|
"ver...ile_name.mp4"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
transform_filename("document_with_long_name.pdf", 15),
|
||||||
|
"doc..._name.pdf"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
transform_filename("image_file_name.jpeg", 15),
|
||||||
|
"ima...name.jpeg"
|
||||||
|
);
|
||||||
|
|
||||||
|
// 4. Numeric sequences (like user's example) - preserve start and end numbers
|
||||||
|
assert_eq!(
|
||||||
|
transform_filename("43509374693.TS.mp4", 15),
|
||||||
|
"435...93.TS.mp4"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
transform_filename("20231201_video.mp4", 15),
|
||||||
|
"202...video.mp4"
|
||||||
|
);
|
||||||
|
assert_eq!(transform_filename("file_v2.1.3.tar", 12), "fi...1.3.tar");
|
||||||
|
|
||||||
|
// 5. Long extensions (treated as part of filename)
|
||||||
|
assert_eq!(
|
||||||
|
transform_filename("file.verylongextension", 15),
|
||||||
|
"fil...extension"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
transform_filename("document.backup_old", 15),
|
||||||
|
"doc...ackup_old"
|
||||||
|
);
|
||||||
|
|
||||||
|
// 6. Edge cases
|
||||||
|
assert_eq!(transform_filename("", 10), "");
|
||||||
|
assert_eq!(transform_filename("a", 0), "");
|
||||||
|
assert_eq!(transform_filename("test", 4), "test");
|
||||||
|
assert_eq!(transform_filename("test", 3), "tes");
|
||||||
|
assert_eq!(transform_filename("ab", 2), "ab");
|
||||||
|
|
||||||
|
// 7. Very short limits - graceful degradation
|
||||||
|
assert_eq!(transform_filename("test.mp4", 8), "test.mp4");
|
||||||
|
assert_eq!(transform_filename("verylongname", 8), "ve...ame");
|
||||||
|
assert_eq!(transform_filename("test.mp4", 7), "tes.mp4");
|
||||||
|
assert_eq!(transform_filename("hello.txt", 9), "hello.txt");
|
||||||
|
|
||||||
|
// 8. Extension edge cases
|
||||||
|
assert_eq!(transform_filename("file.", 10), "file.");
|
||||||
|
assert_eq!(transform_filename(".hidden", 10), ".hidden");
|
||||||
|
assert_eq!(transform_filename("test.a", 10), "test.a");
|
||||||
|
|
||||||
|
// 9. Real-world examples
|
||||||
|
assert_eq!(
|
||||||
|
transform_filename("IMG_20231201_143022.jpg", 15),
|
||||||
|
"IMG...43022.jpg"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
transform_filename("meeting_recording_final_v2.mp4", 20),
|
||||||
|
"mee...g_final_v2.mp4"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
transform_filename("my document (copy).docx", 15),
|
||||||
|
"my ...opy).docx"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_truncate_string() {
|
||||||
|
// Test the helper function directly
|
||||||
|
assert_eq!(truncate_string("hello", 10), "hello");
|
||||||
|
assert_eq!(truncate_string("hello", 5), "hello");
|
||||||
|
assert_eq!(truncate_string("hello_world", 8), "he...rld");
|
||||||
|
assert_eq!(truncate_string("test", 4), "test");
|
||||||
|
assert_eq!(truncate_string("test", 3), "tes");
|
||||||
|
assert_eq!(truncate_string("ab", 2), "ab");
|
||||||
|
assert_eq!(truncate_string("a", 1), "a");
|
||||||
|
assert_eq!(truncate_string("hello", 1), "h");
|
||||||
|
assert_eq!(truncate_string("hello", 0), "");
|
||||||
|
assert_eq!(truncate_string("very_long_name", 10), "ver...name");
|
||||||
|
assert_eq!(truncate_string("document_name", 9), "doc...ame");
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user