diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-08-09 11:10:53 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-08-09 11:10:53 +0900 |
| commit | 66c3eba15ba6302d43de057a9063f7feee8c6fb3 (patch) | |
| tree | d44bf5cec4d9ddfffa1a732dba85418c2873de82 /crates/shirabe-external-packages | |
| parent | 0b737de495e9a8530a6a32ce2799dc49f3e5786f (diff) | |
| download | php-shirabe-66c3eba15ba6302d43de057a9063f7feee8c6fb3.tar.gz php-shirabe-66c3eba15ba6302d43de057a9063f7feee8c6fb3.tar.zst php-shirabe-66c3eba15ba6302d43de057a9063f7feee8c6fb3.zip | |
refactor(pcre): extract composer/pcre into the shirabe-pcre crate
Move `Composer\Pcre` out of shirabe-external-packages and into its own
crate, so the path is `shirabe_pcre::preg::Preg` instead of
`shirabe_external_packages::composer::pcre::preg::Preg`.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe-external-packages')
9 files changed, 6 insertions, 377 deletions
diff --git a/crates/shirabe-external-packages/Cargo.toml b/crates/shirabe-external-packages/Cargo.toml index f274eac9..331efcb7 100644 --- a/crates/shirabe-external-packages/Cargo.toml +++ b/crates/shirabe-external-packages/Cargo.toml @@ -4,6 +4,7 @@ version.workspace = true edition.workspace = true [dependencies] +shirabe-pcre.workspace = true shirabe-php-shim.workspace = true shirabe-semver.workspace = true anyhow.workspace = true diff --git a/crates/shirabe-external-packages/src/composer.rs b/crates/shirabe-external-packages/src/composer.rs index 907f9852..da1ab285 100644 --- a/crates/shirabe-external-packages/src/composer.rs +++ b/crates/shirabe-external-packages/src/composer.rs @@ -1,7 +1,5 @@ pub mod ca_bundle; -pub mod pcre; pub mod xdebug_handler; pub use ca_bundle::*; -pub use pcre::*; pub use xdebug_handler::*; diff --git a/crates/shirabe-external-packages/src/composer/pcre.rs b/crates/shirabe-external-packages/src/composer/pcre.rs deleted file mode 100644 index 20c37d02..00000000 --- a/crates/shirabe-external-packages/src/composer/pcre.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod preg; - -pub use preg::*; diff --git a/crates/shirabe-external-packages/src/composer/pcre/preg.rs b/crates/shirabe-external-packages/src/composer/pcre/preg.rs deleted file mode 100644 index 06586d2c..00000000 --- a/crates/shirabe-external-packages/src/composer/pcre/preg.rs +++ /dev/null @@ -1,367 +0,0 @@ -//! ref: composer/vendor/composer/pcre/src/Preg.php -//! -//! The following two exception classes are intentionally not ported: -//! -//! - `PcreException`: thrown when a `preg_*()` call returns false. Composer never feeds a pattern -//! that fails to compile at runtime, so such a failure would be a programming error rather than -//! a recoverable condition; they panic instead. -//! - `UnexpectedNullMatchException`: thrown by the `Preg::*StrictGroups()` variants when a capture -//! group did not participate. Those variants were dropped because Rust's `Option` already -//! distinguishes participating from non-participating groups. -//! -//! See docs/dev/regex-porting.md for more detailed regex porting rules. - -use indexmap::IndexMap; -pub use shirabe_php_shim::CaptureKey; -use shirabe_php_shim::{ - PREG_OFFSET_CAPTURE, PREG_SET_ORDER, PREG_SPLIT_OFFSET_CAPTURE, PREG_UNMATCHED_AS_NULL, - PregPattern, -}; - -#[derive(Debug)] -pub struct Preg; - -impl Preg { - pub fn match3( - pattern: impl PregPattern, - subject: &str, - matches: Option<&mut IndexMap<CaptureKey, String>>, - ) -> bool { - Self::match5(pattern, subject, matches, 0, 0) - } - - pub fn match5( - pattern: impl PregPattern, - subject: &str, - matches: Option<&mut IndexMap<CaptureKey, String>>, - flags: i64, - offset: usize, - ) -> bool { - Self::check_offset_capture(flags, "matchWithOffsets"); - - let mut internal: IndexMap<CaptureKey, Option<String>> = IndexMap::new(); - let result = shirabe_php_shim::preg_match2( - pattern, - subject, - &mut internal, - flags | PREG_UNMATCHED_AS_NULL, - offset, - ); - - if let Some(out) = matches { - *out = drop_null_matches(internal); - } - - result - } - - pub fn match_all(pattern: impl PregPattern, subject: &str) -> usize { - Self::match_all5(pattern, subject, None, 0, 0) - } - - pub fn match_all3( - pattern: impl PregPattern, - subject: &str, - matches: Option<&mut IndexMap<CaptureKey, Vec<String>>>, - ) -> usize { - Self::match_all5(pattern, subject, matches, 0, 0) - } - - pub fn match_all5( - pattern: impl PregPattern, - subject: &str, - matches: Option<&mut IndexMap<CaptureKey, Vec<String>>>, - flags: i64, - offset: usize, - ) -> usize { - Self::check_offset_capture(flags, "matchAllWithOffsets"); - Self::check_set_order(flags); - - let mut internal: IndexMap<CaptureKey, Vec<Option<String>>> = IndexMap::new(); - let result = shirabe_php_shim::preg_match_all2( - pattern, - subject, - &mut internal, - flags | PREG_UNMATCHED_AS_NULL, - offset, - ); - - if let Some(out) = matches { - *out = null_to_empty_match_all(internal); - } - - result - } - - pub fn match_all_with_offsets5( - pattern: impl PregPattern, - subject: &str, - matches: Option<&mut IndexMap<CaptureKey, Vec<(String, usize)>>>, - flags: i64, - offset: usize, - ) -> usize { - Self::check_set_order(flags); - - let mut internal: IndexMap<CaptureKey, Vec<(Option<String>, i64)>> = IndexMap::new(); - let result = shirabe_php_shim::preg_match_all_offset_capture2( - pattern, - subject, - &mut internal, - flags | PREG_UNMATCHED_AS_NULL | PREG_OFFSET_CAPTURE, - offset, - ); - - if let Some(out) = matches { - *out = null_to_empty_offset_match_all(internal); - } - - result - } - - pub fn replace(pattern: impl PregPattern, replacement: &str, subject: &str) -> String { - Self::replace_impl(pattern, replacement, subject, -1, None) - } - - pub fn replace4( - pattern: impl PregPattern, - replacement: &str, - subject: &str, - limit: i64, - ) -> String { - Self::replace_impl(pattern, replacement, subject, limit, None) - } - - pub fn replace5( - pattern: impl PregPattern, - replacement: &str, - subject: &str, - limit: i64, - count: &mut usize, - ) -> String { - Self::replace_impl(pattern, replacement, subject, limit, Some(count)) - } - - fn replace_impl( - pattern: impl PregPattern, - replacement: &str, - subject: &str, - limit: i64, - count: Option<&mut usize>, - ) -> String { - // `$subject` is statically a string here, so the is_scalar/is_array - // guards (ARRAY_MSG / INVALID_TYPE_MSG) of the PHP original are - // unreachable and not reproduced. - shirabe_php_shim::preg_replace2(pattern, replacement, subject, limit, count) - } - - pub fn replace_callback<F: FnMut(&IndexMap<CaptureKey, String>) -> String>( - pattern: impl PregPattern, - replacement: F, - subject: &str, - ) -> String { - Self::replace_callback6(pattern, replacement, subject, -1, None, 0) - } - - pub fn replace_callback6<F: FnMut(&IndexMap<CaptureKey, String>) -> String>( - pattern: impl PregPattern, - mut replacement: F, - subject: &str, - limit: i64, - count: Option<&mut usize>, - flags: i64, - ) -> String { - let adapter = |internal: &IndexMap<CaptureKey, Option<String>>| -> String { - replacement(&drop_null_matches_ref(internal)) - }; - - shirabe_php_shim::preg_replace_callback2(pattern, adapter, subject, limit, count, flags) - } - - pub fn split(pattern: impl PregPattern, subject: &str) -> Vec<String> { - Self::split4(pattern, subject, -1, 0) - } - - pub fn split4(pattern: impl PregPattern, subject: &str, limit: i64, flags: i64) -> Vec<String> { - assert!( - flags & PREG_SPLIT_OFFSET_CAPTURE == 0, - "PREG_SPLIT_OFFSET_CAPTURE is not supported as it changes the type of $matches, use splitWithOffsets() instead" - ); - - shirabe_php_shim::preg_split2(pattern, subject, limit, flags) - } - - pub fn grep(pattern: impl PregPattern, array: &[&str]) -> Vec<String> { - Self::grep3(pattern, array, 0) - } - - pub fn grep3(pattern: impl PregPattern, array: &[&str], flags: i64) -> Vec<String> { - shirabe_php_shim::preg_grep2(pattern, array, flags) - } - - pub fn is_match(pattern: impl PregPattern, subject: &str) -> bool { - Self::match5(pattern, subject, None, 0, 0) - } - - pub fn is_match3( - pattern: impl PregPattern, - subject: &str, - matches: Option<&mut IndexMap<CaptureKey, String>>, - ) -> bool { - Self::match5(pattern, subject, matches, 0, 0) - } - - pub fn is_match5( - pattern: impl PregPattern, - subject: &str, - matches: Option<&mut IndexMap<CaptureKey, String>>, - flags: i64, - offset: usize, - ) -> bool { - Self::match5(pattern, subject, matches, flags, offset) - } - - pub fn is_match_named( - pattern: impl PregPattern, - subject: &str, - matches: &mut IndexMap<String, String>, - ) -> bool { - let mut internal: IndexMap<CaptureKey, Option<String>> = IndexMap::new(); - let result = shirabe_php_shim::preg_match2( - pattern, - subject, - &mut internal, - PREG_UNMATCHED_AS_NULL, - 0, - ); - - matches.clear(); - for (key, value) in internal { - if let (CaptureKey::ByName(name), Some(value)) = (key, value) { - matches.insert(name, value); - } - } - - result - } - - pub fn is_match_with_indexed_captures( - pattern: impl PregPattern, - subject: &str, - ) -> Option<Vec<String>> { - // Classic preg_match semantics (no PREG_UNMATCHED_AS_NULL): trailing - // unmatched groups are truncated, interior unmatched groups become "". - let mut internal: IndexMap<CaptureKey, Option<String>> = IndexMap::new(); - let result = shirabe_php_shim::preg_match2(pattern, subject, &mut internal, 0, 0); - - if !result { - return None; - } - - let max_index = internal - .keys() - .filter_map(|key| match key { - CaptureKey::ByIndex(index) => Some(*index), - CaptureKey::ByName(_) => None, - }) - .max() - .unwrap_or(0); - - let mut captures = Vec::with_capacity(max_index + 1); - for index in 0..=max_index { - let value = internal - .get(&CaptureKey::ByIndex(index)) - .and_then(|value| value.clone()) - .unwrap_or_default(); - captures.push(value); - } - - Some(captures) - } - - pub fn is_match_all3( - pattern: impl PregPattern, - subject: &str, - matches: Option<&mut IndexMap<CaptureKey, Vec<String>>>, - ) -> bool { - Self::match_all5(pattern, subject, matches, 0, 0) > 0 - } - - pub fn is_match_all_with_offsets3( - pattern: impl PregPattern, - subject: &str, - matches: Option<&mut IndexMap<CaptureKey, Vec<(String, usize)>>>, - ) -> bool { - Self::match_all_with_offsets5(pattern, subject, matches, 0, 0) > 0 - } - - fn check_offset_capture(flags: i64, use_function_name: &str) { - assert!( - flags & PREG_OFFSET_CAPTURE == 0, - "PREG_OFFSET_CAPTURE is not supported as it changes the type of $matches, use {}() instead", - use_function_name - ); - } - - fn check_set_order(flags: i64) { - assert!( - flags & PREG_SET_ORDER == 0, - "PREG_SET_ORDER is not supported as it changes the type of $matches" - ); - } -} - -// 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: IndexMap<CaptureKey, Option<String>>, -) -> IndexMap<CaptureKey, String> { - matches - .into_iter() - .filter_map(|(key, value)| value.map(|value| (key, value))) - .collect() -} - -fn drop_null_matches_ref( - matches: &IndexMap<CaptureKey, Option<String>>, -) -> IndexMap<CaptureKey, String> { - matches - .iter() - .filter_map(|(key, value)| value.clone().map(|value| (key.clone(), value))) - .collect() -} - -// In the `Vec<String>`-valued maps a per-iteration `null` cannot be stored, so -// unmatched groups collapse to "" (the classic non-PREG_UNMATCHED_AS_NULL form). -fn null_to_empty_match_all( - matches: IndexMap<CaptureKey, Vec<Option<String>>>, -) -> IndexMap<CaptureKey, Vec<String>> { - matches - .into_iter() - .map(|(key, values)| { - ( - key, - values - .into_iter() - .map(|value| value.unwrap_or_default()) - .collect(), - ) - }) - .collect() -} - -fn null_to_empty_offset_match_all( - matches: IndexMap<CaptureKey, Vec<(Option<String>, i64)>>, -) -> IndexMap<CaptureKey, Vec<(String, usize)>> { - matches - .into_iter() - .map(|(key, values)| { - ( - key, - values - .into_iter() - .map(|(value, offset)| (value.unwrap_or_default(), offset.max(0) as usize)) - .collect(), - ) - }) - .collect() -} diff --git a/crates/shirabe-external-packages/src/symfony/console/descriptor/json_descriptor.rs b/crates/shirabe-external-packages/src/symfony/console/descriptor/json_descriptor.rs index 6c1665e9..3232e6fa 100644 --- a/crates/shirabe-external-packages/src/symfony/console/descriptor/json_descriptor.rs +++ b/crates/shirabe-external-packages/src/symfony/console/descriptor/json_descriptor.rs @@ -1,6 +1,5 @@ //! ref: composer/vendor/symfony/console/Descriptor/JsonDescriptor.php -use crate::composer::pcre::preg::Preg; use crate::symfony::console::application::Application; use crate::symfony::console::command::command::Command; use crate::symfony::console::descriptor::application_description::ApplicationDescription; @@ -13,6 +12,7 @@ use crate::symfony::console::input::input_definition::InputDefinition; use crate::symfony::console::input::input_option::InputOption; use crate::symfony::console::output::output_interface::OutputInterface; use indexmap::IndexMap; +use shirabe_pcre::preg::Preg; use shirabe_php_shim::{PhpMixed, php_regex}; /// JSON descriptor. diff --git a/crates/shirabe-external-packages/src/symfony/console/descriptor/markdown_descriptor.rs b/crates/shirabe-external-packages/src/symfony/console/descriptor/markdown_descriptor.rs index 12afd53b..48514cfe 100644 --- a/crates/shirabe-external-packages/src/symfony/console/descriptor/markdown_descriptor.rs +++ b/crates/shirabe-external-packages/src/symfony/console/descriptor/markdown_descriptor.rs @@ -1,6 +1,5 @@ //! ref: composer/vendor/symfony/console/Descriptor/MarkdownDescriptor.php -use crate::composer::pcre::preg::Preg; use crate::symfony::console::application::Application; use crate::symfony::console::command::command::Command; use crate::symfony::console::descriptor::application_description::ApplicationDescription; @@ -14,6 +13,7 @@ use crate::symfony::console::input::input_definition::InputDefinition; use crate::symfony::console::input::input_option::InputOption; use crate::symfony::console::output::output_interface::OutputInterface; use indexmap::IndexMap; +use shirabe_pcre::preg::Preg; use shirabe_php_shim::PhpMixed; /// Markdown descriptor. diff --git a/crates/shirabe-external-packages/src/symfony/console/descriptor/text_descriptor.rs b/crates/shirabe-external-packages/src/symfony/console/descriptor/text_descriptor.rs index 4b85844e..147fe73f 100644 --- a/crates/shirabe-external-packages/src/symfony/console/descriptor/text_descriptor.rs +++ b/crates/shirabe-external-packages/src/symfony/console/descriptor/text_descriptor.rs @@ -1,6 +1,5 @@ //! ref: composer/vendor/symfony/console/Descriptor/TextDescriptor.php -use crate::composer::pcre::preg::Preg; use crate::symfony::console::application::Application; use crate::symfony::console::command::command::Command; use crate::symfony::console::descriptor::application_description::ApplicationDescription; @@ -15,6 +14,7 @@ use crate::symfony::console::input::input_definition::InputDefinition; use crate::symfony::console::input::input_option::InputOption; use crate::symfony::console::output::output_interface::OutputInterface; use indexmap::IndexMap; +use shirabe_pcre::preg::Preg; use shirabe_php_shim::PhpMixed; /// Text descriptor. diff --git a/crates/shirabe-external-packages/src/symfony/console/helper/table.rs b/crates/shirabe-external-packages/src/symfony/console/helper/table.rs index a1f5a11e..650dfffa 100644 --- a/crates/shirabe-external-packages/src/symfony/console/helper/table.rs +++ b/crates/shirabe-external-packages/src/symfony/console/helper/table.rs @@ -1,6 +1,5 @@ //! ref: composer/vendor/symfony/console/Helper/Table.php -use crate::composer::pcre::preg::Preg; use crate::symfony::console::exception::invalid_argument_exception::InvalidArgumentException; use crate::symfony::console::exception::runtime_exception::RuntimeException; use crate::symfony::console::formatter::output_formatter::OutputFormatter; @@ -14,6 +13,7 @@ use crate::symfony::console::helper::table_style::TableStyle; use crate::symfony::console::output::console_section_output::ConsoleSectionOutput; use crate::symfony::console::output::output_interface::OutputInterface; use indexmap::IndexMap; +use shirabe_pcre::preg::Preg; use shirabe_php_shim::{PhpMixed, php_regex}; /// A single cell within a table row. diff --git a/crates/shirabe-external-packages/src/symfony/finder/finder.rs b/crates/shirabe-external-packages/src/symfony/finder/finder.rs index c2b6c4cd..4cdcf392 100644 --- a/crates/shirabe-external-packages/src/symfony/finder/finder.rs +++ b/crates/shirabe-external-packages/src/symfony/finder/finder.rs @@ -6,10 +6,10 @@ //! `RecursiveDirectoryIterator` attaches to each `SplFileInfo` is carried on the //! private `Entry` struct so the path/exclude filters keep their exact behavior. -use crate::composer::pcre::{CaptureKey, Preg}; use crate::symfony::finder::glob::Glob; use chrono::{NaiveDate, NaiveDateTime}; use indexmap::{IndexMap, IndexSet}; +use shirabe_pcre::{CaptureKey, Preg}; use shirabe_php_shim::{file_exists, glob, is_dir, php_regex, preg_quote, rtrim}; use std::path::{Path, PathBuf}; use std::time::UNIX_EPOCH; |
