diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-08-17 07:36:34 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-08-17 07:36:34 +0900 |
| commit | 9fd6aecad27240ccedab4487f6c157914142ca47 (patch) | |
| tree | 3819360771b9eb9298315e26604cafabeae84e04 /crates/shirabe-pcre/src | |
| parent | 6b4ce98f20b3cfc14f1c955565a8acd9abcc16a2 (diff) | |
| download | php-shirabe-9fd6aecad27240ccedab4487f6c157914142ca47.tar.gz php-shirabe-9fd6aecad27240ccedab4487f6c157914142ca47.tar.zst php-shirabe-9fd6aecad27240ccedab4487f6c157914142ca47.zip | |
refactor(preg): return the preg_* $matches instead of filling an out-param
PHP fills `$matches` through a by-ref parameter, which the port mirrored
with a `&mut` out-param plus a bool or count return. Every caller then
had to declare an empty binding one line ahead of the call, and nothing
in the type said the binding is only meaningful when the call succeeded.
Return the matches instead: preg_match() and preg_match2() hand back an
Option, and the three preg_match_all* functions hand back the collection
they used to fill.
The occurrence count the two map-shaped preg_match_all* functions used
to return is the length of any one of the map's columns, so it is not
lost -- Preg::match_all() and friends derive it via occurrence_count().
preg_replace2() keeps its `count: Option<&mut usize>`: that one is not
derivable from the replaced string, and callers that do not want it pay
nothing for passing None.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe-pcre/src')
| -rw-r--r-- | crates/shirabe-pcre/src/preg.rs | 58 |
1 files changed, 28 insertions, 30 deletions
diff --git a/crates/shirabe-pcre/src/preg.rs b/crates/shirabe-pcre/src/preg.rs index aa986ae8..dd6e4950 100644 --- a/crates/shirabe-pcre/src/preg.rs +++ b/crates/shirabe-pcre/src/preg.rs @@ -46,19 +46,20 @@ impl Preg { matches: Option<&mut PregMatchedGroups>, offset: usize, ) -> bool { - let mut internal = PregMatches::new(); - let result = preg_match2(pattern, subject, &mut internal, offset); + let internal = preg_match2(pattern, subject, offset); if let Some(out) = matches { - *out = drop_null_matches(internal); + *out = match &internal { + Some(internal) => drop_null_matches(internal), + None => PregMatchedGroups::new(), + }; } - result + internal.is_some() } pub fn match_all(pattern: impl PregPattern, subject: &str) -> usize { - let mut dummy = PregMatchesAll::new(); - preg_match_all2(pattern, subject, &mut dummy) + occurrence_count(&preg_match_all2(pattern, subject)) } pub fn match_all2( @@ -66,7 +67,8 @@ impl Preg { subject: &str, matches: &mut PregMatchesAll, ) -> usize { - preg_match_all2(pattern, subject, matches) + *matches = preg_match_all2(pattern, subject); + occurrence_count(matches) } fn match_all_with_offsets5( @@ -74,14 +76,14 @@ impl Preg { subject: &str, matches: Option<&mut PregMatchesAllWithOffsets>, ) -> usize { - let mut internal = PregMatchesAllWithOffsets::new(); - let result = preg_match_all_offset_capture(pattern, subject, &mut internal); + let internal = preg_match_all_offset_capture(pattern, subject); + let count = internal[&CaptureKey::ByIndex(0)].len(); if let Some(out) = matches { *out = internal; } - result + count } pub fn replace(pattern: impl PregPattern, replacement: &str, subject: &str) -> String { @@ -112,7 +114,7 @@ impl Preg { mut replacement: F, subject: &str, ) -> String { - let adapter = |internal: &PregMatches| Ok(replacement(&drop_null_matches_ref(internal))); + let adapter = |internal: &PregMatches| Ok(replacement(&drop_null_matches(internal))); preg_replace_callback(pattern, adapter, subject).expect("$replacement cannot fail") } @@ -150,13 +152,15 @@ impl Preg { subject: &str, matches: &mut PregNamedGroups, ) -> bool { - let mut internal = PregMatches::new(); - let result = preg_match2(pattern, subject, &mut internal, 0); + let internal = preg_match2(pattern, subject, 0); + let result = internal.is_some(); matches.clear(); - for (key, value) in internal { - if let (CaptureKey::ByName(name), Some(value)) = (key, value) { - matches.insert(name, value); + if let Some(internal) = internal { + for (key, value) in internal { + if let (CaptureKey::ByName(name), Some(value)) = (key, value) { + matches.insert(name, value); + } } } @@ -169,13 +173,8 @@ impl Preg { pattern: impl PregPattern, subject: &str, ) -> Option<Vec<Option<String>>> { - let mut internal = PregMatches::new(); - if !preg_match2(pattern, subject, &mut internal, 0) { - return None; - } - Some( - internal + preg_match2(pattern, subject, 0)? .into_iter() .filter_map(|(key, value)| match key { CaptureKey::ByIndex(_) => Some(value), @@ -204,16 +203,15 @@ impl Preg { // Drops `null` (unmatched) groups, mirroring how the public `string`-valued // `matches` map represents PHP's `string|null` entries by their absence. -fn drop_null_matches(matches: PregMatches) -> PregMatchedGroups { - matches - .into_iter() - .filter_map(|(key, value)| value.map(|value| (key, value))) - .collect() -} - -fn drop_null_matches_ref(matches: &PregMatches) -> PregMatchedGroups { +fn drop_null_matches(matches: &PregMatches) -> PregMatchedGroups { matches .iter() .filter_map(|(key, value)| value.clone().map(|value| (key.clone(), value))) .collect() } + +// PHP's `preg_match_all` returns the number of occurrences; every column of a +// PREG_PATTERN_ORDER map holds one entry per occurrence. +fn occurrence_count(matches: &PregMatchesAll) -> usize { + matches[&CaptureKey::ByIndex(0)].len() +} |
