From 7c393d719b101601b8f6d994d43793dfc2037b8e Mon Sep 17 00:00:00 2001 From: Kuya KOHARA Date: Sun, 4 Oct 2026 16:15:20 +0900 Subject: [PATCH] fix(git-whose): fix panic bug when parsing comment --- src/github/codeowners.rs | 43 +++++++++++++++++++++++++++++++++++----- tests/codeowners.rs | 18 +++++++++++++++++ 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/src/github/codeowners.rs b/src/github/codeowners.rs index 0220e17..23e4c5f 100644 --- a/src/github/codeowners.rs +++ b/src/github/codeowners.rs @@ -33,11 +33,9 @@ impl TryFrom for Record { type Error = CodeOwnersEntryError; fn try_from(value: String) -> Result { - let value = if let Some((i, _)) = value.chars().enumerate().find(|(_, c)| c == &'#') { - &value[0..i] - } else { - &value[..] - }; + let value = value + .split_once('#') + .map_or(value.as_str(), |(line, _)| line); let mut iter = value.split_whitespace(); if let Some(pat) = iter.next() { @@ -93,6 +91,41 @@ mod tests { owners: vec!["@foo".to_string(), "@bar".to_string()], }), ), + ( + "é# comment", + Ok(Record { + pattern: "é".to_string(), + owners: vec![], + }), + ), + ( + "日本語/ @owner # comment", + Ok(Record { + pattern: "日本語/".to_string(), + owners: vec!["@owner".to_string()], + }), + ), + ( + "* @日本語 @😀 # comment", + Ok(Record { + pattern: "*".to_string(), + owners: vec!["@日本語".to_string(), "@😀".to_string()], + }), + ), + ( + "😀/ # comment", + Ok(Record { + pattern: "😀/".to_string(), + owners: vec![], + }), + ), + ( + "日本語/ @日本語", + Ok(Record { + pattern: "日本語/".to_string(), + owners: vec!["@日本語".to_string()], + }), + ), ]; for (i, (input, want)) in test_cases.into_iter().enumerate() { diff --git a/tests/codeowners.rs b/tests/codeowners.rs index 56c2e24..f4fea95 100644 --- a/tests/codeowners.rs +++ b/tests/codeowners.rs @@ -175,3 +175,21 @@ fn codeowner_debug_marks_only_last_match_effective() { assert!(matches[2].is_effective()); assert_eq!(matches[2].owners(), &vec![String::from("@lib-team")]); } + +#[test] +fn codeowner_unicode_before_inline_comment_preserves_owners_and_precedence() { + let data = "* @default\n日本語/ @team @日本語 # comment\n日本語/非公開/ # no owners\n"; + let co = CodeOwners::<()>::try_from_bufread(data.as_bytes()).unwrap(); + + let owners = vec![String::from("@team"), String::from("@日本語")]; + assert_eq!(co.find_owners("日本語/file.rs"), Some(&owners)); + assert_eq!(co.find_owners("日本語/非公開/file.rs"), Some(&vec![])); + + let matches = co.debug("日本語/非公開/file.rs").collect::>(); + assert_eq!(matches.len(), 3); + assert!(!matches[0].is_effective()); + assert!(!matches[1].is_effective()); + assert_eq!(matches[1].owners(), &owners); + assert!(matches[2].is_effective()); + assert!(matches[2].owners().is_empty()); +}