aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/shirabe-external-packages/src/composer/pcre/mod.rs2
-rw-r--r--crates/shirabe-external-packages/src/composer/pcre/pcre_exception.rs36
-rw-r--r--crates/shirabe-external-packages/src/composer/pcre/preg.rs992
-rw-r--r--crates/shirabe-external-packages/src/composer/pcre/unexpected_null_match_exception.rs29
4 files changed, 754 insertions, 305 deletions
diff --git a/crates/shirabe-external-packages/src/composer/pcre/mod.rs b/crates/shirabe-external-packages/src/composer/pcre/mod.rs
index 20c37d0..41def3b 100644
--- a/crates/shirabe-external-packages/src/composer/pcre/mod.rs
+++ b/crates/shirabe-external-packages/src/composer/pcre/mod.rs
@@ -1,3 +1,5 @@
+pub mod pcre_exception;
pub mod preg;
+pub mod unexpected_null_match_exception;
pub use preg::*;
diff --git a/crates/shirabe-external-packages/src/composer/pcre/pcre_exception.rs b/crates/shirabe-external-packages/src/composer/pcre/pcre_exception.rs
new file mode 100644
index 0000000..4ebe2c3
--- /dev/null
+++ b/crates/shirabe-external-packages/src/composer/pcre/pcre_exception.rs
@@ -0,0 +1,36 @@
+//! ref: composer/vendor/composer/pcre/src/PcreException.php
+
+use super::preg::{preg_last_error, preg_last_error_msg};
+
+#[derive(Debug)]
+pub struct PcreException(pub shirabe_php_shim::RuntimeException);
+
+impl PcreException {
+ pub fn from_function(function: &str, pattern: &str) -> PcreException {
+ let code = preg_last_error();
+
+ PcreException(shirabe_php_shim::RuntimeException {
+ message: format!(
+ "{}(): failed executing \"{}\": {}",
+ function,
+ pattern,
+ Self::pcre_last_error_message(code)
+ ),
+ code,
+ })
+ }
+
+ // Modern PHP always provides preg_last_error_msg(), so the legacy fallbacks
+ // that scanned the pcre constants are not reproduced.
+ fn pcre_last_error_message(_code: i64) -> String {
+ preg_last_error_msg()
+ }
+}
+
+impl std::fmt::Display for PcreException {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}", self.0)
+ }
+}
+
+impl std::error::Error for PcreException {}
diff --git a/crates/shirabe-external-packages/src/composer/pcre/preg.rs b/crates/shirabe-external-packages/src/composer/pcre/preg.rs
index c918b80..fba8c0b 100644
--- a/crates/shirabe-external-packages/src/composer/pcre/preg.rs
+++ b/crates/shirabe-external-packages/src/composer/pcre/preg.rs
@@ -1,539 +1,833 @@
//! ref: composer/vendor/composer/pcre/src/Preg.php
+use super::pcre_exception::PcreException;
+use super::unexpected_null_match_exception::UnexpectedNullMatchException;
+use indexmap::IndexMap;
+
+pub const PREG_PATTERN_ORDER: i64 = 1;
+pub const PREG_SET_ORDER: i64 = 2;
+pub const PREG_OFFSET_CAPTURE: i64 = 256;
+pub const PREG_UNMATCHED_AS_NULL: i64 = 512;
+pub const PREG_SPLIT_NO_EMPTY: i64 = 1;
+pub const PREG_SPLIT_DELIM_CAPTURE: i64 = 2;
+pub const PREG_SPLIT_OFFSET_CAPTURE: i64 = 4;
+pub const PREG_GREP_INVERT: i64 = 1;
+
#[derive(Debug)]
pub struct Preg;
impl Preg {
- pub fn r#match(_pattern: &str, _subject: &str) -> anyhow::Result<bool> {
- todo!()
+ const ARRAY_MSG: &'static str =
+ "$subject as an array is not supported. You can use 'foreach' instead.";
+ const INVALID_TYPE_MSG: &'static str = "$subject must be a string, %s given.";
+
+ pub fn r#match(pattern: &str, subject: &str) -> anyhow::Result<bool> {
+ Self::match5(pattern, subject, None, 0, 0)
}
pub fn match3(
- _pattern: &str,
- _subject: &str,
- _matches: Option<&mut indexmap::IndexMap<CaptureKey, String>>,
+ pattern: &str,
+ subject: &str,
+ matches: Option<&mut IndexMap<CaptureKey, String>>,
) -> anyhow::Result<bool> {
- todo!()
+ Self::match5(pattern, subject, matches, 0, 0)
}
pub fn match4(
- _pattern: &str,
- _subject: &str,
- _matches: Option<&mut indexmap::IndexMap<CaptureKey, String>>,
- _flags: i64,
+ pattern: &str,
+ subject: &str,
+ matches: Option<&mut IndexMap<CaptureKey, String>>,
+ flags: i64,
) -> anyhow::Result<bool> {
- todo!()
+ Self::match5(pattern, subject, matches, flags, 0)
}
pub fn match5(
- _pattern: &str,
- _subject: &str,
- _matches: Option<&mut indexmap::IndexMap<CaptureKey, String>>,
- _flags: i64,
- _offset: usize,
+ pattern: &str,
+ subject: &str,
+ matches: Option<&mut IndexMap<CaptureKey, String>>,
+ flags: i64,
+ offset: usize,
) -> anyhow::Result<bool> {
- todo!()
+ Self::check_offset_capture(flags, "matchWithOffsets");
+
+ let mut internal: IndexMap<CaptureKey, Option<String>> = IndexMap::new();
+ let result = preg_match(
+ pattern,
+ subject,
+ Some(&mut internal),
+ flags | PREG_UNMATCHED_AS_NULL,
+ offset,
+ )
+ .ok_or_else(|| PcreException::from_function("preg_match", pattern))?;
+
+ if let Some(out) = matches {
+ *out = drop_null_matches(internal);
+ }
+
+ Ok(result == 1)
}
- pub fn match_strict_groups(_pattern: &str, _subject: &str) -> anyhow::Result<bool> {
- todo!()
+ pub fn match_strict_groups(pattern: &str, subject: &str) -> anyhow::Result<bool> {
+ Self::match_strict_groups5(pattern, subject, None, 0, 0)
}
pub fn match_strict_groups3(
- _pattern: &str,
- _subject: &str,
- _matches: Option<&mut indexmap::IndexMap<CaptureKey, String>>,
+ pattern: &str,
+ subject: &str,
+ matches: Option<&mut IndexMap<CaptureKey, String>>,
) -> anyhow::Result<bool> {
- todo!()
+ Self::match_strict_groups5(pattern, subject, matches, 0, 0)
}
pub fn match_strict_groups4(
- _pattern: &str,
- _subject: &str,
- _matches: Option<&mut indexmap::IndexMap<CaptureKey, String>>,
- _flags: i64,
+ pattern: &str,
+ subject: &str,
+ matches: Option<&mut IndexMap<CaptureKey, String>>,
+ flags: i64,
) -> anyhow::Result<bool> {
- todo!()
+ Self::match_strict_groups5(pattern, subject, matches, flags, 0)
}
pub fn match_strict_groups5(
- _pattern: &str,
- _subject: &str,
- _matches: Option<&mut indexmap::IndexMap<CaptureKey, String>>,
- _flags: i64,
- _offset: usize,
+ pattern: &str,
+ subject: &str,
+ matches: Option<&mut IndexMap<CaptureKey, String>>,
+ flags: i64,
+ offset: usize,
) -> anyhow::Result<bool> {
- todo!()
+ Self::check_offset_capture(flags, "matchWithOffsets");
+
+ let mut internal: IndexMap<CaptureKey, Option<String>> = IndexMap::new();
+ let result = preg_match(
+ pattern,
+ subject,
+ Some(&mut internal),
+ flags | PREG_UNMATCHED_AS_NULL,
+ offset,
+ )
+ .ok_or_else(|| PcreException::from_function("preg_match", pattern))?;
+
+ let enforced = Self::enforce_non_null_matches(pattern, internal, "match")?;
+ if let Some(out) = matches {
+ *out = enforced;
+ }
+
+ Ok(result == 1)
}
- pub fn match_with_offsets(_pattern: &str, _subject: &str) -> anyhow::Result<bool> {
- todo!()
+ pub fn match_with_offsets(pattern: &str, subject: &str) -> anyhow::Result<bool> {
+ Self::match_with_offsets5(pattern, subject, None, 0, 0)
}
pub fn match_with_offsets3(
- _pattern: &str,
- _subject: &str,
- _matches: Option<&mut indexmap::IndexMap<CaptureKey, (String, usize)>>,
+ pattern: &str,
+ subject: &str,
+ matches: Option<&mut IndexMap<CaptureKey, (String, usize)>>,
) -> anyhow::Result<bool> {
- todo!()
+ Self::match_with_offsets5(pattern, subject, matches, 0, 0)
}
pub fn match_with_offsets4(
- _pattern: &str,
- _subject: &str,
- _matches: Option<&mut indexmap::IndexMap<CaptureKey, (String, usize)>>,
- _flags: i64,
+ pattern: &str,
+ subject: &str,
+ matches: Option<&mut IndexMap<CaptureKey, (String, usize)>>,
+ flags: i64,
) -> anyhow::Result<bool> {
- todo!()
+ Self::match_with_offsets5(pattern, subject, matches, flags, 0)
}
pub fn match_with_offsets5(
- _pattern: &str,
- _subject: &str,
- _matches: Option<&mut indexmap::IndexMap<CaptureKey, (String, usize)>>,
- _flags: i64,
- _offset: usize,
+ pattern: &str,
+ subject: &str,
+ matches: Option<&mut IndexMap<CaptureKey, (String, usize)>>,
+ flags: i64,
+ offset: usize,
) -> anyhow::Result<bool> {
- todo!()
+ let mut internal: IndexMap<CaptureKey, (Option<String>, i64)> = IndexMap::new();
+ let result = preg_match_offset_capture(
+ pattern,
+ subject,
+ Some(&mut internal),
+ flags | PREG_UNMATCHED_AS_NULL | PREG_OFFSET_CAPTURE,
+ offset,
+ )
+ .ok_or_else(|| PcreException::from_function("preg_match", pattern))?;
+
+ if let Some(out) = matches {
+ *out = drop_null_offset_matches(internal);
+ }
+
+ Ok(result == 1)
}
- pub fn match_all(_pattern: &str, _subject: &str) -> anyhow::Result<usize> {
- todo!()
+ pub fn match_all(pattern: &str, subject: &str) -> anyhow::Result<usize> {
+ Self::match_all5(pattern, subject, None, 0, 0)
}
pub fn match_all3(
- _pattern: &str,
- _subject: &str,
- _matches: Option<&mut indexmap::IndexMap<CaptureKey, Vec<String>>>,
+ pattern: &str,
+ subject: &str,
+ matches: Option<&mut IndexMap<CaptureKey, Vec<String>>>,
) -> anyhow::Result<usize> {
- todo!()
+ Self::match_all5(pattern, subject, matches, 0, 0)
}
pub fn match_all4(
- _pattern: &str,
- _subject: &str,
- _matches: Option<&mut indexmap::IndexMap<CaptureKey, Vec<String>>>,
- _flags: i64,
+ pattern: &str,
+ subject: &str,
+ matches: Option<&mut IndexMap<CaptureKey, Vec<String>>>,
+ flags: i64,
) -> anyhow::Result<usize> {
- todo!()
+ Self::match_all5(pattern, subject, matches, flags, 0)
}
pub fn match_all5(
- _pattern: &str,
- _subject: &str,
- _matches: Option<&mut indexmap::IndexMap<CaptureKey, Vec<String>>>,
- _flags: i64,
- _offset: usize,
+ pattern: &str,
+ subject: &str,
+ matches: Option<&mut IndexMap<CaptureKey, Vec<String>>>,
+ flags: i64,
+ offset: usize,
) -> anyhow::Result<usize> {
- todo!()
+ Self::check_offset_capture(flags, "matchAllWithOffsets");
+ Self::check_set_order(flags);
+
+ let mut internal: IndexMap<CaptureKey, Vec<Option<String>>> = IndexMap::new();
+ let result = preg_match_all(
+ pattern,
+ subject,
+ Some(&mut internal),
+ flags | PREG_UNMATCHED_AS_NULL,
+ offset,
+ )
+ .ok_or_else(|| PcreException::from_function("preg_match_all", pattern))?;
+
+ if let Some(out) = matches {
+ *out = null_to_empty_match_all(internal);
+ }
+
+ Ok(result as usize)
}
- pub fn match_all_strict_groups(_pattern: &str, _subject: &str) -> anyhow::Result<usize> {
- todo!()
+ pub fn match_all_strict_groups(pattern: &str, subject: &str) -> anyhow::Result<usize> {
+ Self::match_all_strict_groups5(pattern, subject, None, 0, 0)
}
pub fn match_all_strict_groups3(
- _pattern: &str,
- _subject: &str,
- _matches: Option<&mut indexmap::IndexMap<CaptureKey, Vec<String>>>,
+ pattern: &str,
+ subject: &str,
+ matches: Option<&mut IndexMap<CaptureKey, Vec<String>>>,
) -> anyhow::Result<usize> {
- todo!()
+ Self::match_all_strict_groups5(pattern, subject, matches, 0, 0)
}
pub fn match_all_strict_groups4(
- _pattern: &str,
- _subject: &str,
- _matches: Option<&mut indexmap::IndexMap<CaptureKey, Vec<String>>>,
- _flags: i64,
+ pattern: &str,
+ subject: &str,
+ matches: Option<&mut IndexMap<CaptureKey, Vec<String>>>,
+ flags: i64,
) -> anyhow::Result<usize> {
- todo!()
+ Self::match_all_strict_groups5(pattern, subject, matches, flags, 0)
}
pub fn match_all_strict_groups5(
- _pattern: &str,
- _subject: &str,
- _matches: Option<&mut indexmap::IndexMap<CaptureKey, Vec<String>>>,
- _flags: i64,
- _offset: usize,
+ pattern: &str,
+ subject: &str,
+ matches: Option<&mut IndexMap<CaptureKey, Vec<String>>>,
+ flags: i64,
+ offset: usize,
) -> anyhow::Result<usize> {
- todo!()
+ Self::check_offset_capture(flags, "matchAllWithOffsets");
+ Self::check_set_order(flags);
+
+ let mut internal: IndexMap<CaptureKey, Vec<Option<String>>> = IndexMap::new();
+ let result = preg_match_all(
+ pattern,
+ subject,
+ Some(&mut internal),
+ flags | PREG_UNMATCHED_AS_NULL,
+ offset,
+ )
+ .ok_or_else(|| PcreException::from_function("preg_match_all", pattern))?;
+
+ let enforced = Self::enforce_non_null_match_all(pattern, internal, "matchAll")?;
+ if let Some(out) = matches {
+ *out = enforced;
+ }
+
+ Ok(result as usize)
}
- pub fn match_all_with_offsets(_pattern: &str, _subject: &str) -> anyhow::Result<usize> {
- todo!()
+ pub fn match_all_with_offsets(pattern: &str, subject: &str) -> anyhow::Result<usize> {
+ Self::match_all_with_offsets5(pattern, subject, None, 0, 0)
}
pub fn match_all_with_offsets3(
- _pattern: &str,
- _subject: &str,
- _matches: Option<&mut indexmap::IndexMap<CaptureKey, Vec<(String, usize)>>>,
+ pattern: &str,
+ subject: &str,
+ matches: Option<&mut IndexMap<CaptureKey, Vec<(String, usize)>>>,
) -> anyhow::Result<usize> {
- todo!()
+ Self::match_all_with_offsets5(pattern, subject, matches, 0, 0)
}
pub fn match_all_with_offsets4(
- _pattern: &str,
- _subject: &str,
- _matches: Option<&mut indexmap::IndexMap<CaptureKey, Vec<(String, usize)>>>,
- _flags: i64,
+ pattern: &str,
+ subject: &str,
+ matches: Option<&mut IndexMap<CaptureKey, Vec<(String, usize)>>>,
+ flags: i64,
) -> anyhow::Result<usize> {
- todo!()
+ Self::match_all_with_offsets5(pattern, subject, matches, flags, 0)
}
pub fn match_all_with_offsets5(
- _pattern: &str,
- _subject: &str,
- _matches: Option<&mut indexmap::IndexMap<CaptureKey, Vec<(String, usize)>>>,
- _flags: i64,
- _offset: usize,
+ pattern: &str,
+ subject: &str,
+ matches: Option<&mut IndexMap<CaptureKey, Vec<(String, usize)>>>,
+ flags: i64,
+ offset: usize,
) -> anyhow::Result<usize> {
- todo!()
- }
+ Self::check_set_order(flags);
- pub fn replace(_pattern: &str, _replacement: &str, _subject: &str) -> anyhow::Result<String> {
- todo!()
- }
+ let mut internal: IndexMap<CaptureKey, Vec<(Option<String>, i64)>> = IndexMap::new();
+ let result = preg_match_all_offset_capture(
+ pattern,
+ subject,
+ Some(&mut internal),
+ flags | PREG_UNMATCHED_AS_NULL | PREG_OFFSET_CAPTURE,
+ offset,
+ )
+ .ok_or_else(|| PcreException::from_function("preg_match_all", pattern))?;
- pub fn replace4(
- _pattern: &str,
- _replacement: &str,
- _subject: &str,
- _limit: i64,
- ) -> anyhow::Result<String> {
- todo!()
- }
+ if let Some(out) = matches {
+ *out = null_to_empty_offset_match_all(internal);
+ }
- pub fn replace5(
- _pattern: &str,
- _replacement: &str,
- _subject: &str,
- _limit: i64,
- _count: &mut usize,
- ) -> anyhow::Result<String> {
- todo!()
+ Ok(result as usize)
}
- pub fn replace_callback<F: FnMut(&indexmap::IndexMap<CaptureKey, String>) -> String>(
- _pattern: &str,
- _replacement: F,
- _subject: &str,
- ) -> anyhow::Result<String> {
- todo!()
+ pub fn replace(pattern: &str, replacement: &str, subject: &str) -> anyhow::Result<String> {
+ Self::replace_impl(pattern, replacement, subject, -1, None)
}
- pub fn replace_callback4<F: FnMut(&indexmap::IndexMap<CaptureKey, String>) -> String>(
- _pattern: &str,
- _replacement: F,
- _subject: &str,
- _limit: i64,
+ pub fn replace4(
+ pattern: &str,
+ replacement: &str,
+ subject: &str,
+ limit: i64,
) -> anyhow::Result<String> {
- todo!()
+ Self::replace_impl(pattern, replacement, subject, limit, None)
}
- pub fn replace_callback5<F: FnMut(&indexmap::IndexMap<CaptureKey, String>) -> String>(
- _pattern: &str,
- _replacement: F,
- _subject: &str,
- _limit: i64,
- _count: &mut usize,
+ pub fn replace5(
+ pattern: &str,
+ replacement: &str,
+ subject: &str,
+ limit: i64,
+ count: &mut usize,
) -> anyhow::Result<String> {
- todo!()
+ Self::replace_impl(pattern, replacement, subject, limit, Some(count))
}
- pub fn replace_callback6<F: FnMut(&indexmap::IndexMap<CaptureKey, String>) -> String>(
- _pattern: &str,
- _replacement: F,
- _subject: &str,
- _limit: i64,
- _count: Option<&mut usize>,
- _flags: i64,
+ fn replace_impl(
+ pattern: &str,
+ replacement: &str,
+ subject: &str,
+ limit: i64,
+ count: Option<&mut usize>,
) -> anyhow::Result<String> {
- todo!()
+ // `$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.
+ preg_replace(pattern, replacement, subject, limit, count)
+ .ok_or_else(|| PcreException::from_function("preg_replace", pattern).into())
}
- pub fn replace_callback_array(
- _pattern: &indexmap::IndexMap<String, String>,
- _subject: &str,
+ pub fn replace_callback<F: FnMut(&IndexMap<CaptureKey, String>) -> String>(
+ pattern: &str,
+ replacement: F,
+ subject: &str,
) -> anyhow::Result<String> {
- todo!()
+ Self::replace_callback6(pattern, replacement, subject, -1, None, 0)
}
- pub fn replace_callback_array3(
- _pattern: &indexmap::IndexMap<String, String>,
- _subject: &str,
- _limit: i64,
+ pub fn replace_callback4<F: FnMut(&IndexMap<CaptureKey, String>) -> String>(
+ pattern: &str,
+ replacement: F,
+ subject: &str,
+ limit: i64,
) -> anyhow::Result<String> {
- todo!()
+ Self::replace_callback6(pattern, replacement, subject, limit, None, 0)
}
- pub fn replace_callback_array4(
- _pattern: &indexmap::IndexMap<String, String>,
- _subject: &str,
- _limit: i64,
- _count: &mut usize,
+ pub fn replace_callback5<F: FnMut(&IndexMap<CaptureKey, String>) -> String>(
+ pattern: &str,
+ replacement: F,
+ subject: &str,
+ limit: i64,
+ count: &mut usize,
) -> anyhow::Result<String> {
- todo!()
+ Self::replace_callback6(pattern, replacement, subject, limit, Some(count), 0)
}
- pub fn replace_callback_array5(
- _pattern: &indexmap::IndexMap<String, String>,
- _subject: &str,
- _limit: i64,
- _count: Option<&mut usize>,
- _flags: i64,
+ pub fn replace_callback6<F: FnMut(&IndexMap<CaptureKey, String>) -> String>(
+ pattern: &str,
+ mut replacement: F,
+ subject: &str,
+ limit: i64,
+ count: Option<&mut usize>,
+ flags: i64,
) -> anyhow::Result<String> {
- todo!()
+ let adapter = |internal: &IndexMap<CaptureKey, Option<String>>| -> String {
+ replacement(&drop_null_matches_ref(internal))
+ };
+
+ preg_replace_callback(pattern, adapter, subject, limit, count, flags)
+ .ok_or_else(|| PcreException::from_function("preg_replace_callback", pattern).into())
}
- pub fn split(_pattern: &str, _subject: &str) -> anyhow::Result<Vec<String>> {
- todo!()
+ pub fn split(pattern: &str, subject: &str) -> anyhow::Result<Vec<String>> {
+ Self::split4(pattern, subject, -1, 0)
}
- pub fn split3(_pattern: &str, _subject: &str, _limit: i64) -> anyhow::Result<Vec<String>> {
- todo!()
+ pub fn split3(pattern: &str, subject: &str, limit: i64) -> anyhow::Result<Vec<String>> {
+ Self::split4(pattern, subject, limit, 0)
}
pub fn split4(
- _pattern: &str,
- _subject: &str,
- _limit: i64,
- _flags: i64,
+ pattern: &str,
+ subject: &str,
+ limit: i64,
+ flags: i64,
) -> anyhow::Result<Vec<String>> {
- todo!()
+ assert!(
+ flags & PREG_SPLIT_OFFSET_CAPTURE == 0,
+ "PREG_SPLIT_OFFSET_CAPTURE is not supported as it changes the type of $matches, use splitWithOffsets() instead"
+ );
+
+ preg_split(pattern, subject, limit, flags)
+ .ok_or_else(|| PcreException::from_function("preg_split", pattern).into())
}
pub fn split_with_offsets(
- _pattern: &str,
- _subject: &str,
+ pattern: &str,
+ subject: &str,
) -> anyhow::Result<Vec<(String, usize)>> {
- todo!()
+ Self::split_with_offsets4(pattern, subject, -1, 0)
}
pub fn split_with_offsets3(
- _pattern: &str,
- _subject: &str,
- _limit: i64,
+ pattern: &str,
+ subject: &str,
+ limit: i64,
) -> anyhow::Result<Vec<(String, usize)>> {
- todo!()
+ Self::split_with_offsets4(pattern, subject, limit, 0)
}
pub fn split_with_offsets4(
- _pattern: &str,
- _subject: &str,
- _limit: i64,
- _flags: i64,
+ pattern: &str,
+ subject: &str,
+ limit: i64,
+ flags: i64,
) -> anyhow::Result<Vec<(String, usize)>> {
- todo!()
+ preg_split_offset_capture(pattern, subject, limit, flags | PREG_SPLIT_OFFSET_CAPTURE)
+ .ok_or_else(|| PcreException::from_function("preg_split", pattern).into())
}
- pub fn grep(_pattern: &str, _array: &[&str]) -> anyhow::Result<Vec<String>> {
- todo!()
+ pub fn grep(pattern: &str, array: &[&str]) -> anyhow::Result<Vec<String>> {
+ Self::grep3(pattern, array, 0)
}
- pub fn grep3(_pattern: &str, _array: &[&str], _flags: i64) -> anyhow::Result<Vec<String>> {
- todo!()
+ pub fn grep3(pattern: &str, array: &[&str], flags: i64) -> anyhow::Result<Vec<String>> {
+ preg_grep(pattern, array, flags)
+ .ok_or_else(|| PcreException::from_function("preg_grep", pattern).into())
}
- pub fn is_match(_pattern: &str, _subject: &str) -> anyhow::Result<bool> {
- todo!()
+ pub fn is_match(pattern: &str, subject: &str) -> anyhow::Result<bool> {
+ Self::match5(pattern, subject, None, 0, 0)
}
pub fn is_match3(
- _pattern: &str,
- _subject: &str,
- _matches: Option<&mut indexmap::IndexMap<CaptureKey, String>>,
+ pattern: &str,
+ subject: &str,
+ matches: Option<&mut IndexMap<CaptureKey, String>>,
) -> anyhow::Result<bool> {
- todo!()
+ Self::match5(pattern, subject, matches, 0, 0)
}
pub fn is_match4(
- _pattern: &str,
- _subject: &str,
- _matches: Option<&mut indexmap::IndexMap<CaptureKey, String>>,
- _flags: i64,
+ pattern: &str,
+ subject: &str,
+ matches: Option<&mut IndexMap<CaptureKey, String>>,
+ flags: i64,
) -> anyhow::Result<bool> {
- todo!()
+ Self::match5(pattern, subject, matches, flags, 0)
}
pub fn is_match5(
- _pattern: &str,
- _subject: &str,
- _matches: Option<&mut indexmap::IndexMap<CaptureKey, String>>,
- _flags: i64,
- _offset: usize,
+ pattern: &str,
+ subject: &str,
+ matches: Option<&mut IndexMap<CaptureKey, String>>,
+ flags: i64,
+ offset: usize,
) -> anyhow::Result<bool> {
- todo!()
+ Self::match5(pattern, subject, matches, flags, offset)
}
pub fn is_match_named(
- _pattern: &str,
- _subject: &str,
- _matches: &mut indexmap::IndexMap<String, String>,
+ pattern: &str,
+ subject: &str,
+ matches: &mut IndexMap<String, String>,
) -> anyhow::Result<bool> {
- todo!()
+ let mut internal: IndexMap<CaptureKey, Option<String>> = IndexMap::new();
+ let result = preg_match(
+ pattern,
+ subject,
+ Some(&mut internal),
+ PREG_UNMATCHED_AS_NULL,
+ 0,
+ )
+ .ok_or_else(|| PcreException::from_function("preg_match", pattern))?;
+
+ matches.clear();
+ for (key, value) in internal {
+ if let (CaptureKey::ByName(name), Some(value)) = (key, value) {
+ matches.insert(name, value);
+ }
+ }
+
+ Ok(result == 1)
}
- pub fn is_match_strict_groups(_pattern: &str, _subject: &str) -> anyhow::Result<bool> {
- todo!()
+ pub fn is_match_strict_groups(pattern: &str, subject: &str) -> anyhow::Result<bool> {
+ Self::match_strict_groups5(pattern, subject, None, 0, 0)
}
pub fn is_match_strict_groups3(
- _pattern: &str,
- _subject: &str,
- _matches: Option<&mut indexmap::IndexMap<CaptureKey, String>>,
+ pattern: &str,
+ subject: &str,
+ matches: Option<&mut IndexMap<CaptureKey, String>>,
) -> anyhow::Result<bool> {
- todo!()
+ Self::match_strict_groups5(pattern, subject, matches, 0, 0)
}
pub fn is_match_strict_groups4(
- _pattern: &str,
- _subject: &str,
- _matches: Option<&mut indexmap::IndexMap<CaptureKey, String>>,
- _flags: i64,
+ pattern: &str,
+ subject: &str,
+ matches: Option<&mut IndexMap<CaptureKey, String>>,
+ flags: i64,
) -> anyhow::Result<bool> {
- todo!()
+ Self::match_strict_groups5(pattern, subject, matches, flags, 0)
}
pub fn is_match_strict_groups5(
- _pattern: &str,
- _subject: &str,
- _matches: Option<&mut indexmap::IndexMap<CaptureKey, String>>,
- _flags: i64,
- _offset: usize,
+ pattern: &str,
+ subject: &str,
+ matches: Option<&mut IndexMap<CaptureKey, String>>,
+ flags: i64,
+ offset: usize,
) -> anyhow::Result<bool> {
- todo!()
+ Self::match_strict_groups5(pattern, subject, matches, flags, offset)
}
- pub fn is_match_with_offsets(_pattern: &str, _subject: &str) -> anyhow::Result<bool> {
- todo!()
+ pub fn is_match_with_offsets(pattern: &str, subject: &str) -> anyhow::Result<bool> {
+ Self::match_with_offsets5(pattern, subject, None, 0, 0)
}
pub fn is_match_with_offsets3(
- _pattern: &str,
- _subject: &str,
- _matches: Option<&mut indexmap::IndexMap<CaptureKey, (String, usize)>>,
+ pattern: &str,
+ subject: &str,
+ matches: Option<&mut IndexMap<CaptureKey, (String, usize)>>,
) -> anyhow::Result<bool> {
- todo!()
+ Self::match_with_offsets5(pattern, subject, matches, 0, 0)
}
pub fn is_match_with_offsets4(
- _pattern: &str,
- _subject: &str,
- _matches: Option<&mut indexmap::IndexMap<CaptureKey, (String, usize)>>,
- _flags: i64,
+ pattern: &str,
+ subject: &str,
+ matches: Option<&mut IndexMap<CaptureKey, (String, usize)>>,
+ flags: i64,
) -> anyhow::Result<bool> {
- todo!()
+ Self::match_with_offsets5(pattern, subject, matches, flags, 0)
}
pub fn is_match_with_offsets5(
- _pattern: &str,
- _subject: &str,
- _matches: Option<&mut indexmap::IndexMap<CaptureKey, (String, usize)>>,
- _flags: i64,
- _offset: usize,
+ pattern: &str,
+ subject: &str,
+ matches: Option<&mut IndexMap<CaptureKey, (String, usize)>>,
+ flags: i64,
+ offset: usize,
) -> anyhow::Result<bool> {
- todo!()
+ Self::match_with_offsets5(pattern, subject, matches, flags, offset)
}
- pub fn is_match_all(_pattern: &str, _subject: &str) -> anyhow::Result<bool> {
- todo!()
+ pub fn is_match_all(pattern: &str, subject: &str) -> anyhow::Result<bool> {
+ Ok(Self::match_all5(pattern, subject, None, 0, 0)? > 0)
}
pub fn is_match_with_indexed_captures(
- _pattern: &str,
- _subject: &str,
+ pattern: &str,
+ subject: &str,
) -> anyhow::Result<Option<Vec<String>>> {
- todo!()
+ // 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 = preg_match(pattern, subject, Some(&mut internal), 0, 0)
+ .ok_or_else(|| PcreException::from_function("preg_match", pattern))?;
+
+ if result == 0 {
+ return Ok(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);
+ }
+
+ Ok(Some(captures))
}
pub fn is_match_all3(
- _pattern: &str,
- _subject: &str,
- _matches: Option<&mut indexmap::IndexMap<CaptureKey, Vec<String>>>,
+ pattern: &str,
+ subject: &str,
+ matches: Option<&mut IndexMap<CaptureKey, Vec<String>>>,
) -> anyhow::Result<bool> {
- todo!()
+ Ok(Self::match_all5(pattern, subject, matches, 0, 0)? > 0)
}
pub fn is_match_all4(
- _pattern: &str,
- _subject: &str,
- _matches: Option<&mut indexmap::IndexMap<CaptureKey, Vec<String>>>,
- _flags: i64,
+ pattern: &str,
+ subject: &str,
+ matches: Option<&mut IndexMap<CaptureKey, Vec<String>>>,
+ flags: i64,
) -> anyhow::Result<bool> {
- todo!()
+ Ok(Self::match_all5(pattern, subject, matches, flags, 0)? > 0)
}
pub fn is_match_all5(
- _pattern: &str,
- _subject: &str,
- _matches: Option<&mut indexmap::IndexMap<CaptureKey, Vec<String>>>,
- _flags: i64,
- _offset: usize,
+ pattern: &str,
+ subject: &str,
+ matches: Option<&mut IndexMap<CaptureKey, Vec<String>>>,
+ flags: i64,
+ offset: usize,
) -> anyhow::Result<bool> {
- todo!()
+ Ok(Self::match_all5(pattern, subject, matches, flags, offset)? > 0)
}
- pub fn is_match_all_strict_groups(_pattern: &str, _subject: &str) -> anyhow::Result<bool> {
- todo!()
+ pub fn is_match_all_strict_groups(pattern: &str, subject: &str) -> anyhow::Result<bool> {
+ Ok(Self::match_all_strict_groups5(pattern, subject, None, 0, 0)? > 0)
}
pub fn is_match_all_strict_groups3(
- _pattern: &str,
- _subject: &str,
- _matches: Option<&mut indexmap::IndexMap<CaptureKey, Vec<String>>>,
+ pattern: &str,
+ subject: &str,
+ matches: Option<&mut IndexMap<CaptureKey, Vec<String>>>,
) -> anyhow::Result<bool> {
- todo!()
+ Ok(Self::match_all_strict_groups5(pattern, subject, matches, 0, 0)? > 0)
}
pub fn is_match_all_strict_groups4(
- _pattern: &str,
- _subject: &str,
- _matches: Option<&mut indexmap::IndexMap<CaptureKey, Vec<String>>>,
- _flags: i64,
+ pattern: &str,
+ subject: &str,
+ matches: Option<&mut IndexMap<CaptureKey, Vec<String>>>,
+ flags: i64,
) -> anyhow::Result<bool> {
- todo!()
+ Ok(Self::match_all_strict_groups5(pattern, subject, matches, flags, 0)? > 0)
}
pub fn is_match_all_strict_groups5(
- _pattern: &str,
- _subject: &str,
- _matches: Option<&mut indexmap::IndexMap<CaptureKey, Vec<String>>>,
- _flags: i64,
- _offset: usize,
+ pattern: &str,
+ subject: &str,
+ matches: Option<&mut IndexMap<CaptureKey, Vec<String>>>,
+ flags: i64,
+ offset: usize,
) -> anyhow::Result<bool> {
- todo!()
+ Ok(Self::match_all_strict_groups5(pattern, subject, matches, flags, offset)? > 0)
}
- pub fn is_match_all_with_offsets(_pattern: &str, _subject: &str) -> anyhow::Result<bool> {
- todo!()
+ pub fn is_match_all_with_offsets(pattern: &str, subject: &str) -> anyhow::Result<bool> {
+ Ok(Self::match_all_with_offsets5(pattern, subject, None, 0, 0)? > 0)
}
pub fn is_match_all_with_offsets3(
- _pattern: &str,
- _subject: &str,
- _matches: Option<&mut indexmap::IndexMap<CaptureKey, Vec<(String, usize)>>>,
+ pattern: &str,
+ subject: &str,
+ matches: Option<&mut IndexMap<CaptureKey, Vec<(String, usize)>>>,
) -> anyhow::Result<bool> {
- todo!()
+ Ok(Self::match_all_with_offsets5(pattern, subject, matches, 0, 0)? > 0)
}
pub fn is_match_all_with_offsets4(
- _pattern: &str,
- _subject: &str,
- _matches: Option<&mut indexmap::IndexMap<CaptureKey, Vec<(String, usize)>>>,
- _flags: i64,
+ pattern: &str,
+ subject: &str,
+ matches: Option<&mut IndexMap<CaptureKey, Vec<(String, usize)>>>,
+ flags: i64,
) -> anyhow::Result<bool> {
- todo!()
+ Ok(Self::match_all_with_offsets5(pattern, subject, matches, flags, 0)? > 0)
}
pub fn is_match_all_with_offsets5(
- _pattern: &str,
- _subject: &str,
- _matches: Option<&mut indexmap::IndexMap<CaptureKey, Vec<(String, usize)>>>,
- _flags: i64,
- _offset: usize,
+ pattern: &str,
+ subject: &str,
+ matches: Option<&mut IndexMap<CaptureKey, Vec<(String, usize)>>>,
+ flags: i64,
+ offset: usize,
) -> anyhow::Result<bool> {
- todo!()
+ Ok(Self::match_all_with_offsets5(pattern, subject, matches, flags, offset)? > 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"
+ );
}
+
+ fn enforce_non_null_matches(
+ pattern: &str,
+ matches: IndexMap<CaptureKey, Option<String>>,
+ variant_method: &str,
+ ) -> anyhow::Result<IndexMap<CaptureKey, String>> {
+ let mut result = IndexMap::new();
+ for (group, m) in matches {
+ match m {
+ None => {
+ return Err(UnexpectedNullMatchException::new(format!(
+ "Pattern \"{}\" had an unexpected unmatched group \"{}\", make sure the pattern always matches or use {}() instead.",
+ pattern,
+ capture_key_to_string(&group),
+ variant_method
+ ))
+ .into());
+ }
+ Some(value) => {
+ result.insert(group, value);
+ }
+ }
+ }
+ Ok(result)
+ }
+
+ fn enforce_non_null_match_all(
+ pattern: &str,
+ matches: IndexMap<CaptureKey, Vec<Option<String>>>,
+ variant_method: &str,
+ ) -> anyhow::Result<IndexMap<CaptureKey, Vec<String>>> {
+ let mut result = IndexMap::new();
+ for (group, group_matches) in matches {
+ let mut converted = Vec::with_capacity(group_matches.len());
+ for m in group_matches {
+ match m {
+ None => {
+ return Err(UnexpectedNullMatchException::new(format!(
+ "Pattern \"{}\" had an unexpected unmatched group \"{}\", make sure the pattern always matches or use {}() instead.",
+ pattern,
+ capture_key_to_string(&group),
+ variant_method
+ ))
+ .into());
+ }
+ Some(value) => converted.push(value),
+ }
+ }
+ result.insert(group, converted);
+ }
+ Ok(result)
+ }
+}
+
+fn capture_key_to_string(key: &CaptureKey) -> String {
+ match key {
+ CaptureKey::ByIndex(index) => index.to_string(),
+ CaptureKey::ByName(name) => name.clone(),
+ }
+}
+
+// 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()
+}
+
+fn drop_null_offset_matches(
+ matches: IndexMap<CaptureKey, (Option<String>, i64)>,
+) -> IndexMap<CaptureKey, (String, usize)> {
+ matches
+ .into_iter()
+ .filter_map(|(key, (value, offset))| {
+ value.map(|value| (key, (value, offset.max(0) as usize)))
+ })
+ .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()
}
#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
@@ -541,3 +835,91 @@ pub enum CaptureKey {
ByIndex(usize),
ByName(String),
}
+
+pub fn preg_last_error() -> i64 {
+ todo!()
+}
+
+pub fn preg_last_error_msg() -> String {
+ todo!()
+}
+
+// Returns Some(0|1) on success or None when the underlying preg_match returned
+// false. Unmatched groups are reported as None (PREG_UNMATCHED_AS_NULL).
+pub fn preg_match(
+ _pattern: &str,
+ _subject: &str,
+ _matches: Option<&mut IndexMap<CaptureKey, Option<String>>>,
+ _flags: i64,
+ _offset: usize,
+) -> Option<i64> {
+ todo!()
+}
+
+pub fn preg_match_offset_capture(
+ _pattern: &str,
+ _subject: &str,
+ _matches: Option<&mut IndexMap<CaptureKey, (Option<String>, i64)>>,
+ _flags: i64,
+ _offset: usize,
+) -> Option<i64> {
+ todo!()
+}
+
+pub fn preg_match_all(
+ _pattern: &str,
+ _subject: &str,
+ _matches: Option<&mut IndexMap<CaptureKey, Vec<Option<String>>>>,
+ _flags: i64,
+ _offset: usize,
+) -> Option<i64> {
+ todo!()
+}
+
+pub fn preg_match_all_offset_capture(
+ _pattern: &str,
+ _subject: &str,
+ _matches: Option<&mut IndexMap<CaptureKey, Vec<(Option<String>, i64)>>>,
+ _flags: i64,
+ _offset: usize,
+) -> Option<i64> {
+ todo!()
+}
+
+pub fn preg_replace(
+ _pattern: &str,
+ _replacement: &str,
+ _subject: &str,
+ _limit: i64,
+ _count: Option<&mut usize>,
+) -> Option<String> {
+ todo!()
+}
+
+pub fn preg_replace_callback<F: FnMut(&IndexMap<CaptureKey, Option<String>>) -> String>(
+ _pattern: &str,
+ _callback: F,
+ _subject: &str,
+ _limit: i64,
+ _count: Option<&mut usize>,
+ _flags: i64,
+) -> Option<String> {
+ todo!()
+}
+
+pub fn preg_split(_pattern: &str, _subject: &str, _limit: i64, _flags: i64) -> Option<Vec<String>> {
+ todo!()
+}
+
+pub fn preg_split_offset_capture(
+ _pattern: &str,
+ _subject: &str,
+ _limit: i64,
+ _flags: i64,
+) -> Option<Vec<(String, usize)>> {
+ todo!()
+}
+
+pub fn preg_grep(_pattern: &str, _array: &[&str], _flags: i64) -> Option<Vec<String>> {
+ todo!()
+}
diff --git a/crates/shirabe-external-packages/src/composer/pcre/unexpected_null_match_exception.rs b/crates/shirabe-external-packages/src/composer/pcre/unexpected_null_match_exception.rs
new file mode 100644
index 0000000..513870c
--- /dev/null
+++ b/crates/shirabe-external-packages/src/composer/pcre/unexpected_null_match_exception.rs
@@ -0,0 +1,29 @@
+//! ref: composer/vendor/composer/pcre/src/UnexpectedNullMatchException.php
+
+use super::pcre_exception::PcreException;
+
+#[derive(Debug)]
+pub struct UnexpectedNullMatchException(pub PcreException);
+
+impl UnexpectedNullMatchException {
+ pub fn new(message: String) -> UnexpectedNullMatchException {
+ UnexpectedNullMatchException(PcreException(shirabe_php_shim::RuntimeException {
+ message,
+ code: 0,
+ }))
+ }
+
+ pub fn from_function(_function: &str, _pattern: &str) -> UnexpectedNullMatchException {
+ panic!(
+ "fromFunction should not be called on UnexpectedNullMatchException, use PcreException"
+ );
+ }
+}
+
+impl std::fmt::Display for UnexpectedNullMatchException {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}", self.0)
+ }
+}
+
+impl std::error::Error for UnexpectedNullMatchException {}