diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-08-17 04:45:53 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-08-17 04:45:53 +0900 |
| commit | bc79875f9a0cbacce94722fede76228b06486269 (patch) | |
| tree | a80f31d8e0713697286359b7c414994f478ddb03 /crates/shirabe-php-shim | |
| parent | 2337aa8225b1452e2ec65c122e4b6e8b54f3ea7e (diff) | |
| download | php-shirabe-bc79875f9a0cbacce94722fede76228b06486269.tar.gz php-shirabe-bc79875f9a0cbacce94722fede76228b06486269.tar.zst php-shirabe-bc79875f9a0cbacce94722fede76228b06486269.zip | |
refactor(php-shim): split single_match_map() by unmatched_as_null
Each branch of the flag is now its own function. The PREG_UNMATCHED_AS_NULL
variant needs neither the trailing-group truncation nor the empty-string
fallback, so it loses the break condition and the last_participating scan.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe-php-shim')
| -rw-r--r-- | crates/shirabe-php-shim/src/preg.rs | 41 |
1 files changed, 28 insertions, 13 deletions
diff --git a/crates/shirabe-php-shim/src/preg.rs b/crates/shirabe-php-shim/src/preg.rs index 62ad464c..1156129a 100644 --- a/crates/shirabe-php-shim/src/preg.rs +++ b/crates/shirabe-php-shim/src/preg.rs @@ -76,7 +76,11 @@ pub fn preg_match2( matches.clear(); if let Some(caps) = &caps { let names: Vec<Option<&str>> = re.capture_names().collect(); - *matches = single_match_map(caps, &names, unmatched_as_null); + *matches = if unmatched_as_null { + single_match_map_unmatched_as_null(caps, &names) + } else { + single_match_map(caps, &names) + }; } caps.is_some() @@ -291,7 +295,7 @@ where for caps in re.captures_iter(subject) { let m = caps.get(0).unwrap(); out.extend_from_slice(&subject.as_bytes()[last..m.start()]); - let map = single_match_map(&caps, &names, false); + let map = single_match_map(&caps, &names); out.extend_from_slice(callback(&map)?.as_bytes()); last = m.end(); } @@ -578,13 +582,11 @@ fn php_match_row(caps: ®ex::Captures) -> Vec<String> { } // Builds a single match's `$matches` map with both named and numbered keys -// (the named key precedes its number). With PREG_UNMATCHED_AS_NULL, every group -// is present and non-participating ones are None; otherwise classic semantics -// apply: trailing unmatched groups are dropped and interior ones become "". +// (the named key precedes its number). Trailing unmatched groups are dropped +// and interior ones become "". fn single_match_map( caps: ®ex::Captures, names: &[Option<&str>], - unmatched_as_null: bool, ) -> indexmap::IndexMap<CaptureKey, Option<String>> { let mut out = indexmap::IndexMap::new(); let group_count = caps.len(); @@ -592,18 +594,31 @@ fn single_match_map( for i in 0..group_count { let m = caps.get(i); - if !unmatched_as_null - && m.is_none() + if m.is_none() && let Some(last) = last_participating && i > last { break; } - let value = if unmatched_as_null { - m.map(|m| m.as_str().to_string()) - } else { - Some(m.map(|m| m.as_str().to_string()).unwrap_or_default()) - }; + let value = Some(m.map(|m| m.as_str().to_string()).unwrap_or_default()); + if let Some(Some(name)) = names.get(i) { + out.insert(CaptureKey::ByName((*name).to_string()), value.clone()); + } + out.insert(CaptureKey::ByIndex(i), value); + } + out +} + +// PREG_UNMATCHED_AS_NULL counterpart of single_match_map(): every group is +// present and non-participating ones are None. +fn single_match_map_unmatched_as_null( + caps: ®ex::Captures, + names: &[Option<&str>], +) -> indexmap::IndexMap<CaptureKey, Option<String>> { + let mut out = indexmap::IndexMap::new(); + + for i in 0..caps.len() { + let value = caps.get(i).map(|m| m.as_str().to_string()); if let Some(Some(name)) = names.get(i) { out.insert(CaptureKey::ByName((*name).to_string()), value.clone()); } |
