From 9a31fe8295b36215bf7a59c97b8f06aabdaeaf41 Mon Sep 17 00:00:00 2001 From: cucadmuh <49571317+cucadmuh@users.noreply.github.com> Date: Mon, 22 Jun 2026 03:12:45 +0300 Subject: [PATCH] fix(library): avoid UTF-8 panic in artist article strip (#1152) --- .../crates/psysonic-library/src/artist_sort.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src-tauri/crates/psysonic-library/src/artist_sort.rs b/src-tauri/crates/psysonic-library/src/artist_sort.rs index df39eab1..a94511d1 100644 --- a/src-tauri/crates/psysonic-library/src/artist_sort.rs +++ b/src-tauri/crates/psysonic-library/src/artist_sort.rs @@ -8,9 +8,10 @@ pub fn strip_leading_articles(name: &str, ignored_articles: &str) -> String { let trimmed = name.trim(); for article in ignored_articles.split(' ').filter(|s| !s.is_empty()) { let prefix = format!("{} ", article); - if trimmed.len() >= prefix.len() - && trimmed[..prefix.len()].eq_ignore_ascii_case(&prefix) - { + // `prefix` is ASCII; use `get` so we never slice inside a multibyte rune + // (e.g. "Elə…" must not panic when probing the "El " article). + let head = trimmed.get(0..prefix.len()); + if head.is_some_and(|h| h.eq_ignore_ascii_case(&prefix)) { return trimmed[prefix.len()..].trim_start().to_string(); } } @@ -60,4 +61,12 @@ mod tests { "beatles" ); } + + #[test] + fn does_not_panic_when_article_prefix_aligns_with_multibyte_rune() { + // Regression: byte slice `trimmed[..prefix.len()]` panicked on "Elə…" + // when probing the "El " ignored article (ə spans bytes 2..4). + let key = sort_key_for_display_name("Eləmir", DEFAULT_IGNORED_ARTICLES); + assert_eq!(key, "eləmir"); + } }