/
magnusroot
/
nm
Обзор
Документация
Войти
/
magnusroot
/
nm
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
crates/textfix/src/lib.rs
77 строк
4 KB
Magnus Root
Some fixes
13 июл 2026, 09:31
13 июл 2026, 09:31
e6c694a
Код
Авторство
О чём код?
//! Исправление "кракозябр" в тегах — общая логика для `library` (сканирование библиотеки) //! и `player` (чтение тегов текущего трека для "Now Playing"). Раньше жила только в одном //! из двух мест, из-за чего кракозябры чинились в списке библиотеки, но не в панели //! Now Playing при реальном воспроизведении — вынесено сюда, чтобы такого больше не было. /// Чинит частый случай со старыми (обычно ID3v1, но встречается и в ID3v2) тегами, где /// кириллица была записана в CP1251, а прочитана так, будто это Latin-1/ISO-8859-1 /// (типичное поведение множества старых тегеров и библиотек метаданных). Раз символы /// результата — это однозначно code points ≤0xFF (свойство Latin-1-декодирования), /// исходные байты восстанавливаются 1:1, и остаётся просто раскодировать их как CP1251. /// Если результат не выглядит правдоподобно кириллическим — возвращаем строку как есть. pub fn fix_mojibake(s: &str) -> String { if s.is_ascii() { return s.to_string(); } let bytes: Option<Vec<u8>> = s .chars() .map(|c| { let cp = c as u32; if cp <= 0xFF { Some(cp as u8) } else { None } }) .collect(); let Some(bytes) = bytes else { return s.to_string(); // содержит символы вне Latin-1 — это не наш случай, не трогаем }; let (decoded, _, had_errors) = encoding_rs::WINDOWS_1251.decode(&bytes); if had_errors { return s.to_string(); } let cyrillic_count = decoded .chars() .filter(|c| ('А'..='я').contains(c) || *c == 'Ё' || *c == 'ё') .count(); let total_alpha = decoded.chars().filter(|c| c.is_alphabetic()).count().max(1); if cyrillic_count * 2 >= total_alpha { decoded.into_owned() } else { s.to_string() } } #[cfg(test)] mod tests { use super::*; #[test] fn fixes_cp1251_mojibake() { // "Ïåñíÿ" — это ровно то, что получается, когда CP1251-байты слова "Песня" // прочитаны как Latin-1 (типичное поведение старых ID3v1-тегов). assert_eq!(fix_mojibake("Ïåñíÿ"), "Песня"); assert_eq!(fix_mojibake("Àðòèñò"), "Артист"); } #[test] fn fixes_real_world_example_from_bug_report() { // Реальный случай из багрепорта: старый mp3, поймано в Now Playing, но не // в библиотеке — то есть именно то расхождение, ради которого этот крейт создан. assert_eq!(fix_mojibake("Ìàëü÷èøíèê"), "Мальчишник"); } #[test] fn leaves_plain_ascii_untouched() { assert_eq!(fix_mojibake("Hello World"), "Hello World"); } #[test] fn leaves_already_correct_cyrillic_untouched() { // уже нормальный UTF-8 текст не должен портиться повторной перекодировкой assert_eq!(fix_mojibake("Пример"), "Пример"); } }