aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src/util/git.rs
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-08-18 01:57:02 +0900
committernsfisis <nsfisis@gmail.com>2026-08-18 01:57:02 +0900
commit530d085d4f3e19f94ac3cf8f8ac3b17000214b2e (patch)
treeb4de2c2443e2bb2cfc692454ac284dc1d2313e59 /crates/shirabe/src/util/git.rs
parent0caac63bacefb9a1f62848636d47fca07f592bba (diff)
downloadphp-shirabe-530d085d4f3e19f94ac3cf8f8ac3b17000214b2e.tar.gz
php-shirabe-530d085d4f3e19f94ac3cf8f8ac3b17000214b2e.tar.zst
php-shirabe-530d085d4f3e19f94ac3cf8f8ac3b17000214b2e.zip
refactor(pcre): inline Preg into its call sites and drop the crate
Preg had shed everything it owned: after the last few rounds its methods were one-line forwards to the shim's preg_*(), differing only in a default argument or a wrapper the caller unwrapped anyway. The 460 call sites now name the shim function, and shirabe-pcre is gone from the workspace along with its LICENSE entry. The forwards expand as they read: isMatch becomes preg_match2(.., 0).is_some() (is_none() where PHP negates it), isMatch3 and match3 drop the .is_some(), matchAll counts through preg_match_all2(..).occurrence_count(), and replace4/replace5 spell out the limit and count arguments preg_replace2 takes. Callbacks are the one place the shapes differ: preg_replace_callback carries an error out of the callback, so the fourteen infallible closures wrap their result in Ok() and expect() it back. Config::process() is the fifteenth, and it drops the `error` cell it captured to smuggle a failure past a closure that could only return a String. The `?` in the closure now carries it, which is what the PHP does -- a throw from the callback leaves preg_replace_callback at the failing match rather than running the remaining replacements and reporting the last error. The module doc that explained why composer/pcre's exceptions and *StrictGroups() variants have no counterpart moves to the shim's preg module, where the functions it describes live. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe/src/util/git.rs')
-rw-r--r--crates/shirabe/src/util/git.rs84
1 files changed, 51 insertions, 33 deletions
diff --git a/crates/shirabe/src/util/git.rs b/crates/shirabe/src/util/git.rs
index e14ff05f..31771e6c 100644
--- a/crates/shirabe/src/util/git.rs
+++ b/crates/shirabe/src/util/git.rs
@@ -14,12 +14,11 @@ use crate::util::ProcessExecutor;
use crate::util::Url;
use crate::util::{AuthHelper, StoreAuth};
use indexmap::IndexMap;
-use shirabe_pcre::{Preg, PregMatches};
use shirabe_php_shim::{
- AnyThrowable, CmpOp, InvalidArgumentException, PHP_EOL, PhpMixed, RuntimeException, array_map,
- clearstatcache, explode, implode, in_array_loose, in_array_strict, is_dir, php_regex,
- preg_quote, rawurldecode, rawurlencode, str_replace_array, strlen, strpos, substr, trim,
- version_compare,
+ AnyThrowable, CmpOp, InvalidArgumentException, PHP_EOL, PhpMixed, PregMatches,
+ RuntimeException, array_map, clearstatcache, explode, implode, in_array_loose, in_array_strict,
+ is_dir, php_regex, preg_match2, preg_quote, preg_replace, rawurldecode, rawurlencode,
+ str_replace_array, strlen, strpos, substr, trim, version_compare,
};
use std::sync::Mutex;
@@ -110,7 +109,7 @@ impl Git {
map.insert("%url%".to_string(), url.to_string());
map.insert(
"%sanitizedUrl%".to_string(),
- Preg::replace(php_regex!(r"{://([^@]+?):(.+?)@}"), "://", url),
+ preg_replace(php_regex!(r"{://([^@]+?):(.+?)@}"), "://", url),
);
array_map(
@@ -210,7 +209,7 @@ impl Git {
status
};
- if Preg::is_match(php_regex!(r"{^ssh://[^@]+@[^:]+:[^0-9]+}"), url) {
+ if preg_match2(php_regex!(r"{^ssh://[^@]+@[^:]+:[^0-9]+}"), url, 0).is_some() {
return Err(InvalidArgumentException::new(format!(
"The source URL {} is invalid, ssh URLs should have a port number after \":\".\nUse ssh://git@example.com:22/path or just git@example.com:path if you do not want to provide a password or custom port.",
url
@@ -226,9 +225,10 @@ impl Git {
&mut output,
cwd,
)?;
- if let Some(m) = Preg::is_match3(
+ if let Some(m) = preg_match2(
php_regex!(r"{^(?:composer|origin)\s+https?://(.+):(.+)@([^/]+)}im"),
&output,
+ 0,
) {
let m3 = m.get(3).unwrap_or_default().to_string();
if !self.io.has_authentication(&m3) {
@@ -244,12 +244,13 @@ impl Git {
let protocols = self.config.borrow_mut().get("github-protocols");
// public github, autoswitch protocols
// @phpstan-ignore composerPcre.maybeUnsafeStrictGroups
- if let Some(m) = Preg::is_match3(
+ if let Some(m) = preg_match2(
format!(
"{{^(?:https?|git)://{}/(.*)}}",
Self::get_github_domains_regex(&self.config.borrow())
),
url,
+ 0,
) {
let mut messages: Vec<String> = vec![];
let protocols_list: Vec<String> = match &protocols {
@@ -280,7 +281,7 @@ impl Git {
messages.push(format!(
"- {}\n{}",
proto_url,
- Preg::replace(r"#^#m", " ", self.process.borrow().get_error_output())
+ preg_replace(r"#^#m", " ", self.process.borrow().get_error_output())
));
if initial_clone && let Some(ref orig) = orig_cwd {
@@ -311,19 +312,22 @@ impl Git {
.collect(),
_ => vec![],
};
- let bypass_ssh_for_github = Preg::is_match(
+ let bypass_ssh_for_github = preg_match2(
format!(
"{{^git@{}:(.+?)\\.git$}}i",
Self::get_github_domains_regex(&self.config.borrow())
),
url,
- ) && !in_array_strict(
- "ssh".to_string(),
- &protocols_list
- .iter()
- .map(|s| PhpMixed::String(s.clone()))
- .collect::<Vec<_>>(),
- );
+ 0,
+ )
+ .is_some()
+ && !in_array_strict(
+ "ssh".to_string(),
+ &protocols_list
+ .iter()
+ .map(|s| PhpMixed::String(s.clone()))
+ .collect::<Vec<_>>(),
+ );
let mut auth: Option<IndexMap<String, Option<String>>> = None;
let mut credentials: Vec<String> = vec![];
@@ -338,20 +342,22 @@ impl Git {
let mut error_msg = self.process.borrow().get_error_output().to_string();
// private github repository without ssh key access, try https with auth
// @phpstan-ignore composerPcre.maybeUnsafeStrictGroups
- let github_matched = Preg::is_match3(
+ let github_matched = preg_match2(
format!(
"{{^git@{}:(.+?)\\.git$}}i",
Self::get_github_domains_regex(&self.config.borrow())
),
url,
+ 0,
)
.or_else(|| {
- Preg::is_match3(
+ preg_match2(
format!(
"{{^https?://{}/(.*?)(?:\\.git)?$}}i",
Self::get_github_domains_regex(&self.config.borrow())
),
url,
+ 0,
)
});
if let Some(m) = github_matched {
@@ -404,12 +410,18 @@ impl Git {
credentials = vec![rawurlencode(&username), rawurlencode(&password)];
error_msg = self.process.borrow().get_error_output().to_string();
}
- } else if let Some(m) = Preg::is_match3(
+ } else if let Some(m) = preg_match2(
php_regex!(r"{^(https?)://(bitbucket\.org)/(.*?)(?:\.git)?$}i"),
url,
+ 0,
)
- .or_else(|| Preg::is_match3(php_regex!(r"{^(git)@(bitbucket\.org):(.+?\.git)$}i"), url))
- {
+ .or_else(|| {
+ preg_match2(
+ php_regex!(r"{^(git)@(bitbucket\.org):(.+?\.git)$}i"),
+ url,
+ 0,
+ )
+ }) {
// bitbucket either through oauth or app password, with fallback to ssh.
let mut bitbucket_util = Bitbucket::new(
self.io.clone(),
@@ -546,20 +558,22 @@ impl Git {
}
error_msg = self.process.borrow().get_error_output().to_string();
- } else if let Some(m) = Preg::is_match3(
+ } else if let Some(m) = preg_match2(
format!(
"{{^(git)@{}:(.+?\\.git)$}}i",
Self::get_gitlab_domains_regex(&self.config.borrow())
),
url,
+ 0,
)
.or_else(|| {
- Preg::is_match3(
+ preg_match2(
format!(
"{{^(https?)://{}/(.*)}}i",
Self::get_gitlab_domains_regex(&self.config.borrow())
),
url,
+ 0,
)
}) {
let mut m1 = m.get(1).unwrap_or_default().to_string();
@@ -914,10 +928,10 @@ impl Git {
pretty_version: Option<&str>,
) -> anyhow::Result<bool> {
if self.check_ref_is_in_mirror(dir, r#ref)? {
- if Preg::is_match(php_regex!(r"{^[a-f0-9]{40}$}"), r#ref)
+ if preg_match2(php_regex!(r"{^[a-f0-9]{40}$}"), r#ref, 0).is_some()
&& let Some(pretty_version) = pretty_version
{
- let branch = Preg::replace(
+ let branch = preg_replace(
php_regex!(r"{(?:^dev-|(?:\.x)?-dev$)}i"),
"",
pretty_version,
@@ -948,15 +962,19 @@ impl Git {
// this can occur if a git tag gets created *after* the reference is already put into the cache, as the ref check above will then not sync the new tags
// see https://github.com/composer/composer/discussions/11002
if branches.is_some()
- && !Preg::is_match(
+ && preg_match2(
format!(r"{{^[\s*]*v?{}$}}m", preg_quote(&branch, None)),
branches.as_deref().unwrap_or(""),
+ 0,
)
+ .is_none()
&& tags.is_some()
- && !Preg::is_match(
+ && preg_match2(
format!(r"{{^[\s*]*{}$}}m", preg_quote(&branch, None)),
tags.as_deref().unwrap_or(""),
+ 0,
)
+ .is_none()
{
self.sync_mirror(url, dir)?;
}
@@ -1042,7 +1060,7 @@ impl Git {
}
// Filter out "commit <hash>" lines for older git versions
- Preg::replace(php_regex!(r"{^commit [a-f0-9]{40}\n?}m"), "", output)
+ preg_replace(php_regex!(r"{^commit [a-f0-9]{40}\n?}m"), "", output)
}
fn check_ref_is_in_mirror(&mut self, dir: &str, r#ref: &str) -> anyhow::Result<bool> {
@@ -1081,7 +1099,7 @@ impl Git {
}
fn get_authentication_failure<'u>(&self, url: &'u str) -> Option<PregMatches<'u>> {
- let m = Preg::is_match3(php_regex!(r"{^(https?://)([^/]+)(.*)$}i"), url)?;
+ let m = preg_match2(php_regex!(r"{^(https?://)([^/]+)(.*)$}i"), url, 0)?;
let auth_failures = [
"fatal: Authentication failed",
@@ -1164,7 +1182,7 @@ impl Git {
.split_lines(output_mixed.as_string().unwrap_or(""));
for line in lines {
if let Some(matches) =
- Preg::is_match3(php_regex!(r"{^\s*HEAD branch:\s(.+)\s*$}m"), &line)
+ preg_match2(php_regex!(r"{^\s*HEAD branch:\s(.+)\s*$}m"), &line, 0)
{
return Ok(Some(matches.get(1).unwrap_or_default().to_string()));
}
@@ -1285,7 +1303,7 @@ impl Git {
);
if exit_code == 0
&& let Some(matches) =
- Preg::is_match3(php_regex!(r"/^git version (\d+(?:\.\d+)+)/m"), &output)
+ preg_match2(php_regex!(r"/^git version (\d+(?:\.\d+)+)/m"), &output, 0)
{
*version = Some(matches.get(1).map(str::to_string));
}