diff --git a/src/git.rs b/src/git.rs index aac48ca69..d883a2d62 100644 --- a/src/git.rs +++ b/src/git.rs @@ -60,8 +60,10 @@ impl GitCache { match repo.statuses(None) { Ok(status_list) => { for status_entry in status_list.iter() { - // git2-rs provides / separated path even on Windows. We have to rebuild it - let str_path = status_entry.path().unwrap(); + // git2 returns None for non-utf8 paths, skip them + let Some(str_path) = status_entry.path() else { + continue; + }; let path: PathBuf = str_path.split('/').collect::>().iter().collect(); let path = workdir.join(path); diff --git a/src/meta/owner.rs b/src/meta/owner.rs index ca04023fa..0f708d959 100644 --- a/src/meta/owner.rs +++ b/src/meta/owner.rs @@ -50,8 +50,9 @@ fn truncate(input: &str, after: Option, marker: Option) -> String let mut output = input.to_string(); if let Some(after) = after { - if output.len() > after { - output.truncate(after); + // after is a char count, find the matching byte index + if let Some((byte_idx, _)) = output.char_indices().nth(after) { + output.truncate(byte_idx); if let Some(marker) = marker { output.push_str(&marker); @@ -134,4 +135,19 @@ mod test_truncate { fn test_truncated_with_marker() { assert_eq!("a…", truncate("ab", Some(1), Some("…".to_string()))); } + + #[test] + fn test_truncated_at_multibyte_char_boundary() { + assert_eq!("é", truncate("éb", Some(1), None)); + } + + #[test] + fn test_truncated_multibyte_with_marker() { + assert_eq!("é…", truncate("éb", Some(1), Some("…".to_string()))); + } + + #[test] + fn test_no_truncation_when_shorter_multibyte() { + assert_eq!("é", truncate("é", Some(2), Some("…".to_string()))); + } } diff --git a/src/meta/symlink.rs b/src/meta/symlink.rs index 786b49202..6d594c13a 100644 --- a/src/meta/symlink.rs +++ b/src/meta/symlink.rs @@ -15,22 +15,12 @@ impl From<&Path> for SymLink { if target.is_absolute() || path.parent().is_none() { return Self { valid: target.exists(), - target: Some( - target - .to_str() - .expect("failed to convert symlink to str") - .to_string(), - ), + target: Some(target.to_string_lossy().into_owned()), }; } return Self { - target: Some( - target - .to_str() - .expect("failed to convert symlink to str") - .to_string(), - ), + target: Some(target.to_string_lossy().into_owned()), valid: path.parent().unwrap().join(target).exists(), }; }