aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src/downloader
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-08-17 03:09:04 +0900
committernsfisis <nsfisis@gmail.com>2026-08-17 03:09:04 +0900
commit8b57e90f5c563bbc1a6060d9f5e94b5d3cd91324 (patch)
treea8052aebf264e3cf31c25e2bc5cb612dadde485b /crates/shirabe/src/downloader
parentfcc6ee5a13cd2e2b27fff66dcfc7e9e1f062a653 (diff)
downloadphp-shirabe-8b57e90f5c563bbc1a6060d9f5e94b5d3cd91324.tar.gz
php-shirabe-8b57e90f5c563bbc1a6060d9f5e94b5d3cd91324.tar.zst
php-shirabe-8b57e90f5c563bbc1a6060d9f5e94b5d3cd91324.zip
fix(pcre): preserve unmatched groups in Preg::match_all*()
PHP's Preg::matchAll() and matchAllWithOffsets() always set PREG_UNMATCHED_AS_NULL, so a non-participating group is `null` and its offset is -1. The Rust wrappers collapsed those to "" and 0, so callers could not tell a group that did not participate from one that matched an empty string at offset 0, and the offset value matched no PHP mode at all. Hand the shim's representation through unchanged and let each caller mirror what the PHP original does with it: `isset()` and `(string)` casts stay lenient, while `assert(is_string(...))` and the *StrictGroups() variants become `expect()`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe/src/downloader')
-rw-r--r--crates/shirabe/src/downloader/git_downloader.rs15
1 files changed, 10 insertions, 5 deletions
diff --git a/crates/shirabe/src/downloader/git_downloader.rs b/crates/shirabe/src/downloader/git_downloader.rs
index 4394cc95..b34f7800 100644
--- a/crates/shirabe/src/downloader/git_downloader.rs
+++ b/crates/shirabe/src/downloader/git_downloader.rs
@@ -109,7 +109,7 @@ impl GitDownloader {
.cloned()
.unwrap_or_default();
- let mut branches_match: IndexMap<CaptureKey, Vec<String>> = IndexMap::new();
+ let mut branches_match: IndexMap<CaptureKey, Vec<Option<String>>> = IndexMap::new();
if !Preg::is_match_all3(
format!("{{^{} refs/heads/(.+)$}}mi", preg_quote(&head_ref, None)),
&refs,
@@ -121,7 +121,10 @@ impl GitDownloader {
let candidate_branches: Vec<String> = branches_match
.get(&CaptureKey::ByIndex(1))
.cloned()
- .unwrap_or_default();
+ .unwrap_or_default()
+ .into_iter()
+ .map(|branch| branch.expect("group 1 participates whenever the pattern matches"))
+ .collect();
// use the first match as branch name for now
let mut branch = candidate_branches[0].clone();
@@ -134,7 +137,7 @@ impl GitDownloader {
// try to find matching branch names in remote repos
for candidate in &candidate_branches {
- let mut m: IndexMap<CaptureKey, Vec<String>> = IndexMap::new();
+ let mut m: IndexMap<CaptureKey, Vec<Option<String>>> = IndexMap::new();
if Preg::is_match_all3(
format!(
"{{^[a-f0-9]+ refs/remotes/((?:[^/]+)/{})$}}mi",
@@ -143,11 +146,13 @@ impl GitDownloader {
&refs,
Some(&mut m),
) {
- let matches: Vec<String> =
+ let matches: Vec<Option<String>> =
m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default();
for match_ in matches {
branch = candidate.clone();
- remote_branches.push(match_);
+ remote_branches.push(
+ match_.expect("group 1 participates whenever the pattern matches"),
+ );
}
break;
}