aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src/repository
diff options
context:
space:
mode:
Diffstat (limited to 'crates/shirabe/src/repository')
-rw-r--r--crates/shirabe/src/repository/array_repository.rs52
-rw-r--r--crates/shirabe/src/repository/artifact_repository.rs20
-rw-r--r--crates/shirabe/src/repository/canonical_packages_trait.rs17
-rw-r--r--crates/shirabe/src/repository/composer_repository.rs87
-rw-r--r--crates/shirabe/src/repository/composite_repository.rs21
-rw-r--r--crates/shirabe/src/repository/filesystem_repository.rs76
-rw-r--r--crates/shirabe/src/repository/filter_repository.rs8
-rw-r--r--crates/shirabe/src/repository/installed_array_repository.rs14
-rw-r--r--crates/shirabe/src/repository/installed_filesystem_repository.rs6
-rw-r--r--crates/shirabe/src/repository/installed_repository.rs24
-rw-r--r--crates/shirabe/src/repository/lock_array_repository.rs4
-rw-r--r--crates/shirabe/src/repository/package_repository.rs90
-rw-r--r--crates/shirabe/src/repository/path_repository.rs74
-rw-r--r--crates/shirabe/src/repository/platform_repository.rs565
-rw-r--r--crates/shirabe/src/repository/repository_factory.rs53
-rw-r--r--crates/shirabe/src/repository/repository_interface.rs13
-rw-r--r--crates/shirabe/src/repository/repository_manager.rs18
-rw-r--r--crates/shirabe/src/repository/repository_set.rs43
-rw-r--r--crates/shirabe/src/repository/repository_utils.rs14
-rw-r--r--crates/shirabe/src/repository/root_package_repository.rs4
-rw-r--r--crates/shirabe/src/repository/vcs/forgejo_driver.rs46
-rw-r--r--crates/shirabe/src/repository/vcs/fossil_driver.rs58
-rw-r--r--crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs46
-rw-r--r--crates/shirabe/src/repository/vcs/git_driver.rs108
-rw-r--r--crates/shirabe/src/repository/vcs/github_driver.rs202
-rw-r--r--crates/shirabe/src/repository/vcs/gitlab_driver.rs165
-rw-r--r--crates/shirabe/src/repository/vcs/hg_driver.rs55
-rw-r--r--crates/shirabe/src/repository/vcs/perforce_driver.rs1
-rw-r--r--crates/shirabe/src/repository/vcs/svn_driver.rs132
-rw-r--r--crates/shirabe/src/repository/vcs/vcs_driver.rs65
-rw-r--r--crates/shirabe/src/repository/vcs/vcs_driver_interface.rs2
-rw-r--r--crates/shirabe/src/repository/vcs_repository.rs23
-rw-r--r--crates/shirabe/src/repository/version_cache_interface.rs2
-rw-r--r--crates/shirabe/src/repository/writable_array_repository.rs20
34 files changed, 1373 insertions, 755 deletions
diff --git a/crates/shirabe/src/repository/array_repository.rs b/crates/shirabe/src/repository/array_repository.rs
index 5652dad..ef15b39 100644
--- a/crates/shirabe/src/repository/array_repository.rs
+++ b/crates/shirabe/src/repository/array_repository.rs
@@ -53,10 +53,7 @@ impl ArrayRepository {
/// Adds a new package to the repository
pub fn add_package(&self, package: Box<dyn PackageInterface>) -> Result<()> {
// PHP: if (!$package instanceof BasePackage) throw new \InvalidArgumentException(...)
- if (package.as_any() as &dyn Any)
- .downcast_ref::<BasePackage>()
- .is_none()
- {
+ if package.as_any().downcast_ref::<dyn BasePackage>().is_none() {
return Err(InvalidArgumentException {
message: "Only subclasses of BasePackage are supported".to_string(),
code: 0,
@@ -74,7 +71,7 @@ impl ArrayRepository {
package.set_repository(todo!("self as Box<dyn RepositoryInterface>"))?;
let aliased_package: Option<Box<dyn BasePackage>> =
- if let Some(alias) = (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>() {
+ if let Some(alias) = package.as_any().downcast_ref::<AliasPackage>() {
Some(alias.get_alias_of().clone_box())
} else {
None
@@ -101,14 +98,11 @@ impl ArrayRepository {
alias: String,
pretty_alias: String,
) -> Box<dyn BasePackage> {
- while let Some(alias_pkg) = (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>() {
+ while let Some(alias_pkg) = package.as_any().downcast_ref::<AliasPackage>() {
package = alias_pkg.get_alias_of().clone_box();
}
- if (package.as_any() as &dyn Any)
- .downcast_ref::<CompletePackage>()
- .is_some()
- {
+ if package.as_any().downcast_ref::<CompletePackage>().is_some() {
// TODO(phase-b): construct CompleteAliasPackage/AliasPackage and return as Box<BasePackage>
return todo!("new CompleteAliasPackage(package, alias, pretty_alias)");
}
@@ -199,9 +193,7 @@ impl RepositoryInterface for ArrayRepository {
// add selected packages which match stability requirements
result.insert(spl_object_hash(package.as_ref()), package.clone_box());
// add the aliased package for packages where the alias matches
- if let Some(alias) =
- (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>()
- {
+ if let Some(alias) = package.as_any().downcast_ref::<AliasPackage>() {
let aliased = alias.get_alias_of();
if !result.contains_key(&spl_object_hash(aliased.as_ref())) {
result.insert(spl_object_hash(aliased.as_ref()), aliased.clone_box());
@@ -218,7 +210,7 @@ impl RepositoryInterface for ArrayRepository {
// add aliases of packages that were selected, even if the aliases did not match
for package in &packages {
- if let Some(alias) = (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>() {
+ if let Some(alias) = package.as_any().downcast_ref::<AliasPackage>() {
let aliased = alias.get_alias_of();
if result.contains_key(&spl_object_hash(aliased.as_ref())) {
result.insert(spl_object_hash(package.as_ref()), package.clone_box());
@@ -234,17 +226,16 @@ impl RepositoryInterface for ArrayRepository {
fn find_package(
&self,
- name: String,
+ name: &str,
constraint: FindPackageConstraint,
) -> Option<Box<dyn BasePackage>> {
- let name = strtolower(&name);
+ let name = strtolower(name);
let constraint: Box<dyn ConstraintInterface> = match constraint {
FindPackageConstraint::Constraint(c) => c,
FindPackageConstraint::String(s) => {
let version_parser = VersionParser::new();
- // TODO(phase-b): Arc<dyn ConstraintInterface + Send + Sync> -> Box<dyn ConstraintInterface>
- Box::new(version_parser.parse_constraints(&s).unwrap())
+ version_parser.parse_constraints(&s).unwrap().clone_box()
}
};
@@ -262,11 +253,11 @@ impl RepositoryInterface for ArrayRepository {
fn find_packages(
&self,
- name: String,
+ name: &str,
constraint: Option<FindPackageConstraint>,
) -> Vec<Box<dyn BasePackage>> {
// normalize name
- let name = strtolower(&name);
+ let name = strtolower(name);
let mut packages = vec![];
let constraint: Option<Box<dyn ConstraintInterface>> = match constraint {
@@ -274,8 +265,7 @@ impl RepositoryInterface for ArrayRepository {
Some(FindPackageConstraint::Constraint(c)) => Some(c),
Some(FindPackageConstraint::String(s)) => {
let version_parser = VersionParser::new();
- // TODO(phase-b): Arc<dyn ConstraintInterface + Send + Sync> -> Box<dyn ConstraintInterface>
- Some(Box::new(version_parser.parse_constraints(&s).unwrap()))
+ Some(version_parser.parse_constraints(&s).unwrap().clone_box())
}
};
@@ -296,7 +286,7 @@ impl RepositoryInterface for ArrayRepository {
}
fn search(&self, query: String, mode: i64, r#type: Option<String>) -> Vec<SearchResult> {
- let regex = if mode == Self::SEARCH_FULLTEXT {
+ let regex = if mode == crate::repository::repository_interface::SEARCH_FULLTEXT {
format!(
"{{(?:{})}}i",
implode("|", &Preg::split("{\\s+}", &preg_quote(&query, None)))
@@ -309,7 +299,7 @@ impl RepositoryInterface for ArrayRepository {
let mut matches: IndexMap<String, SearchResult> = IndexMap::new();
for package in self.get_packages() {
let mut name = PackageInterface::get_name(package.as_ref()).to_string();
- if mode == Self::SEARCH_VENDOR {
+ if mode == crate::repository::repository_interface::SEARCH_VENDOR {
// PHP: [$name] = explode('/', $name);
let parts: Vec<&str> = name.splitn(2, '/').collect();
name = parts[0].to_string();
@@ -323,9 +313,9 @@ impl RepositoryInterface for ArrayRepository {
}
}
- let complete = (package.as_any() as &dyn Any).downcast_ref::<CompletePackage>();
+ let complete = package.as_any().downcast_ref::<CompletePackage>();
- let fulltext_match = mode == Self::SEARCH_FULLTEXT
+ let fulltext_match = mode == crate::repository::repository_interface::SEARCH_FULLTEXT
&& complete.is_some()
&& Preg::is_match(
&regex,
@@ -334,10 +324,11 @@ impl RepositoryInterface for ArrayRepository {
implode(" ", &complete.unwrap().get_keywords()),
complete.unwrap().get_description().unwrap_or("")
),
- );
+ )
+ .unwrap_or(false);
- if Preg::is_match(&regex, &name) || fulltext_match {
- if mode == Self::SEARCH_VENDOR {
+ if Preg::is_match(&regex, &name).unwrap_or(false) || fulltext_match {
+ if mode == crate::repository::repository_interface::SEARCH_VENDOR {
matches.insert(
name.clone(),
SearchResult {
@@ -405,8 +396,7 @@ impl RepositoryInterface for ArrayRepository {
}
for link in candidate.get_provides().values() {
if package_name == link.get_target() {
- let complete =
- (candidate.as_any() as &dyn Any).downcast_ref::<CompletePackage>();
+ let complete = candidate.as_any().downcast_ref::<CompletePackage>();
let description = complete.and_then(|c| c.get_description().map(String::from));
result.insert(
PackageInterface::get_name(candidate.as_ref()).to_string(),
diff --git a/crates/shirabe/src/repository/artifact_repository.rs b/crates/shirabe/src/repository/artifact_repository.rs
index faef831..afaf9cb 100644
--- a/crates/shirabe/src/repository/artifact_repository.rs
+++ b/crates/shirabe/src/repository/artifact_repository.rs
@@ -106,7 +106,7 @@ impl ArtifactRepository {
let basename = file_path.file_name().and_then(|n| n.to_str()).unwrap_or("");
match package {
None => {
- self.io.write_error(
+ self.io.write_error3(
&format!(
"File <comment>{}</comment> doesn't seem to hold a package",
basename
@@ -116,16 +116,12 @@ impl ArtifactRepository {
);
}
Some(package) => {
- self.io.write_error(
- &format!(
- "Found package <info>{}</info> (<comment>{}</comment>) in file <info>{}</info>",
- package.get_name(),
- package.get_pretty_version(),
- basename,
- ),
- true,
- io_interface::VERBOSE,
- );
+ self.io.write_error3(&format!(
+ "Found package <info>{}</info> (<comment>{}</comment>) in file <info>{}</info>",
+ package.get_name(),
+ package.get_pretty_version(),
+ basename,
+ ), true, io_interface::VERBOSE);
self.inner.add_package(package);
}
}
@@ -169,7 +165,7 @@ impl ArtifactRepository {
match get_result {
Ok(j) => json = j,
Err(exception) => {
- self.io.write(
+ self.io.write3(
&format!("Failed loading package {}: {}", pathname, exception),
false,
io_interface::VERBOSE,
diff --git a/crates/shirabe/src/repository/canonical_packages_trait.rs b/crates/shirabe/src/repository/canonical_packages_trait.rs
index 8fae936..3c9c75c 100644
--- a/crates/shirabe/src/repository/canonical_packages_trait.rs
+++ b/crates/shirabe/src/repository/canonical_packages_trait.rs
@@ -14,8 +14,12 @@ pub trait CanonicalPackagesTrait {
// get at most one package of each name, preferring non-aliased ones
let mut packages_by_name: IndexMap<String, Box<dyn PackageInterface>> = IndexMap::new();
for package in packages {
- let name = package.get_name();
- if !packages_by_name.contains_key(&name) || packages_by_name[&name].is_alias_package() {
+ let name = package.get_name().to_string();
+ let prefer_replace = packages_by_name
+ .get(&name)
+ .map(|existing| existing.as_alias_package().is_some())
+ .unwrap_or(true);
+ if prefer_replace {
packages_by_name.insert(name, package);
}
}
@@ -23,11 +27,10 @@ pub trait CanonicalPackagesTrait {
let mut canonical_packages = Vec::new();
// unfold aliased packages
- for mut package in packages_by_name.into_values() {
- while package.is_alias_package() {
- package = package.get_alias_of();
- }
-
+ for package in packages_by_name.into_values() {
+ // TODO(phase-b): unfolding requires `Box<dyn PackageInterface>` traversal of
+ // `AliasPackage::get_alias_of()` (currently returns `&BasePackage`, not an
+ // ownable trait object). Push the alias as-is for now.
canonical_packages.push(package);
}
diff --git a/crates/shirabe/src/repository/composer_repository.rs b/crates/shirabe/src/repository/composer_repository.rs
index dfbb0b5..1e6443f 100644
--- a/crates/shirabe/src/repository/composer_repository.rs
+++ b/crates/shirabe/src/repository/composer_repository.rs
@@ -2,7 +2,7 @@
use indexmap::IndexMap;
use shirabe_external_packages::composer::metadata_minifier::metadata_minifier::MetadataMinifier;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_external_packages::react::promise::promise_interface::PromiseInterface;
use shirabe_php_shim::{
InvalidArgumentException, JSON_UNESCAPED_SLASHES, JSON_UNESCAPED_UNICODE, LogicException,
@@ -84,7 +84,7 @@ pub struct ComposerRepository {
/// non-empty-string
base_url: String,
io: Box<dyn IOInterface>,
- http_downloader: HttpDownloader,
+ http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
r#loop: std::rc::Rc<std::cell::RefCell<Loop>>,
pub(crate) cache: Cache,
pub(crate) notify_url: Option<String>,
@@ -148,7 +148,7 @@ impl ComposerRepository {
mut repo_config: IndexMap<String, PhpMixed>,
io: Box<dyn IOInterface>,
config: &Config,
- http_downloader: HttpDownloader,
+ http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
event_dispatcher: Option<EventDispatcher>,
) -> anyhow::Result<Self> {
// parent::__construct();
@@ -246,13 +246,16 @@ impl ComposerRepository {
.to_string();
// force url for packagist.org to repo.packagist.org
- let mut match_packagist: Vec<String> = Vec::new();
- if Preg::is_match_with_matches(
+ let mut match_packagist: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(
r"{^(?P<proto>https?)://packagist\.org/?$}i",
&url,
- &mut match_packagist,
+ Some(&mut match_packagist),
)? {
- let proto = match_packagist.get(1).cloned().unwrap_or_default();
+ let proto = match_packagist
+ .get(&CaptureKey::ByName("proto".to_string()))
+ .cloned()
+ .unwrap_or_default();
url = format!("{}://repo.packagist.org", proto);
}
@@ -272,7 +275,7 @@ impl ComposerRepository {
let loader = ArrayLoader::new_with_parser(version_parser.clone());
let r#loop = std::rc::Rc::new(std::cell::RefCell::new(Loop::new(
- http_downloader.clone(),
+ std::rc::Rc::clone(&http_downloader),
None,
)));
@@ -336,10 +339,10 @@ impl ComposerRepository {
let name = strtolower(&name);
let constraint: Box<dyn ConstraintInterface> = match constraint {
- PhpMixed::String(s) => self.version_parser.parse_constraints(&s)?,
+ PhpMixed::String(s) => self.version_parser.parse_constraints(&s)?.clone_box(),
_ => {
// already a ConstraintInterface object passed as opaque PhpMixed
- self.version_parser.parse_constraints("")?
+ self.version_parser.parse_constraints("")?.clone_box()
}
};
@@ -393,7 +396,10 @@ impl ComposerRepository {
return Ok(None);
}
- Ok(self.inner.find_package(name, Some(constraint)))
+ Ok(self.inner.find_package(
+ &name,
+ crate::repository::repository_interface::FindPackageConstraint::Constraint(constraint),
+ ))
}
/// @inheritDoc
@@ -408,7 +414,9 @@ impl ComposerRepository {
let name = strtolower(&name);
let constraint: Option<Box<dyn ConstraintInterface>> = match constraint {
None => None,
- Some(PhpMixed::String(s)) => Some(self.version_parser.parse_constraints(&s)?),
+ Some(PhpMixed::String(s)) => {
+ Some(self.version_parser.parse_constraints(&s)?.clone_box())
+ }
Some(_) => None,
};
@@ -458,7 +466,11 @@ impl ComposerRepository {
return Ok(vec![]);
}
- Ok(self.inner.find_packages(name, constraint))
+ Ok(self.inner.find_packages(
+ &name,
+ constraint
+ .map(crate::repository::repository_interface::FindPackageConstraint::Constraint),
+ ))
}
fn filter_packages(
@@ -571,7 +583,10 @@ impl ComposerRepository {
};
let filter_results = |results: Vec<String>| -> anyhow::Result<Vec<String>> {
match &package_filter_regex {
- Some(regex) => Ok(Preg::grep(regex, &results)?),
+ Some(regex) => {
+ let results_refs: Vec<&str> = results.iter().map(|s| s.as_str()).collect();
+ Ok(Preg::grep(regex, &results_refs)?)
+ }
None => Ok(results),
}
};
@@ -658,6 +673,7 @@ impl ComposerRepository {
url.push_str(&format!("?filter={}", urlencode(filter)));
let result = self
.http_downloader
+ .borrow_mut()
.get(&url, &self.options)?
.decode_json()?;
let package_names: Vec<String> = result
@@ -689,6 +705,7 @@ impl ComposerRepository {
let result = self
.http_downloader
+ .borrow_mut()
.get(&url, &self.options)?
.decode_json()?;
let package_names: Vec<String> = result
@@ -914,15 +931,21 @@ impl ComposerRepository {
if self.has_providers()? || self.lazy_providers_url.is_some() {
// optimize search for "^foo/bar" where at least "^foo/" is present by loading this directly from the listUrl if present
- let mut match_groups: Vec<String> = Vec::new();
- if Preg::is_match_strict_groups(
+ let mut match_groups: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
r"{^\^(?P<query>(?P<vendor>[a-z0-9_.-]+)/[a-z0-9_.-]*)\*?$}i",
&query,
- &mut match_groups,
+ Some(&mut match_groups),
)? && self.list_url.is_some()
{
- let q = match_groups.get(1).cloned().unwrap_or_default();
- let vendor = match_groups.get(2).cloned().unwrap_or_default();
+ let q = match_groups
+ .get(&CaptureKey::ByName("query".to_string()))
+ .cloned()
+ .unwrap_or_default();
+ let vendor = match_groups
+ .get(&CaptureKey::ByName("vendor".to_string()))
+ .cloned()
+ .unwrap_or_default();
let url = format!(
"{}?vendor={}&filter={}",
self.list_url.as_ref().unwrap(),
@@ -931,6 +954,7 @@ impl ComposerRepository {
);
let result = self
.http_downloader
+ .borrow_mut()
.get(&url, &self.options)?
.decode_json()?;
@@ -1179,7 +1203,7 @@ impl ComposerRepository {
http_map.insert("content".to_string(), Box::new(PhpMixed::String(body)));
}
- let response = self.http_downloader.get(&api_url, &options)?;
+ let response = self.http_downloader.borrow_mut().get(&api_url, &options)?;
let mut warned = false;
let decoded = response.decode_json()?;
let advisories_response = decoded
@@ -1245,7 +1269,7 @@ impl ComposerRepository {
let mut result: IndexMap<String, IndexMap<String, PhpMixed>> = IndexMap::new();
if let Some(providers_api_url) = self.providers_api_url.clone() {
- let api_result = match self.http_downloader.get(
+ let api_result = match self.http_downloader.borrow_mut().get(
&providers_api_url.replace("%package%", package_name),
&self.options,
) {
@@ -2514,11 +2538,14 @@ impl ComposerRepository {
}
if url.starts_with('/') {
- let mut matches: Vec<String> = Vec::new();
- if Preg::is_match_with_matches(r"{^[^:]++://[^/]*+}", &self.url, &mut matches)? {
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(r"{^[^:]++://[^/]*+}", &self.url, Some(&mut matches))? {
return Ok(format!(
"{}{}",
- matches.get(0).cloned().unwrap_or_default(),
+ matches
+ .get(&CaptureKey::ByIndex(0))
+ .cloned()
+ .unwrap_or_default(),
url
));
}
@@ -2823,7 +2850,7 @@ impl ComposerRepository {
if let Some(dispatcher) = self.event_dispatcher.as_mut() {
let mut pre_file_download_event = PreFileDownloadEvent::new(
PluginEvents::PRE_FILE_DOWNLOAD.to_string(),
- &self.http_downloader,
+ std::rc::Rc::clone(&self.http_downloader),
filename.clone(),
"metadata".to_string(),
{
@@ -2842,7 +2869,7 @@ impl ComposerRepository {
options = pre_file_download_event.get_transport_options();
}
- let response = self.http_downloader.get(&filename, &options)?;
+ let response = self.http_downloader.borrow_mut().get(&filename, &options)?;
let mut json = response.get_body().to_string();
if let Some(sha256_val) = sha256 {
if sha256_val != hash("sha256", &json) {
@@ -3004,7 +3031,7 @@ impl ComposerRepository {
if let Some(dispatcher) = self.event_dispatcher.as_mut() {
let mut pre_file_download_event = PreFileDownloadEvent::new(
PluginEvents::PRE_FILE_DOWNLOAD.to_string(),
- &self.http_downloader,
+ std::rc::Rc::clone(&self.http_downloader),
filename.clone(),
"metadata".to_string(),
{
@@ -3048,7 +3075,7 @@ impl ComposerRepository {
http_map.insert("header".to_string(), Box::new(PhpMixed::List(headers)));
}
- let response = self.http_downloader.get(&filename, &options)?;
+ let response = self.http_downloader.borrow_mut().get(&filename, &options)?;
let mut json = response.get_body().to_string();
if json.is_empty() && response.get_status_code() == 304 {
return Ok(FetchFileIfLastModifiedResult::NotModified);
@@ -3159,7 +3186,7 @@ impl ComposerRepository {
if let Some(dispatcher) = self.event_dispatcher.as_mut() {
let mut pre_file_download_event = PreFileDownloadEvent::new(
PluginEvents::PRE_FILE_DOWNLOAD.to_string(),
- &self.http_downloader,
+ std::rc::Rc::clone(&self.http_downloader),
filename.clone(),
"metadata".to_string(),
{
@@ -3332,7 +3359,7 @@ impl ComposerRepository {
}
};
- let initial = self.http_downloader.add(&filename, &options)?;
+ let initial = self.http_downloader.borrow_mut().add(&filename, &options)?;
Ok(initial.then_with_reject_boxed(Box::new(accept), Box::new(reject)))
}
diff --git a/crates/shirabe/src/repository/composite_repository.rs b/crates/shirabe/src/repository/composite_repository.rs
index 4914538..d956cdd 100644
--- a/crates/shirabe/src/repository/composite_repository.rs
+++ b/crates/shirabe/src/repository/composite_repository.rs
@@ -31,17 +31,16 @@ impl CompositeRepository {
&self.repositories
}
- pub fn remove_package(&mut self, package: &dyn PackageInterface) {
- for repository in &mut self.repositories {
- // TODO(phase-b): only call remove_package on WritableRepositoryInterface implementors
- let _ = repository.remove_package(package);
+ pub fn remove_package(&mut self, _package: &dyn PackageInterface) {
+ // TODO(phase-b): only call remove_package on WritableRepositoryInterface implementors;
+ // requires a downcast helper such as `as_writable() -> Option<&mut dyn WritableRepositoryInterface>` on RepositoryInterface.
+ for _repository in &mut self.repositories {
+ todo!()
}
}
pub fn add_repository(&mut self, repository: Box<dyn RepositoryInterface>) {
- if let Some(composite) =
- (repository.as_any() as &dyn Any).downcast_ref::<CompositeRepository>()
- {
+ if let Some(composite) = repository.as_any().downcast_ref::<CompositeRepository>() {
for repo in composite.get_repositories() {
self.repositories.push(repo.clone_box());
}
@@ -78,11 +77,11 @@ impl RepositoryInterface for CompositeRepository {
fn find_package(
&self,
- name: String,
+ name: &str,
constraint: FindPackageConstraint,
) -> Option<Box<dyn BasePackage>> {
for repository in &self.repositories {
- let package = repository.find_package(name.clone(), constraint.clone());
+ let package = repository.find_package(name, constraint.clone());
if package.is_some() {
return package;
}
@@ -92,12 +91,12 @@ impl RepositoryInterface for CompositeRepository {
fn find_packages(
&self,
- name: String,
+ name: &str,
constraint: Option<FindPackageConstraint>,
) -> Vec<Box<dyn BasePackage>> {
let mut packages = vec![];
for repository in &self.repositories {
- packages.extend(repository.find_packages(name.clone(), constraint.clone()));
+ packages.extend(repository.find_packages(name, constraint.clone()));
}
packages
}
diff --git a/crates/shirabe/src/repository/filesystem_repository.rs b/crates/shirabe/src/repository/filesystem_repository.rs
index 3d785e7..4ce8585 100644
--- a/crates/shirabe/src/repository/filesystem_repository.rs
+++ b/crates/shirabe/src/repository/filesystem_repository.rs
@@ -19,6 +19,7 @@ use crate::json::json_file::JsonFile;
use crate::package::alias_package::AliasPackage;
use crate::package::dumper::array_dumper::ArrayDumper;
use crate::package::loader::array_loader::ArrayLoader;
+use crate::package::loader::loader_interface::LoaderInterface;
use crate::package::package_interface::PackageInterface;
use crate::package::root_alias_package::RootAliasPackage;
use crate::package::root_package_interface::RootPackageInterface;
@@ -39,7 +40,7 @@ pub struct FilesystemRepository {
/// @var ?RootPackageInterface
root_package: Option<Box<dyn RootPackageInterface>>,
/// @var Filesystem
- filesystem: Filesystem,
+ filesystem: std::rc::Rc<std::cell::RefCell<Filesystem>>,
/// @var bool|null
dev_mode: Option<bool>,
}
@@ -53,9 +54,10 @@ impl FilesystemRepository {
repository_file: JsonFile,
dump_versions: bool,
root_package: Option<Box<dyn RootPackageInterface>>,
- filesystem: Option<Filesystem>,
+ filesystem: Option<std::rc::Rc<std::cell::RefCell<Filesystem>>>,
) -> Result<Self> {
- let filesystem = filesystem.unwrap_or_else(|| Filesystem::new(None));
+ let filesystem = filesystem
+ .unwrap_or_else(|| std::rc::Rc::new(std::cell::RefCell::new(Filesystem::new(None))));
if dump_versions && root_package.is_none() {
return Err(InvalidArgumentException {
message: "Expected a root package instance if $dumpVersions is true".to_string(),
@@ -191,10 +193,13 @@ impl FilesystemRepository {
// as realpath() does some additional normalizations with network paths that normalizePath does not
// and we need to find shortest path correctly
let repo_dir = dirname(self.file.get_path());
- self.filesystem.ensure_directory_exists(&repo_dir);
+ self.filesystem
+ .borrow_mut()
+ .ensure_directory_exists(&repo_dir);
let repo_dir = self
.filesystem
+ .borrow()
.normalize_path(&realpath(&repo_dir).unwrap_or_default());
let mut install_paths: IndexMap<String, Option<String>> = IndexMap::new();
@@ -204,8 +209,9 @@ impl FilesystemRepository {
let mut install_path: Option<String> = None;
if let Some(path_str) = &path {
if !path_str.is_empty() {
- let normalized_path = self.filesystem.normalize_path(&if self
+ let normalized_path = self.filesystem.borrow_mut().normalize_path(&if self
.filesystem
+ .borrow()
.is_absolute_path(path_str)
{
path_str.clone()
@@ -216,7 +222,7 @@ impl FilesystemRepository {
path_str
)
});
- install_path = Some(self.filesystem.find_shortest_path(
+ install_path = Some(self.filesystem.borrow_mut().find_shortest_path(
&repo_dir,
&normalized_path,
true,
@@ -282,17 +288,12 @@ impl FilesystemRepository {
});
}
- self.file.write(
- PhpMixed::Array(
- data.clone()
- .into_iter()
- .map(|(k, v)| (k, Box::new(v)))
- .collect(),
- ),
- shirabe_php_shim::JSON_UNESCAPED_SLASHES
- | shirabe_php_shim::JSON_PRETTY_PRINT
- | shirabe_php_shim::JSON_UNESCAPED_UNICODE,
- )?;
+ self.file.write(PhpMixed::Array(
+ data.clone()
+ .into_iter()
+ .map(|(k, v)| (k, Box::new(v)))
+ .collect(),
+ ))?;
if self.dump_versions {
let versions = self.generate_installed_versions(
@@ -302,7 +303,7 @@ impl FilesystemRepository {
&repo_dir,
)?;
- self.filesystem.file_put_contents_if_modified(
+ self.filesystem.borrow_mut().file_put_contents_if_modified(
&format!("{}/installed.php", repo_dir),
&format!("<?php return {};\n", self.dump_to_php_code(&versions, 0),),
);
@@ -311,7 +312,7 @@ impl FilesystemRepository {
// this normally should not happen but during upgrades of Composer when it is installed in the project it is a possibility
if let Some(class_content) = installed_versions_class {
- self.filesystem.file_put_contents_if_modified(
+ self.filesystem.borrow_mut().file_put_contents_if_modified(
&format!("{}/InstalledVersions.php", repo_dir),
&class_content,
);
@@ -347,7 +348,7 @@ impl FilesystemRepository {
let pattern = "{(?(DEFINE)\n (?<number> -? \\s*+ \\d++ (?:\\.\\d++)? )\n (?<boolean> true | false | null )\n (?<strings> (?&string) (?: \\s*+ \\. \\s*+ (?&string))*+ )\n (?<string> (?: \" (?:[^\"\\\\$]*+ | \\\\ [\"\\\\0] )* \" | ' (?:[^'\\\\]*+ | \\\\ ['\\\\] )* ' ) )\n (?<array> array\\( \\s*+ (?: (?:(?&number)|(?&strings)) \\s*+ => \\s*+ (?: (?:__DIR__ \\s*+ \\. \\s*+)? (?&strings) | (?&value) ) \\s*+, \\s*+ )*+ \\s*+ \\) )\n (?<value> (?: (?&number) | (?&boolean) | (?&strings) | (?&array) ) )\n)\n^<\\?php\\s++return\\s++(?&array)\\s*+;$}ix";
if let Some(data) = installed_versions_data {
let mixed = PhpMixed::String(data.clone());
- if is_string(&mixed) && Preg::is_match(pattern, &trim(&data, None)) {
+ if is_string(&mixed) && Preg::is_match(pattern, &trim(&data, None)).unwrap_or(false) {
let replaced = Preg::replace(
r#"{=>\s*+__DIR__\s*+\.\s*+(['\"])}"#,
&format!(
@@ -356,6 +357,10 @@ impl FilesystemRepository {
),
&data,
);
+ let replaced = match replaced {
+ Ok(s) => s,
+ Err(_) => return false,
+ };
let evaluated = r#eval(&format!("?>{}", replaced));
InstalledVersions::reload(
evaluated
@@ -411,7 +416,7 @@ impl FilesystemRepository {
}
} else if key == "install_path" && is_string(value) {
let s = value.as_string().unwrap_or("").to_string();
- if self.filesystem.is_absolute_path(&s) {
+ if self.filesystem.borrow_mut().is_absolute_path(&s) {
lines.push_str(&format!("{},\n", var_export(&PhpMixed::String(s), true),));
} else {
lines.push_str(&format!(
@@ -480,9 +485,7 @@ impl FilesystemRepository {
let mut current_root: Box<dyn RootPackageInterface> = root_package;
// packages.push(current_root.clone_box());
- while let Some(_alias) =
- (current_root.as_any() as &dyn Any).downcast_ref::<RootAliasPackage>()
- {
+ while let Some(_alias) = current_root.as_any().downcast_ref::<RootAliasPackage>() {
current_root =
todo!("RootAliasPackage::get_alias_of() returning Box<dyn RootPackageInterface>");
// packages.push(current_root.clone_box());
@@ -507,10 +510,7 @@ impl FilesystemRepository {
// add real installed packages
for package in &packages {
- if (package.as_any() as &dyn Any)
- .downcast_ref::<AliasPackage>()
- .is_some()
- {
+ if package.as_any().downcast_ref::<AliasPackage>().is_some() {
continue;
}
@@ -532,7 +532,7 @@ impl FilesystemRepository {
.as_array()
.map(|m| m.contains_key(package.get_name()))
.unwrap_or(false);
- for replace in package.get_replaces() {
+ for (_, replace) in package.get_replaces() {
// exclude platform replaces as when they are really there we can not check for their presence
if PlatformRepository::is_platform_package(replace.get_target()) {
continue;
@@ -550,7 +550,7 @@ impl FilesystemRepository {
todo!("append replaced to versions['versions'][target]['replaced']");
}
}
- for provide in package.get_provides() {
+ for (_, provide) in package.get_provides() {
// exclude platform provides as when they are really there we can not check for their presence
if PlatformRepository::is_platform_package(provide.get_target()) {
continue;
@@ -571,12 +571,13 @@ impl FilesystemRepository {
// add aliases
for package in &packages {
- let Some(alias) = (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>() else {
+ let Some(alias) = package.as_any().downcast_ref::<AliasPackage>() else {
continue;
};
// TODO(phase-b): mutate nested versions['versions'][name]['aliases']
todo!("append alias->getPrettyVersion() to versions['versions'][name]['aliases']");
- if (package.as_any() as &dyn Any)
+ if package
+ .as_any()
.downcast_ref::<dyn RootPackageInterface>()
.is_some()
{
@@ -641,14 +642,19 @@ impl FilesystemRepository {
};
}
- let install_path = if (package.as_any() as &dyn Any)
+ let install_path = if package
+ .as_any()
.downcast_ref::<dyn RootPackageInterface>()
.is_some()
{
- let to = self.filesystem.normalize_path(
+ let to = self.filesystem.borrow_mut().normalize_path(
&realpath(&Platform::get_cwd(false).unwrap_or_default()).unwrap_or_default(),
);
- Some(self.filesystem.find_shortest_path(repo_dir, &to, true))
+ Some(
+ self.filesystem
+ .borrow_mut()
+ .find_shortest_path(repo_dir, &to, true),
+ )
} else {
install_paths.get(package.get_name()).cloned().flatten()
};
diff --git a/crates/shirabe/src/repository/filter_repository.rs b/crates/shirabe/src/repository/filter_repository.rs
index ffdb3b9..482ffbe 100644
--- a/crates/shirabe/src/repository/filter_repository.rs
+++ b/crates/shirabe/src/repository/filter_repository.rs
@@ -165,10 +165,10 @@ impl RepositoryInterface for FilterRepository {
fn find_package(
&self,
- name: String,
+ name: &str,
constraint: FindPackageConstraint,
) -> Option<Box<dyn BasePackage>> {
- if !self.is_allowed(&name) {
+ if !self.is_allowed(name) {
return None;
}
@@ -177,10 +177,10 @@ impl RepositoryInterface for FilterRepository {
fn find_packages(
&self,
- name: String,
+ name: &str,
constraint: Option<FindPackageConstraint>,
) -> Vec<Box<dyn BasePackage>> {
- if !self.is_allowed(&name) {
+ if !self.is_allowed(name) {
return Vec::new();
}
diff --git a/crates/shirabe/src/repository/installed_array_repository.rs b/crates/shirabe/src/repository/installed_array_repository.rs
index c30d682..ddcda90 100644
--- a/crates/shirabe/src/repository/installed_array_repository.rs
+++ b/crates/shirabe/src/repository/installed_array_repository.rs
@@ -20,6 +20,16 @@ pub struct InstalledArrayRepository {
}
impl InstalledArrayRepository {
+ pub fn new() -> anyhow::Result<Self> {
+ Self::new_with_packages(Vec::new())
+ }
+
+ pub fn new_with_packages(packages: Vec<Box<dyn PackageInterface>>) -> anyhow::Result<Self> {
+ Ok(Self {
+ inner: WritableArrayRepository::new(packages)?,
+ })
+ }
+
pub fn get_repo_name(&self) -> String {
format!("installed {}", self.inner.get_repo_name())
}
@@ -89,14 +99,14 @@ impl RepositoryInterface for InstalledArrayRepository {
}
fn find_package(
&self,
- _name: String,
+ _name: &str,
_constraint: FindPackageConstraint,
) -> Option<Box<dyn BasePackage>> {
todo!()
}
fn find_packages(
&self,
- _name: String,
+ _name: &str,
_constraint: Option<FindPackageConstraint>,
) -> Vec<Box<dyn BasePackage>> {
todo!()
diff --git a/crates/shirabe/src/repository/installed_filesystem_repository.rs b/crates/shirabe/src/repository/installed_filesystem_repository.rs
index 1d1caf6..c7f2a4d 100644
--- a/crates/shirabe/src/repository/installed_filesystem_repository.rs
+++ b/crates/shirabe/src/repository/installed_filesystem_repository.rs
@@ -28,7 +28,7 @@ impl InstalledFilesystemRepository {
repository_file: JsonFile,
dump_versions: bool,
root_package: Option<Box<dyn RootPackageInterface>>,
- filesystem: Option<Filesystem>,
+ filesystem: Option<std::rc::Rc<std::cell::RefCell<Filesystem>>>,
) -> Result<Self> {
Ok(Self {
inner: FilesystemRepository::new(
@@ -109,14 +109,14 @@ impl RepositoryInterface for InstalledFilesystemRepository {
}
fn find_package(
&self,
- _name: String,
+ _name: &str,
_constraint: FindPackageConstraint,
) -> Option<Box<dyn BasePackage>> {
todo!()
}
fn find_packages(
&self,
- _name: String,
+ _name: &str,
_constraint: Option<FindPackageConstraint>,
) -> Vec<Box<dyn BasePackage>> {
todo!()
diff --git a/crates/shirabe/src/repository/installed_repository.rs b/crates/shirabe/src/repository/installed_repository.rs
index d0130f4..12abf1e 100644
--- a/crates/shirabe/src/repository/installed_repository.rs
+++ b/crates/shirabe/src/repository/installed_repository.rs
@@ -51,7 +51,7 @@ impl InstalledRepository {
pub fn find_packages_with_replacers_and_providers(
&self,
- name: String,
+ name: &str,
constraint: Option<FindPackageConstraint>,
) -> Vec<Box<dyn BasePackage>> {
let name = name.to_lowercase();
@@ -121,7 +121,7 @@ impl InstalledRepository {
let mut root_package: Option<Box<dyn BasePackage>> = None;
for package in self.inner.get_packages() {
- if package.as_any().is::<dyn RootPackageInterface>() {
+ if package.as_root_package_interface().is_some() {
root_package = Some(package);
break;
}
@@ -177,7 +177,7 @@ impl InstalledRepository {
}
}
- if package.as_any().is::<dyn RootPackageInterface>() {
+ if package.as_root_package_interface().is_some() {
for (k, v) in package.get_dev_requires() {
links.entry(k).or_insert(v);
}
@@ -222,7 +222,7 @@ impl InstalledRepository {
if invert && needles.contains(&package.get_name().to_string()) {
for link in package.get_conflicts().values() {
- for pkg in self.find_packages(link.get_target().to_string(), None) {
+ for pkg in self.find_packages(link.get_target(), None) {
let version = Constraint::new("=", pkg.get_version());
if link.get_constraint().matches(&version) == invert {
results.push(DependentsEntry(package.clone_box(), link.clone(), None));
@@ -233,7 +233,7 @@ impl InstalledRepository {
for link in package.get_conflicts().values() {
if needles.contains(&link.get_target().to_string()) {
- for pkg in self.find_packages(link.get_target().to_string(), None) {
+ for pkg in self.find_packages(link.get_target(), None) {
let version = Constraint::new("=", pkg.get_version());
if link.get_constraint().matches(&version) == invert {
results.push(DependentsEntry(package.clone_box(), link.clone(), None));
@@ -254,7 +254,7 @@ impl InstalledRepository {
if PlatformRepository::is_platform_package(link.get_target()) {
if self
.find_package(
- link.get_target().to_string(),
+ link.get_target(),
FindPackageConstraint::Constraint(
link.get_constraint().clone_box(),
),
@@ -265,7 +265,7 @@ impl InstalledRepository {
}
let platform_pkg = self.find_package(
- link.get_target().to_string(),
+ link.get_target(),
FindPackageConstraint::String("*".to_string()),
);
let description = platform_pkg
@@ -292,7 +292,7 @@ impl InstalledRepository {
}
for pkg in self.get_packages() {
- if !pkg.get_names().contains(&link.get_target().to_string()) {
+ if !pkg.get_names(true).contains(&link.get_target().to_string()) {
continue;
}
@@ -320,7 +320,9 @@ impl InstalledRepository {
root_reqs.entry(k).or_insert(v);
}
for root_req in root_reqs.values() {
- if pkg.get_names().contains(&root_req.get_target().to_string())
+ if pkg
+ .get_names(true)
+ .contains(&root_req.get_target().to_string())
&& !root_req.get_constraint().matches(link.get_constraint())
{
results.push(DependentsEntry(
@@ -422,7 +424,7 @@ impl RepositoryInterface for InstalledRepository {
fn find_package(
&self,
- name: String,
+ name: &str,
constraint: FindPackageConstraint,
) -> Option<Box<dyn BasePackage>> {
self.inner.find_package(name, constraint)
@@ -430,7 +432,7 @@ impl RepositoryInterface for InstalledRepository {
fn find_packages(
&self,
- name: String,
+ name: &str,
constraint: Option<FindPackageConstraint>,
) -> Vec<Box<dyn BasePackage>> {
self.inner.find_packages(name, constraint)
diff --git a/crates/shirabe/src/repository/lock_array_repository.rs b/crates/shirabe/src/repository/lock_array_repository.rs
index c0a5034..87acf6e 100644
--- a/crates/shirabe/src/repository/lock_array_repository.rs
+++ b/crates/shirabe/src/repository/lock_array_repository.rs
@@ -41,7 +41,7 @@ impl RepositoryInterface for LockArrayRepository {
fn find_package(
&self,
- name: String,
+ name: &str,
constraint: FindPackageConstraint,
) -> Option<Box<dyn BasePackage>> {
self.inner.find_package(name, constraint)
@@ -49,7 +49,7 @@ impl RepositoryInterface for LockArrayRepository {
fn find_packages(
&self,
- name: String,
+ name: &str,
constraint: Option<FindPackageConstraint>,
) -> Vec<Box<dyn BasePackage>> {
self.inner.find_packages(name, constraint)
diff --git a/crates/shirabe/src/repository/package_repository.rs b/crates/shirabe/src/repository/package_repository.rs
index 64b578e..dc8a8f3 100644
--- a/crates/shirabe/src/repository/package_repository.rs
+++ b/crates/shirabe/src/repository/package_repository.rs
@@ -40,18 +40,24 @@ impl PackageRepository {
};
Self {
- inner: ArrayRepository::new(),
+ inner: ArrayRepository::new(vec![])
+ .expect("ArrayRepository::new with empty vec cannot fail"),
config: config_list,
security_advisories,
}
}
pub fn initialize(&mut self) -> anyhow::Result<Result<(), InvalidRepositoryException>> {
- self.inner.initialize()?;
+ self.inner.initialize();
- let loader = ValidatingArrayLoader::new(ArrayLoader::new(None, true), true);
+ let mut loader =
+ ValidatingArrayLoader::new(Box::new(ArrayLoader::new(None, true)), true, None, 0);
for package in &self.config {
- let package = match loader.load(package) {
+ let config_map: IndexMap<String, Box<PhpMixed>> = match package {
+ PhpMixed::Array(m) => m.clone(),
+ _ => IndexMap::new(),
+ };
+ let package_loaded = match loader.load(config_map, "") {
Ok(p) => p,
Err(e) => {
let msg = format!(
@@ -65,13 +71,16 @@ impl PackageRepository {
})));
}
};
- self.inner.add_package(package)?;
+ // TODO(phase-b): add_package expects Box<dyn PackageInterface>; loader returns Box<dyn BasePackage>
+ let _ = package_loaded;
}
Ok(Ok(()))
}
pub fn get_repo_name(&self) -> String {
+ use crate::repository::repository_interface::RepositoryInterface;
Preg::replace(r"^array ", "package ", &self.inner.get_repo_name())
+ .unwrap_or_else(|_| self.inner.get_repo_name())
}
}
@@ -91,42 +100,45 @@ impl AdvisoryProviderInterface for PackageRepository {
let mut advisories: IndexMap<String, Vec<PartialOrSecurityAdvisory>> = IndexMap::new();
for (package_name, package_advisories) in &self.security_advisories {
- if package_constraint_map.contains_key(package_name.as_str()) {
- let items: anyhow::Result<Vec<PartialOrSecurityAdvisory>> = match package_advisories {
- PhpMixed::List(list) => list
- .iter()
- .filter_map(|data| {
- let data_map = match data.as_ref() {
- PhpMixed::Array(m) => m
- .iter()
- .map(|(k, v)| (k.clone(), *v.clone()))
- .collect::<IndexMap<String, PhpMixed>>(),
- _ => return Ok(None),
- };
- let advisory =
- PartialSecurityAdvisory::create(package_name, &data_map, &semver_parser)
- .ok()?;
- if !allow_partial_advisories
- && matches!(advisory, PartialOrSecurityAdvisory::Partial(_))
- {
- return Err(anyhow::anyhow!(RuntimeException { message: format!("Advisory for {} could not be loaded as a full advisory from {}\n{}", package_name, self.get_repo_name(), var_export(data, true)), code: 0 }));
- }
- let affected_versions = match &advisory {
- PartialOrSecurityAdvisory::Full(a) => &a.affected_versions,
- PartialOrSecurityAdvisory::Partial(a) => &a.affected_versions,
- };
- if !affected_versions
- .matches(package_constraint_map[package_name.as_str()].as_ref())
- {
- return Ok(None);
- }
- Ok(Some(advisory))
- })
- .collect(),
- _ => vec![],
+ if !package_constraint_map.contains_key(package_name.as_str()) {
+ continue;
+ }
+ let list = match package_advisories {
+ PhpMixed::List(list) => list,
+ _ => continue,
+ };
+ let mut items: Vec<PartialOrSecurityAdvisory> = Vec::new();
+ for data in list {
+ let data_map: IndexMap<String, PhpMixed> = match data.as_ref() {
+ PhpMixed::Array(m) => m.iter().map(|(k, v)| (k.clone(), *v.clone())).collect(),
+ _ => continue,
};
- advisories.insert(package_name.clone(), items?);
+ let advisory = match PartialSecurityAdvisory::create(
+ package_name,
+ &data_map,
+ &semver_parser,
+ ) {
+ Ok(a) => a,
+ Err(_) => continue,
+ };
+ if !allow_partial_advisories
+ && matches!(advisory, PartialOrSecurityAdvisory::Partial(_))
+ {
+ return Err(anyhow::anyhow!(RuntimeException {
+ message: format!(
+ "Advisory for {} could not be loaded as a full advisory from {}\n{}",
+ package_name,
+ self.get_repo_name(),
+ var_export(data, true)
+ ),
+ code: 0,
+ }));
+ }
+ // TODO(phase-b): affected_versions is a method, not a field, and matches() return type may differ
+ let _ = (&advisory, &package_constraint_map);
+ items.push(advisory);
}
+ advisories.insert(package_name.clone(), items);
}
let names_found: Vec<String> = advisories.keys().cloned().collect();
diff --git a/crates/shirabe/src/repository/path_repository.rs b/crates/shirabe/src/repository/path_repository.rs
index 3b6ed8d..c29e6c0 100644
--- a/crates/shirabe/src/repository/path_repository.rs
+++ b/crates/shirabe/src/repository/path_repository.rs
@@ -12,6 +12,7 @@ use crate::event_dispatcher::event_dispatcher::EventDispatcher;
use crate::io::io_interface::IOInterface;
use crate::json::json_file::JsonFile;
use crate::package::loader::array_loader::ArrayLoader;
+use crate::package::loader::loader_interface::LoaderInterface;
use crate::package::version::version_guesser::VersionGuesser;
use crate::package::version::version_parser::VersionParser;
use crate::repository::array_repository::ArrayRepository;
@@ -30,7 +31,7 @@ pub struct PathRepository {
version_guesser: VersionGuesser,
url: String,
repo_config: IndexMap<String, PhpMixed>,
- process: ProcessExecutor,
+ process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
options: IndexMap<String, PhpMixed>,
}
@@ -44,10 +45,10 @@ impl PathRepository {
pub fn new(
repo_config: IndexMap<String, PhpMixed>,
io: Box<dyn IOInterface>,
- config: Config,
- http_downloader: Option<HttpDownloader>,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
+ http_downloader: Option<std::rc::Rc<std::cell::RefCell<HttpDownloader>>>,
dispatcher: Option<EventDispatcher>,
- process: Option<ProcessExecutor>,
+ process: Option<std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>,
) -> anyhow::Result<Self> {
if !repo_config.contains_key("url") {
return Err(RuntimeException {
@@ -64,8 +65,17 @@ impl PathRepository {
.unwrap_or("")
.to_string();
let url = Platform::expand_path(&url_str);
- let process = process.unwrap_or_else(|| ProcessExecutor::new(&*io));
- let version_guesser = VersionGuesser::new(&config, &process, VersionParser::new(), &*io);
+ let process = process.unwrap_or_else(|| {
+ std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(Some(
+ io.clone_box(),
+ ))))
+ });
+ let version_guesser = VersionGuesser::new(
+ config,
+ std::rc::Rc::clone(&process),
+ shirabe_semver::version_parser::VersionParser,
+ Some(io.clone_box()),
+ );
let mut options = repo_config
.get("options")
.and_then(|v| v.as_array())
@@ -81,7 +91,7 @@ impl PathRepository {
}
Ok(Self {
- inner: ArrayRepository::new(),
+ inner: ArrayRepository::new(vec![])?,
loader: ArrayLoader::new(None, true),
version_guesser,
url,
@@ -105,7 +115,7 @@ impl PathRepository {
}
pub(crate) fn initialize(&mut self) -> anyhow::Result<()> {
- self.inner.initialize()?;
+ self.inner.initialize();
let url_matches = self.get_url_matches()?;
@@ -140,8 +150,11 @@ impl PathRepository {
}
let json = file_get_contents(&composer_file_path).unwrap_or_default();
- let mut package =
- JsonFile::parse_json(&json, Some(&composer_file_path))?.unwrap_or_default();
+ let parsed = JsonFile::parse_json(Some(&json), Some(&composer_file_path))?;
+ let mut package: IndexMap<String, PhpMixed> = match parsed {
+ PhpMixed::Array(m) => m.into_iter().map(|(k, v)| (k, *v)).collect(),
+ _ => IndexMap::new(),
+ };
let dist = {
let mut dist = IndexMap::new();
dist.insert(
@@ -213,20 +226,20 @@ impl PathRepository {
if !package.contains_key("version") {
if let Some(root_version) = Platform::get_env("COMPOSER_ROOT_VERSION") {
if !root_version.is_empty() {
- let mut ref1 = String::new();
- let mut ref2 = String::new();
- if self.process.execute(
- &["git", "rev-parse", "HEAD"].map(|s| s.to_string()).to_vec(),
- &mut ref1,
- Some(path.clone()),
- ) == 0
- && self.process.execute(
- &["git", "rev-parse", "HEAD"].map(|s| s.to_string()).to_vec(),
- &mut ref2,
- None,
- ) == 0
- && ref1 == ref2
- {
+ let mut ref1 = PhpMixed::Null;
+ let mut ref2 = PhpMixed::Null;
+ let cmd = PhpMixed::from(vec!["git", "rev-parse", "HEAD"]);
+ let code1 = self
+ .process
+ .borrow_mut()
+ .execute(cmd.clone(), Some(&mut ref1), Some(path.as_str()))
+ .unwrap_or(1);
+ let code2 = self
+ .process
+ .borrow_mut()
+ .execute(cmd, Some(&mut ref2), None)
+ .unwrap_or(1);
+ if code1 == 0 && code2 == 0 && ref1.as_string() == ref2.as_string() {
package.insert(
"version".to_string(),
PhpMixed::String(self.version_guesser.get_root_version_from_env()),
@@ -236,7 +249,7 @@ impl PathRepository {
}
}
- let mut output = String::new();
+ let mut output = PhpMixed::Null;
let command = GitUtil::build_rev_list_command(&self.process, {
let mut args = vec![
"-n1".to_string(),
@@ -250,10 +263,17 @@ impl PathRepository {
&& shirabe_php_shim::is_dir(&format!("{}/.git", path.trim_end_matches('/')))
&& self
.process
- .execute(&command, &mut output, Some(path.clone()))
+ .borrow_mut()
+ .execute(
+ PhpMixed::from(command),
+ Some(&mut output),
+ Some(path.as_str()),
+ )
+ .unwrap_or(1)
== 0
{
- let ref_val = GitUtil::parse_rev_list_output(&output, &self.process)
+ let output_str = output.as_string().unwrap_or("").to_string();
+ let ref_val = GitUtil::parse_rev_list_output(&output_str, &self.process)
.trim()
.to_string();
if let Some(PhpMixed::Array(ref mut dist)) = package.get_mut("dist") {
diff --git a/crates/shirabe/src/repository/platform_repository.rs b/crates/shirabe/src/repository/platform_repository.rs
index 8fb9d0a..64bc861 100644
--- a/crates/shirabe/src/repository/platform_repository.rs
+++ b/crates/shirabe/src/repository/platform_repository.rs
@@ -4,7 +4,7 @@ use std::sync::{LazyLock, Mutex};
use indexmap::IndexMap;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_external_packages::composer::xdebug_handler::xdebug_handler::XdebugHandler;
use shirabe_php_shim::{
InvalidArgumentException, PhpMixed, UnexpectedValueException, array_map_str_fn, array_slice,
@@ -337,14 +337,20 @@ impl PlatformRepository {
let info = self.runtime.get_extension_info(name)?;
// librabbitmq version => 0.9.0
- if let Ok(Some(librabbitmq_matches)) = Preg::is_match_strict_groups(
+ let mut librabbitmq_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(
"/^librabbitmq version => (?<version>.+)$/im",
&info,
- ) {
+ Some(&mut librabbitmq_matches),
+ )
+ .unwrap_or(false)
+ {
self.add_library(
&mut libraries,
&format!("{}-librabbitmq", name),
- Some(&librabbitmq_matches["version"]),
+ librabbitmq_matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
Some("AMQP librabbitmq version"),
&[],
&[],
@@ -352,14 +358,22 @@ impl PlatformRepository {
}
// AMQP protocol version => 0-9-1
- if let Ok(Some(protocol_matches)) = Preg::is_match_strict_groups(
+ let mut protocol_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
"/^AMQP protocol version => (?<version>.+)$/im",
&info,
- ) {
+ Some(&mut protocol_matches),
+ )
+ .unwrap_or(false)
+ {
+ let version_str = protocol_matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .cloned()
+ .unwrap_or_default();
self.add_library(
&mut libraries,
&format!("{}-protocol", name),
- Some(&str_replace("-", ".", &protocol_matches["version"])),
+ Some(&str_replace("-", ".", &version_str)),
Some("AMQP protocol version"),
&[],
&[],
@@ -371,13 +385,20 @@ impl PlatformRepository {
let info = self.runtime.get_extension_info(name)?;
// BZip2 Version => 1.0.6, 6-Sept-2010
- if let Ok(Some(matches)) =
- Preg::is_match_strict_groups("/^BZip2 Version => (?<version>.*),/im", &info)
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(
+ "/^BZip2 Version => (?<version>.*),/im",
+ &info,
+ Some(&mut matches),
+ )
+ .unwrap_or(false)
{
self.add_library(
&mut libraries,
name,
- Some(&matches["version"]),
+ matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
None,
&[],
&[],
@@ -402,16 +423,27 @@ impl PlatformRepository {
let info = self.runtime.get_extension_info(name)?;
// SSL Version => OpenSSL/1.0.1t
- if let Ok(Some(ssl_matches)) = Preg::is_match_strict_groups(
+ let mut ssl_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
"{^SSL Version => (?<library>[^/]+)/(?<version>.+)$}im",
&info,
- ) {
- let library = strtolower(&ssl_matches["library"]);
+ Some(&mut ssl_matches),
+ )
+ .unwrap_or(false)
+ {
+ let ssl_library_raw = ssl_matches
+ .get(&CaptureKey::ByName("library".to_string()))
+ .cloned()
+ .unwrap_or_default();
+ let ssl_version = ssl_matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .cloned()
+ .unwrap_or_default();
+ let library = strtolower(&ssl_library_raw);
if library == "openssl" {
let mut is_fips = false;
- let parsed_version =
- Version::parse_openssl(&ssl_matches["version"], &mut is_fips)
- .unwrap_or_default();
+ let parsed_version = Version::parse_openssl(&ssl_version, &mut is_fips)
+ .unwrap_or_default();
self.add_library(
&mut libraries,
&format!("{}-openssl{}", name, if is_fips { "-fips" } else { "" }),
@@ -427,14 +459,21 @@ impl PlatformRepository {
} else {
let (shortlib, ssl_lib);
if str_starts_with(&library, "(securetransport)") {
- if let Ok(Some(securetransport_matches)) =
- Preg::is_match_strict_groups(
- "{^\\(securetransport\\) ([a-z0-9]+)}",
- &library,
- )
+ let mut securetransport_matches: IndexMap<CaptureKey, String> =
+ IndexMap::new();
+ if Preg::is_match3(
+ "{^\\(securetransport\\) ([a-z0-9]+)}",
+ &library,
+ Some(&mut securetransport_matches),
+ )
+ .unwrap_or(false)
{
shortlib = "securetransport".to_string();
- ssl_lib = format!("curl-{}", securetransport_matches["1"]);
+ let m1 = securetransport_matches
+ .get(&CaptureKey::ByIndex(1))
+ .cloned()
+ .unwrap_or_default();
+ ssl_lib = format!("curl-{}", m1);
} else {
shortlib = library.clone();
ssl_lib = "curl-openssl".to_string();
@@ -446,11 +485,8 @@ impl PlatformRepository {
self.add_library(
&mut libraries,
&format!("{}-{}", name, shortlib),
- Some(&ssl_matches["version"]),
- Some(&format!(
- "curl {} version ({})",
- library, &ssl_matches["version"]
- )),
+ Some(&ssl_version),
+ Some(&format!("curl {} version ({})", library, ssl_version)),
&[ssl_lib],
&[],
)?;
@@ -458,28 +494,47 @@ impl PlatformRepository {
}
// libSSH Version => libssh2/1.4.3
- if let Ok(Some(ssh_matches)) = Preg::is_match_strict_groups(
+ let mut ssh_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
"{^libSSH Version => (?<library>[^/]+)/(?<version>.+?)(?:/.*)?$}im",
&info,
- ) {
+ Some(&mut ssh_matches),
+ )
+ .unwrap_or(false)
+ {
+ let ssh_library = ssh_matches
+ .get(&CaptureKey::ByName("library".to_string()))
+ .cloned()
+ .unwrap_or_default();
+ let ssh_version = ssh_matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .cloned()
+ .unwrap_or_default();
self.add_library(
&mut libraries,
- &format!("{}-{}", name, strtolower(&ssh_matches["library"])),
- Some(&ssh_matches["version"]),
- Some(&format!("curl {} version", &ssh_matches["library"])),
+ &format!("{}-{}", name, strtolower(&ssh_library)),
+ Some(&ssh_version),
+ Some(&format!("curl {} version", &ssh_library)),
&[],
&[],
)?;
}
// ZLib Version => 1.2.8
- if let Ok(Some(zlib_matches)) =
- Preg::is_match_strict_groups("{^ZLib Version => (?<version>.+)$}im", &info)
+ let mut zlib_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
+ "{^ZLib Version => (?<version>.+)$}im",
+ &info,
+ Some(&mut zlib_matches),
+ )
+ .unwrap_or(false)
{
self.add_library(
&mut libraries,
&format!("{}-zlib", name),
- Some(&zlib_matches["version"]),
+ zlib_matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
Some("curl zlib version"),
&[],
&[],
@@ -491,14 +546,20 @@ impl PlatformRepository {
let info = self.runtime.get_extension_info(name)?;
// timelib version => 2018.03
- if let Ok(Some(timelib_matches)) = Preg::is_match_strict_groups(
+ let mut timelib_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
"/^timelib version => (?<version>.+)$/im",
&info,
- ) {
+ Some(&mut timelib_matches),
+ )
+ .unwrap_or(false)
+ {
self.add_library(
&mut libraries,
&format!("{}-timelib", name),
- Some(&timelib_matches["version"]),
+ timelib_matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
Some("date timelib version"),
&[],
&[],
@@ -506,21 +567,36 @@ impl PlatformRepository {
}
// Timezone Database => internal
- if let Ok(Some(zoneinfo_source_matches)) = Preg::is_match_strict_groups(
+ let mut zoneinfo_source_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
"/^Timezone Database => (?<source>internal|external)$/im",
&info,
- ) {
- let external = zoneinfo_source_matches["source"] == "external";
- if let Ok(Some(zoneinfo_matches)) = Preg::is_match_strict_groups(
+ Some(&mut zoneinfo_source_matches),
+ )
+ .unwrap_or(false)
+ {
+ let external = zoneinfo_source_matches
+ .get(&CaptureKey::ByName("source".to_string()))
+ .map(|s| s == "external")
+ .unwrap_or(false);
+ let mut zoneinfo_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
"/^\"Olson\" Timezone Database Version => (?<version>.+?)(?:\\.system)?$/im",
&info,
- ) {
+ Some(&mut zoneinfo_matches),
+ )
+ .unwrap_or(false)
+ {
+ let zoneinfo_version = zoneinfo_matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .cloned()
+ .unwrap_or_default();
// If the timezonedb is provided by ext/timezonedb, register that version as a replacement
if external && loaded_extensions.iter().any(|n| n == "timezonedb") {
self.add_library(
&mut libraries,
"timezonedb-zoneinfo",
- Some(&zoneinfo_matches["version"]),
+ Some(&zoneinfo_version),
Some(
"zoneinfo (\"Olson\") database for date (replaced by timezonedb)",
),
@@ -531,7 +607,7 @@ impl PlatformRepository {
self.add_library(
&mut libraries,
&format!("{}-zoneinfo", name),
- Some(&zoneinfo_matches["version"]),
+ Some(&zoneinfo_version),
Some("zoneinfo (\"Olson\") database for date"),
&[],
&[],
@@ -545,13 +621,20 @@ impl PlatformRepository {
let info = self.runtime.get_extension_info(name)?;
// libmagic => 537
- if let Ok(Some(magic_matches)) =
- Preg::is_match_strict_groups("/^libmagic => (?<version>.+)$/im", &info)
+ let mut magic_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(
+ "/^libmagic => (?<version>.+)$/im",
+ &info,
+ Some(&mut magic_matches),
+ )
+ .unwrap_or(false)
{
self.add_library(
&mut libraries,
&format!("{}-libmagic", name),
- Some(&magic_matches["version"]),
+ magic_matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
Some("fileinfo libmagic version"),
&[],
&[],
@@ -576,12 +659,19 @@ impl PlatformRepository {
let info = self.runtime.get_extension_info(name)?;
- if let Ok(Some(libjpeg_matches)) = Preg::is_match_strict_groups(
+ let mut libjpeg_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
"/^libJPEG Version => (?<version>.+?)(?: compatible)?$/im",
&info,
- ) {
- let parsed =
- Version::parse_libjpeg(&libjpeg_matches["version"]).unwrap_or_default();
+ Some(&mut libjpeg_matches),
+ )
+ .unwrap_or(false)
+ {
+ let libjpeg_version = libjpeg_matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .cloned()
+ .unwrap_or_default();
+ let parsed = Version::parse_libjpeg(&libjpeg_version).unwrap_or_default();
self.add_library(
&mut libraries,
&format!("{}-libjpeg", name),
@@ -592,41 +682,59 @@ impl PlatformRepository {
)?;
}
- if let Ok(Some(libpng_matches)) = Preg::is_match_strict_groups(
+ let mut libpng_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
"/^libPNG Version => (?<version>.+)$/im",
&info,
- ) {
+ Some(&mut libpng_matches),
+ )
+ .unwrap_or(false)
+ {
self.add_library(
&mut libraries,
&format!("{}-libpng", name),
- Some(&libpng_matches["version"]),
+ libpng_matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
Some("libpng version for gd"),
&[],
&[],
)?;
}
- if let Ok(Some(freetype_matches)) = Preg::is_match_strict_groups(
+ let mut freetype_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
"/^FreeType Version => (?<version>.+)$/im",
&info,
- ) {
+ Some(&mut freetype_matches),
+ )
+ .unwrap_or(false)
+ {
self.add_library(
&mut libraries,
&format!("{}-freetype", name),
- Some(&freetype_matches["version"]),
+ freetype_matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
Some("freetype version for gd"),
&[],
&[],
)?;
}
- if let Ok(Some(libxpm_matches)) = Preg::is_match_strict_groups(
+ let mut libxpm_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
"/^libXpm Version => (?<versionId>\\d+)$/im",
&info,
- ) {
- let version_id: i64 = libxpm_matches["versionId"].parse().unwrap_or(0);
- let converted =
- Version::convert_libxpm_version_id(version_id).unwrap_or_default();
+ Some(&mut libxpm_matches),
+ )
+ .unwrap_or(false)
+ {
+ let version_id: i64 = libxpm_matches
+ .get(&CaptureKey::ByName("versionId".to_string()))
+ .and_then(|s| s.parse().ok())
+ .unwrap_or(0);
+ let converted = Version::convert_libxpm_version_id(version_id);
self.add_library(
&mut libraries,
&format!("{}-libxpm", name),
@@ -689,27 +797,42 @@ impl PlatformRepository {
&[],
&[],
)?;
- } else if let Ok(Some(matches)) =
- Preg::is_match_strict_groups("/^ICU version => (?<version>.+)$/im", &info)
- {
- self.add_library(
- &mut libraries,
- "icu",
- Some(&matches["version"]),
- Some(description),
- &[],
- &[],
- )?;
+ } else {
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(
+ "/^ICU version => (?<version>.+)$/im",
+ &info,
+ Some(&mut matches),
+ )
+ .unwrap_or(false)
+ {
+ self.add_library(
+ &mut libraries,
+ "icu",
+ matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
+ Some(description),
+ &[],
+ &[],
+ )?;
+ }
}
// ICU TZData version => 2019c
- if let Ok(Some(zoneinfo_matches)) = Preg::is_match_strict_groups(
+ let mut zoneinfo_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
"/^ICU TZData version => (?<version>.*)$/im",
&info,
- ) {
- if let Some(parsed) =
- Version::parse_zoneinfo_version(&zoneinfo_matches["version"])
- {
+ Some(&mut zoneinfo_matches),
+ )
+ .unwrap_or(false)
+ {
+ let zi_version = zoneinfo_matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .cloned()
+ .unwrap_or_default();
+ if let Some(parsed) = Version::parse_zoneinfo_version(&zi_version) {
self.add_library(
&mut libraries,
"icu-zoneinfo",
@@ -784,12 +907,19 @@ impl PlatformRepository {
Self::imagick_get_version_string(&image_magick_version);
// 6.x: ImageMagick 6.2.9 08/24/06 Q16 http://www.imagemagick.org
// 7.x: ImageMagick 7.0.8-34 Q16 x86_64 2019-03-23 https://imagemagick.org
- if let Ok(Some(matches)) = Preg::is_match_strict_groups(
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(
"/^ImageMagick (?<version>[\\d.]+)(?:-(?<patch>\\d+))?/",
&image_magick_version_str,
- ) {
- let mut version_built = matches["version"].clone();
- if let Some(patch) = matches.get("patch") {
+ Some(&mut matches),
+ )
+ .unwrap_or(false)
+ {
+ let mut version_built = matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .cloned()
+ .unwrap_or_default();
+ if let Some(patch) = matches.get(&CaptureKey::ByName("patch".to_string())) {
version_built = format!("{}.{}", version_built, patch);
}
@@ -807,21 +937,35 @@ impl PlatformRepository {
"ldap" => {
let info = self.runtime.get_extension_info(name)?;
- if let (Ok(Some(matches)), Ok(Some(vendor_matches))) = (
- Preg::is_match_strict_groups(
- "/^Vendor Version => (?<versionId>\\d+)$/im",
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ let mut vendor_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
+ "/^Vendor Version => (?<versionId>\\d+)$/im",
+ &info,
+ Some(&mut matches),
+ )
+ .unwrap_or(false)
+ && Preg::is_match_strict_groups3(
+ "/^Vendor Name => (?<vendor>.+)$/im",
&info,
- ),
- Preg::is_match_strict_groups("/^Vendor Name => (?<vendor>.+)$/im", &info),
- ) {
- let version_id: i64 = matches["versionId"].parse().unwrap_or(0);
- let converted =
- Version::convert_openldap_version_id(version_id).unwrap_or_default();
+ Some(&mut vendor_matches),
+ )
+ .unwrap_or(false)
+ {
+ let version_id: i64 = matches
+ .get(&CaptureKey::ByName("versionId".to_string()))
+ .and_then(|s| s.parse().ok())
+ .unwrap_or(0);
+ let converted = Version::convert_openldap_version_id(version_id);
+ let vendor = vendor_matches
+ .get(&CaptureKey::ByName("vendor".to_string()))
+ .cloned()
+ .unwrap_or_default();
self.add_library(
&mut libraries,
- &format!("{}-{}", name, strtolower(&vendor_matches["vendor"])),
+ &format!("{}-{}", name, strtolower(&vendor)),
Some(&converted),
- Some(&format!("{} version of ldap", &vendor_matches["vendor"])),
+ Some(&format!("{} version of ldap", vendor)),
&[],
&[],
)?;
@@ -857,14 +1001,20 @@ impl PlatformRepository {
let info = self.runtime.get_extension_info(name)?;
// libmbfl version => 1.3.2
- if let Ok(Some(libmbfl_matches)) = Preg::is_match_strict_groups(
+ let mut libmbfl_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(
"/^libmbfl version => (?<version>.+)$/im",
&info,
- ) {
+ Some(&mut libmbfl_matches),
+ )
+ .unwrap_or(false)
+ {
self.add_library(
&mut libraries,
&format!("{}-libmbfl", name),
- Some(&libmbfl_matches["version"]),
+ libmbfl_matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
Some("mbstring libmbfl version"),
&[],
&[],
@@ -888,18 +1038,26 @@ impl PlatformRepository {
// Multibyte regex (oniguruma) version => 5.9.5
// oniguruma version => 6.9.0
- } else if let Ok(Some(oniguruma_matches)) = Preg::is_match_strict_groups(
- "/^(?:oniguruma|Multibyte regex \\(oniguruma\\)) version => (?<version>.+)$/im",
- &info,
- ) {
- self.add_library(
- &mut libraries,
- &format!("{}-oniguruma", name),
- Some(&oniguruma_matches["version"]),
- Some("mbstring oniguruma version"),
- &[],
- &[],
- )?;
+ } else {
+ let mut oniguruma_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(
+ "/^(?:oniguruma|Multibyte regex \\(oniguruma\\)) version => (?<version>.+)$/im",
+ &info,
+ Some(&mut oniguruma_matches),
+ )
+ .unwrap_or(false)
+ {
+ self.add_library(
+ &mut libraries,
+ &format!("{}-oniguruma", name),
+ oniguruma_matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
+ Some("mbstring oniguruma version"),
+ &[],
+ &[],
+ )?;
+ }
}
}
@@ -907,14 +1065,20 @@ impl PlatformRepository {
let info = self.runtime.get_extension_info(name)?;
// libmemcached version => 1.0.18
- if let Ok(Some(matches)) = Preg::is_match_strict_groups(
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(
"/^libmemcached version => (?<version>.+)$/im",
&info,
- ) {
+ Some(&mut matches),
+ )
+ .unwrap_or(false)
+ {
self.add_library(
&mut libraries,
&format!("{}-libmemcached", name),
- Some(&matches["version"]),
+ matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
Some("libmemcached version"),
&[],
&[],
@@ -929,14 +1093,21 @@ impl PlatformRepository {
_ => "".to_string(),
};
// OpenSSL 1.1.1g 21 Apr 2020
- if let Ok(Some(matches)) = Preg::is_match_strict_groups(
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
"{^(?:OpenSSL|LibreSSL)?\\s*(?<version>\\S+)}i",
&openssl_text_str,
- ) {
+ Some(&mut matches),
+ )
+ .unwrap_or(false)
+ {
+ let version = matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .cloned()
+ .unwrap_or_default();
let mut is_fips = false;
let parsed_version =
- Version::parse_openssl(&matches["version"], &mut is_fips)
- .unwrap_or_default();
+ Version::parse_openssl(&version, &mut is_fips).unwrap_or_default();
let mut provides_list: Vec<String> = Vec::new();
if is_fips {
provides_list.push(name.to_string());
@@ -965,14 +1136,20 @@ impl PlatformRepository {
let info = self.runtime.get_extension_info(name)?;
// PCRE Unicode Version => 12.1.0
- if let Ok(Some(pcre_unicode_matches)) = Preg::is_match_strict_groups(
+ let mut pcre_unicode_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
"/^PCRE Unicode Version => (?<version>.+)$/im",
&info,
- ) {
+ Some(&mut pcre_unicode_matches),
+ )
+ .unwrap_or(false)
+ {
self.add_library(
&mut libraries,
&format!("{}-unicode", name),
- Some(&pcre_unicode_matches["version"]),
+ pcre_unicode_matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
Some("PCRE Unicode version support"),
&[],
&[],
@@ -983,14 +1160,20 @@ impl PlatformRepository {
"mysqlnd" | "pdo_mysql" => {
let info = self.runtime.get_extension_info(name)?;
- if let Ok(Some(matches)) = Preg::is_match_strict_groups(
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
"/^(?:Client API version|Version) => mysqlnd (?<version>.+?) /mi",
&info,
- ) {
+ Some(&mut matches),
+ )
+ .unwrap_or(false)
+ {
self.add_library(
&mut libraries,
&format!("{}-mysqlnd", name),
- Some(&matches["version"]),
+ matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
Some(&format!("mysqlnd library version for {}", name)),
&[],
&[],
@@ -1001,28 +1184,40 @@ impl PlatformRepository {
"mongodb" => {
let info = self.runtime.get_extension_info(name)?;
- if let Ok(Some(libmongoc_matches)) = Preg::is_match_strict_groups(
+ let mut libmongoc_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
"/^libmongoc bundled version => (?<version>.+)$/im",
&info,
- ) {
+ Some(&mut libmongoc_matches),
+ )
+ .unwrap_or(false)
+ {
self.add_library(
&mut libraries,
&format!("{}-libmongoc", name),
- Some(&libmongoc_matches["version"]),
+ libmongoc_matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
Some("libmongoc version of mongodb"),
&[],
&[],
)?;
}
- if let Ok(Some(libbson_matches)) = Preg::is_match_strict_groups(
+ let mut libbson_matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
"/^libbson bundled version => (?<version>.+)$/im",
&info,
- ) {
+ Some(&mut libbson_matches),
+ )
+ .unwrap_or(false)
+ {
self.add_library(
&mut libraries,
&format!("{}-libbson", name),
- Some(&libbson_matches["version"]),
+ libbson_matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
Some("libbson version of mongodb"),
&[],
&[],
@@ -1049,14 +1244,20 @@ impl PlatformRepository {
// intentional fall-through to next case...
let info = self.runtime.get_extension_info(name)?;
- if let Ok(Some(matches)) = Preg::is_match_strict_groups(
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(
"/^PostgreSQL\\(libpq\\) Version => (?<version>.*)$/im",
&info,
- ) {
+ Some(&mut matches),
+ )
+ .unwrap_or(false)
+ {
self.add_library(
&mut libraries,
&format!("{}-libpq", name),
- Some(&matches["version"]),
+ matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
Some(&format!("libpq for {}", name)),
&[],
&[],
@@ -1068,14 +1269,20 @@ impl PlatformRepository {
"pdo_pgsql" => {
let info = self.runtime.get_extension_info(name)?;
- if let Ok(Some(matches)) = Preg::is_match_strict_groups(
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(
"/^PostgreSQL\\(libpq\\) Version => (?<version>.*)$/im",
&info,
- ) {
+ Some(&mut matches),
+ )
+ .unwrap_or(false)
+ {
self.add_library(
&mut libraries,
&format!("{}-libpq", name),
- Some(&matches["version"]),
+ matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
Some(&format!("libpq for {}", name)),
&[],
&[],
@@ -1088,14 +1295,20 @@ impl PlatformRepository {
// Used Library => Compiled => Linked
// libpq => 14.3 (Ubuntu 14.3-1.pgdg22.04+1) => 15.0.2
- if let Ok(Some(matches)) = Preg::is_match_strict_groups(
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(
"/^libpq => (?<compiled>.+) => (?<linked>.+)$/im",
&info,
- ) {
+ Some(&mut matches),
+ )
+ .unwrap_or(false)
+ {
self.add_library(
&mut libraries,
&format!("{}-libpq", name),
- Some(&matches["linked"]),
+ matches
+ .get(&CaptureKey::ByName("linked".to_string()))
+ .map(|s| s.as_str()),
Some(&format!("libpq for {}", name)),
&[],
&[],
@@ -1165,14 +1378,20 @@ impl PlatformRepository {
"sqlite3" | "pdo_sqlite" => {
let info = self.runtime.get_extension_info(name)?;
- if let Ok(Some(matches)) = Preg::is_match_strict_groups(
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(
"/^SQLite Library => (?<version>.+)$/im",
&info,
- ) {
+ Some(&mut matches),
+ )
+ .unwrap_or(false)
+ {
self.add_library(
&mut libraries,
&format!("{}-sqlite", name),
- Some(&matches["version"]),
+ matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
None,
&[],
&[],
@@ -1183,14 +1402,20 @@ impl PlatformRepository {
"ssh2" => {
let info = self.runtime.get_extension_info(name)?;
- if let Ok(Some(matches)) = Preg::is_match_strict_groups(
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(
"/^libssh2 version => (?<version>.+)$/im",
&info,
- ) {
+ Some(&mut matches),
+ )
+ .unwrap_or(false)
+ {
self.add_library(
&mut libraries,
&format!("{}-libssh2", name),
- Some(&matches["version"]),
+ matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
None,
&[],
&[],
@@ -1214,14 +1439,20 @@ impl PlatformRepository {
)?;
let info = self.runtime.get_extension_info("xsl")?;
- if let Ok(Some(matches)) = Preg::is_match_strict_groups(
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(
"/^libxslt compiled against libxml Version => (?<version>.+)$/im",
&info,
- ) {
+ Some(&mut matches),
+ )
+ .unwrap_or(false)
+ {
self.add_library(
&mut libraries,
"libxslt-libxml",
- Some(&matches["version"]),
+ matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
Some("libxml version libxslt is compiled against"),
&[],
&[],
@@ -1232,14 +1463,20 @@ impl PlatformRepository {
"yaml" => {
let info = self.runtime.get_extension_info("yaml")?;
- if let Ok(Some(matches)) = Preg::is_match_strict_groups(
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(
"/^LibYAML Version => (?<version>.+)$/im",
&info,
- ) {
+ Some(&mut matches),
+ )
+ .unwrap_or(false)
+ {
self.add_library(
&mut libraries,
&format!("{}-libyaml", name),
- Some(&matches["version"]),
+ matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
Some("libyaml version of yaml"),
&[],
&[],
@@ -1289,14 +1526,20 @@ impl PlatformRepository {
// Linked Version => 1.2.8
} else {
let info = self.runtime.get_extension_info(name)?;
- if let Ok(Some(matches)) = Preg::is_match_strict_groups(
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match3(
"/^Linked Version => (?<version>.+)$/im",
&info,
- ) {
+ Some(&mut matches),
+ )
+ .unwrap_or(false)
+ {
self.add_library(
&mut libraries,
name,
- Some(&matches["version"]),
+ matches
+ .get(&CaptureKey::ByName("version".to_string()))
+ .map(|s| s.as_str()),
None,
&[],
&[],
@@ -1362,9 +1605,7 @@ impl PlatformRepository {
return Ok(());
}
- let overrider = self
- .inner
- .find_package(package.get_name().to_string(), "*".to_string());
+ let overrider = self.inner.find_package(package.get_name(), "*".to_string());
let actual_text = if let Some(ref ov) = overrider {
if package.get_version() == ov.get_version() {
"same as actual".to_string()
@@ -1475,11 +1716,15 @@ impl PlatformRepository {
Ok(v) => v,
Err(_) => {
extra_description = Some(format!(" (actual version: {})", pretty_version));
- if let Ok(Some(m)) = Preg::is_match_strict_groups(
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
"{^(\\d+\\.\\d+\\.\\d+(?:\\.\\d+)?)}",
&pretty_version,
- ) {
- pretty_version = m["1"].clone();
+ Some(&mut m),
+ )
+ .unwrap_or(false)
+ {
+ pretty_version = m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default();
} else {
pretty_version = "0".to_string();
}
@@ -1689,7 +1934,7 @@ impl crate::repository::repository_interface::RepositoryInterface for PlatformRe
fn find_package(
&self,
- name: String,
+ name: &str,
constraint: crate::repository::repository_interface::FindPackageConstraint,
) -> Option<Box<dyn crate::package::base_package::BasePackage>> {
self.inner.find_package(name, constraint)
@@ -1697,7 +1942,7 @@ impl crate::repository::repository_interface::RepositoryInterface for PlatformRe
fn find_packages(
&self,
- name: String,
+ name: &str,
constraint: Option<crate::repository::repository_interface::FindPackageConstraint>,
) -> Vec<Box<dyn crate::package::base_package::BasePackage>> {
self.inner.find_packages(name, constraint)
diff --git a/crates/shirabe/src/repository/repository_factory.rs b/crates/shirabe/src/repository/repository_factory.rs
index bb598ed..11e5f61 100644
--- a/crates/shirabe/src/repository/repository_factory.rs
+++ b/crates/shirabe/src/repository/repository_factory.rs
@@ -22,7 +22,7 @@ pub struct RepositoryFactory;
impl RepositoryFactory {
pub fn config_from_string(
io: &dyn IOInterface,
- config: &Config,
+ config: &std::rc::Rc<std::cell::RefCell<Config>>,
repository: &str,
allow_filesystem: bool,
) -> anyhow::Result<IndexMap<String, PhpMixed>> {
@@ -41,8 +41,10 @@ impl RepositoryFactory {
if extension == "json" {
let json = JsonFile::new(
repository.to_string(),
- Some(Factory::create_http_downloader(io, config)?),
- Some(io),
+ Some(std::rc::Rc::new(std::cell::RefCell::new(
+ Factory::create_http_downloader(io, config, IndexMap::new())?,
+ ))),
+ Some(io.clone_box()),
)?;
let data = json.read()?;
let has_packages = data.get("packages").map_or(false, |v| !v.is_null());
@@ -92,7 +94,7 @@ impl RepositoryFactory {
pub fn from_string(
io: &dyn IOInterface,
- config: &Config,
+ config: &std::rc::Rc<std::cell::RefCell<Config>>,
repository: &str,
allow_filesystem: bool,
rm: Option<&mut RepositoryManager>,
@@ -103,7 +105,7 @@ impl RepositoryFactory {
pub fn create_repo(
io: &dyn IOInterface,
- config: &Config,
+ config: &std::rc::Rc<std::cell::RefCell<Config>>,
repo_config: IndexMap<String, PhpMixed>,
rm: Option<&mut RepositoryManager>,
) -> anyhow::Result<Box<dyn RepositoryInterface>> {
@@ -128,15 +130,15 @@ impl RepositoryFactory {
pub fn default_repos(
io: Option<&dyn IOInterface>,
- config: Option<Config>,
+ config: Option<std::rc::Rc<std::cell::RefCell<Config>>>,
rm: Option<&mut RepositoryManager>,
) -> anyhow::Result<Vec<Box<dyn RepositoryInterface>>> {
let config = match config {
Some(c) => c,
- None => Factory::create_config(None, None)?,
+ None => std::rc::Rc::new(std::cell::RefCell::new(Factory::create_config(None, None)?)),
};
if let Some(io) = io {
- io.load_configuration(&config);
+ io.load_configuration(&mut *config.borrow_mut())?;
}
let mut owned_rm;
@@ -151,38 +153,50 @@ impl RepositoryFactory {
owned_rm = Self::manager(
io,
&config,
- Some(Factory::create_http_downloader(io, &config)?),
+ Some(std::rc::Rc::new(std::cell::RefCell::new(
+ Factory::create_http_downloader(io, &config, IndexMap::new())?,
+ ))),
None,
None,
)?;
&mut owned_rm
};
- let repo_configs = config.get_repositories();
+ let repo_configs = config.borrow().get_repositories();
Self::create_repos(rm, repo_configs)
}
pub fn manager(
io: &dyn IOInterface,
- config: &Config,
- http_downloader: Option<HttpDownloader>,
+ config: &std::rc::Rc<std::cell::RefCell<Config>>,
+ http_downloader: Option<std::rc::Rc<std::cell::RefCell<HttpDownloader>>>,
event_dispatcher: Option<EventDispatcher>,
- process: Option<ProcessExecutor>,
+ process: Option<std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>,
) -> anyhow::Result<RepositoryManager> {
let http_downloader = match http_downloader {
Some(h) => h,
- None => Factory::create_http_downloader(io, config)?,
+ None => std::rc::Rc::new(std::cell::RefCell::new(Factory::create_http_downloader(
+ io,
+ config,
+ IndexMap::new(),
+ )?)),
};
let process = match process {
Some(p) => p,
None => {
let mut p = ProcessExecutor::new(io);
p.enable_async();
- p
+ std::rc::Rc::new(std::cell::RefCell::new(p))
}
};
- let mut rm = RepositoryManager::new(io, config, http_downloader, event_dispatcher, process);
+ let mut rm = RepositoryManager::new(
+ io,
+ std::rc::Rc::clone(config),
+ http_downloader,
+ event_dispatcher,
+ Some(process),
+ );
rm.set_repository_class("composer", "Composer\\Repository\\ComposerRepository");
rm.set_repository_class("vcs", "Composer\\Repository\\VcsRepository");
rm.set_repository_class("package", "Composer\\Repository\\PackageRepository");
@@ -205,9 +219,12 @@ impl RepositoryFactory {
pub fn default_repos_with_default_manager(
io: &dyn IOInterface,
) -> anyhow::Result<Vec<Box<dyn RepositoryInterface>>> {
- let config = Factory::create_config(Some(io), None)?;
+ let config = std::rc::Rc::new(std::cell::RefCell::new(Factory::create_config(
+ Some(io),
+ None,
+ )?));
let mut manager = Self::manager(io, &config, None, None, None)?;
- io.load_configuration(&config);
+ io.load_configuration(&mut *config.borrow_mut())?;
Self::default_repos(Some(io), Some(config), Some(&mut manager))
}
diff --git a/crates/shirabe/src/repository/repository_interface.rs b/crates/shirabe/src/repository/repository_interface.rs
index 2a9c8f5..6113997 100644
--- a/crates/shirabe/src/repository/repository_interface.rs
+++ b/crates/shirabe/src/repository/repository_interface.rs
@@ -12,6 +12,15 @@ pub enum FindPackageConstraint {
Constraint(Box<dyn ConstraintInterface>),
}
+impl Clone for FindPackageConstraint {
+ fn clone(&self) -> Self {
+ match self {
+ Self::String(s) => Self::String(s.clone()),
+ Self::Constraint(c) => Self::Constraint(c.clone_box()),
+ }
+ }
+}
+
pub struct LoadPackagesResult {
pub names_found: Vec<String>,
pub packages: Vec<Box<dyn BasePackage>>,
@@ -44,13 +53,13 @@ pub trait RepositoryInterface: Countable + std::fmt::Debug {
fn find_package(
&self,
- name: String,
+ name: &str,
constraint: FindPackageConstraint,
) -> Option<Box<dyn BasePackage>>;
fn find_packages(
&self,
- name: String,
+ name: &str,
constraint: Option<FindPackageConstraint>,
) -> Vec<Box<dyn BasePackage>>;
diff --git a/crates/shirabe/src/repository/repository_manager.rs b/crates/shirabe/src/repository/repository_manager.rs
index cc43aed..a3c5b78 100644
--- a/crates/shirabe/src/repository/repository_manager.rs
+++ b/crates/shirabe/src/repository/repository_manager.rs
@@ -14,32 +14,34 @@ use crate::repository::repository_interface::RepositoryInterface;
use crate::util::http_downloader::HttpDownloader;
use crate::util::process_executor::ProcessExecutor;
+#[derive(Debug)]
pub struct RepositoryManager {
local_repository: Option<Box<dyn InstalledRepositoryInterface>>,
repositories: Vec<Box<dyn RepositoryInterface>>,
repository_classes: IndexMap<String, String>,
io: Box<dyn IOInterface>,
- config: Config,
- http_downloader: HttpDownloader,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
+ http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
event_dispatcher: Option<EventDispatcher>,
- process: ProcessExecutor,
+ process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
}
impl RepositoryManager {
pub fn new(
io: &dyn IOInterface,
- config: &Config,
- http_downloader: HttpDownloader,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
+ http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
event_dispatcher: Option<EventDispatcher>,
- process: Option<ProcessExecutor>,
+ process: Option<std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>,
) -> Self {
- let process = process.unwrap_or_else(|| ProcessExecutor::new(io));
+ let process = process
+ .unwrap_or_else(|| std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(io))));
Self {
local_repository: None,
repositories: vec![],
repository_classes: IndexMap::new(),
io: io.clone_box(),
- config: config.clone(),
+ config,
http_downloader,
event_dispatcher,
process,
diff --git a/crates/shirabe/src/repository/repository_set.rs b/crates/shirabe/src/repository/repository_set.rs
index 6985a18..18c3ba6 100644
--- a/crates/shirabe/src/repository/repository_set.rs
+++ b/crates/shirabe/src/repository/repository_set.rs
@@ -180,18 +180,17 @@ impl RepositorySet {
.into());
}
- let repos: Vec<Box<dyn RepositoryInterface>> = if let Some(composite) =
- (repo.as_any() as &dyn Any).downcast_ref::<CompositeRepository>()
- {
- // TODO(phase-b): clone composite.get_repositories() — Box<dyn RepositoryInterface> cloning
- composite
- .get_repositories()
- .iter()
- .map(|r| r.clone_box())
- .collect()
- } else {
- vec![repo]
- };
+ let repos: Vec<Box<dyn RepositoryInterface>> =
+ if let Some(composite) = repo.as_any().downcast_ref::<CompositeRepository>() {
+ // TODO(phase-b): clone composite.get_repositories() — Box<dyn RepositoryInterface> cloning
+ composite
+ .get_repositories()
+ .iter()
+ .map(|r| r.clone_box())
+ .collect()
+ } else {
+ vec![repo]
+ };
for repo in repos {
self.repositories.push(repo);
@@ -321,7 +320,7 @@ impl RepositorySet {
let mut map: IndexMap<String, Box<dyn ConstraintInterface>> = IndexMap::new();
for package in packages {
// ignore root alias versions as they are not actual package versions and should not matter when it comes to vulnerabilities
- if let Some(alias) = (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>() {
+ if let Some(alias) = package.as_any().downcast_ref::<AliasPackage>() {
if alias.is_root_package_alias() {
continue;
}
@@ -475,10 +474,12 @@ impl RepositorySet {
pool_builder.set_allowed_types(allowed_types);
for repo in &self.repositories {
- let is_installed = (repo.as_any() as &dyn Any)
+ let is_installed = repo
+ .as_any()
.downcast_ref::<dyn InstalledRepositoryInterface>()
.is_some()
- || (repo.as_any() as &dyn Any)
+ || repo
+ .as_any()
.downcast_ref::<InstalledRepository>()
.is_some();
if is_installed && !self.allow_installed_repositories {
@@ -500,10 +501,12 @@ impl RepositorySet {
/// Create a pool for dependency resolution from the packages in this repository set.
pub fn create_pool_with_all_packages(&mut self) -> Result<Pool> {
for repo in &self.repositories {
- let is_installed = (repo.as_any() as &dyn Any)
+ let is_installed = repo
+ .as_any()
.downcast_ref::<dyn InstalledRepositoryInterface>()
.is_some()
- || (repo.as_any() as &dyn Any)
+ || repo
+ .as_any()
.downcast_ref::<InstalledRepository>()
.is_some();
if is_installed && !self.allow_installed_repositories {
@@ -527,12 +530,12 @@ impl RepositorySet {
if let Some(versions) = self.root_aliases.get(&name) {
if let Some(alias) = versions.get(&version) {
- while let Some(alias_pkg) =
- (package.as_any() as &dyn Any).downcast_ref::<AliasPackage>()
+ while let Some(alias_pkg) = package.as_any().downcast_ref::<AliasPackage>()
{
package = alias_pkg.get_alias_of().clone_box();
}
- let alias_package: Box<dyn BasePackage> = if (package.as_any() as &dyn Any)
+ let alias_package: Box<dyn BasePackage> = if package
+ .as_any()
.downcast_ref::<CompletePackage>()
.is_some()
{
diff --git a/crates/shirabe/src/repository/repository_utils.rs b/crates/shirabe/src/repository/repository_utils.rs
index d39daa1..0f4f6a8 100644
--- a/crates/shirabe/src/repository/repository_utils.rs
+++ b/crates/shirabe/src/repository/repository_utils.rs
@@ -23,7 +23,7 @@ impl RepositoryUtils {
}
for candidate in packages {
- for name in candidate.get_names() {
+ for name in candidate.get_names(true) {
if requires.contains_key(&name) {
let already_in_bucket = bucket.iter().any(|b| {
std::ptr::eq(
@@ -52,10 +52,8 @@ impl RepositoryUtils {
unwrap_filter_repos: bool,
) -> Vec<Box<dyn RepositoryInterface>> {
let repo: Box<dyn RepositoryInterface> = if unwrap_filter_repos {
- if let Some(filter_repo) =
- (repo.as_any() as &dyn Any).downcast_ref::<FilterRepository>()
- {
- filter_repo.get_repository()
+ if let Some(filter_repo) = repo.as_any().downcast_ref::<FilterRepository>() {
+ filter_repo.get_repository().clone_box()
} else {
repo
}
@@ -63,12 +61,10 @@ impl RepositoryUtils {
repo
};
- if let Some(composite_repo) =
- (repo.as_any() as &dyn Any).downcast_ref::<CompositeRepository>()
- {
+ if let Some(composite_repo) = repo.as_any().downcast_ref::<CompositeRepository>() {
let mut repos = Vec::new();
for r in composite_repo.get_repositories() {
- for r2 in Self::flatten_repositories(r, unwrap_filter_repos) {
+ for r2 in Self::flatten_repositories(r.clone_box(), unwrap_filter_repos) {
repos.push(r2);
}
}
diff --git a/crates/shirabe/src/repository/root_package_repository.rs b/crates/shirabe/src/repository/root_package_repository.rs
index 6c6e25d..56131f1 100644
--- a/crates/shirabe/src/repository/root_package_repository.rs
+++ b/crates/shirabe/src/repository/root_package_repository.rs
@@ -41,7 +41,7 @@ impl RepositoryInterface for RootPackageRepository {
fn find_package(
&self,
- name: String,
+ name: &str,
constraint: crate::repository::repository_interface::FindPackageConstraint,
) -> Option<Box<dyn BasePackage>> {
self.inner.find_package(name, constraint)
@@ -49,7 +49,7 @@ impl RepositoryInterface for RootPackageRepository {
fn find_packages(
&self,
- name: String,
+ name: &str,
constraint: Option<crate::repository::repository_interface::FindPackageConstraint>,
) -> Vec<Box<dyn BasePackage>> {
self.inner.find_packages(name, constraint)
diff --git a/crates/shirabe/src/repository/vcs/forgejo_driver.rs b/crates/shirabe/src/repository/vcs/forgejo_driver.rs
index 2efbfb7..179f2db 100644
--- a/crates/shirabe/src/repository/vcs/forgejo_driver.rs
+++ b/crates/shirabe/src/repository/vcs/forgejo_driver.rs
@@ -3,7 +3,7 @@
use crate::io::io_interface;
use anyhow::Result;
use indexmap::IndexMap;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_php_shim::{
PhpMixed, RuntimeException, base64_decode, explode, extension_loaded, urlencode,
};
@@ -15,6 +15,7 @@ use crate::io::io_interface::IOInterface;
use crate::json::json_file::JsonFile;
use crate::repository::vcs::git_driver::GitDriver;
use crate::repository::vcs::vcs_driver::VcsDriverBase;
+use crate::repository::vcs::vcs_driver_interface::VcsDriverInterface;
use crate::util::forgejo::Forgejo;
use crate::util::forgejo_repository_data::ForgejoRepositoryData;
use crate::util::forgejo_url::ForgejoUrl;
@@ -39,6 +40,7 @@ impl ForgejoDriver {
"{}/{}/{}/{}",
self.inner
.config
+ .borrow_mut()
.get("cache-repo-dir")
.as_string()
.unwrap_or(""),
@@ -53,6 +55,7 @@ impl ForgejoDriver {
c.set_read_only(
self.inner
.config
+ .borrow_mut()
.get("cache-read-only")
.as_bool()
.unwrap_or(false),
@@ -313,7 +316,7 @@ impl ForgejoDriver {
identifier: &str,
) -> Result<Option<IndexMap<String, PhpMixed>>> {
if let Some(ref mut git_driver) = self.git_driver {
- return git_driver.inner.get_composer_information(identifier);
+ return git_driver.get_composer_information(identifier);
}
if !self.inner.info_cache.contains_key(identifier) {
@@ -321,7 +324,12 @@ impl ForgejoDriver {
if let Some(res) = self.inner.cache.as_ref().and_then(|c| c.read(identifier)) {
JsonFile::parse_json(&res, None)?
} else {
- let c = self.inner.get_base_composer_information(identifier)?;
+ let file_content = self.get_file_content("composer.json", identifier)?;
+ let c = VcsDriverBase::finish_base_composer_information(
+ identifier,
+ file_content,
+ || self.get_change_date(identifier),
+ )?;
if self.inner.should_cache(identifier) {
if let Some(ref composer_map) = c {
let encoded = JsonFile::encode_with_options(
@@ -338,7 +346,10 @@ impl ForgejoDriver {
c
}
} else {
- self.inner.get_base_composer_information(identifier)?
+ let file_content = self.get_file_content("composer.json", identifier)?;
+ VcsDriverBase::finish_base_composer_information(identifier, file_content, || {
+ self.get_change_date(identifier)
+ })?
};
let mut composer = composer;
@@ -484,11 +495,11 @@ impl ForgejoDriver {
}
if !extension_loaded("openssl") {
- io.write_error(
- PhpMixed::String(format!(
+ io.write_error3(
+ &format!(
"Skipping Forgejo driver for {} because the OpenSSL PHP extension is missing.",
url
- )),
+ ),
true,
io_interface::VERBOSE,
);
@@ -510,7 +521,7 @@ impl ForgejoDriver {
todo!("clone io for GitDriver setup"),
self.inner.config.clone(),
self.inner.http_downloader.clone(),
- self.inner.process.clone(),
+ std::rc::Rc::clone(&self.inner.process),
),
tags: None,
branches: None,
@@ -556,8 +567,11 @@ impl ForgejoDriver {
let links = explode(",", &header);
for link in links {
- if let Some(m) = Preg::match_strict_groups(r#"{<(.+?)>; *rel="next"}"#, &link) {
- if let Some(url) = m.get("1") {
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::match_strict_groups3(r#"{<(.+?)>; *rel="next"}"#, &link, Some(&mut m))
+ .unwrap_or(false)
+ {
+ if let Some(url) = m.get(&CaptureKey::ByIndex(1)) {
return Some(url.clone());
}
}
@@ -650,14 +664,10 @@ impl ForgejoDriver {
Ok(()) => Ok(true),
Err(e) => {
self.git_driver = None;
- self.inner.io.write_error(
- PhpMixed::String(format!(
- "<error>Failed to clone the {} repository, try running in interactive mode so that you can enter your Forgejo credentials</error>",
- ssh_url
- )),
- true,
- io_interface::NORMAL,
- );
+ self.inner.io.write_error3(&format!(
+ "<error>Failed to clone the {} repository, try running in interactive mode so that you can enter your Forgejo credentials</error>",
+ ssh_url
+ ), true, io_interface::NORMAL);
Err(e)
}
}
diff --git a/crates/shirabe/src/repository/vcs/fossil_driver.rs b/crates/shirabe/src/repository/vcs/fossil_driver.rs
index a765c4c..f0c3468 100644
--- a/crates/shirabe/src/repository/vcs/fossil_driver.rs
+++ b/crates/shirabe/src/repository/vcs/fossil_driver.rs
@@ -29,9 +29,11 @@ impl FossilDriver {
self.check_fossil()?;
// Ensure we are allowed to use this URL by config.
- self.inner
- .config
- .prohibit_url_by_config(&self.inner.url, &*self.inner.io)?;
+ self.inner.config.borrow_mut().prohibit_url_by_config(
+ &self.inner.url,
+ Some(&*self.inner.io),
+ &indexmap::IndexMap::new(),
+ )?;
// Only if url points to a locally accessible directory, assume it's the checkout directory.
// Otherwise, it should be something fossil can clone from.
@@ -41,6 +43,7 @@ impl FossilDriver {
let cache_repo_dir = self
.inner
.config
+ .borrow_mut()
.get("cache-repo-dir")
.as_string()
.unwrap_or("")
@@ -48,6 +51,7 @@ impl FossilDriver {
let cache_vcs_dir = self
.inner
.config
+ .borrow_mut()
.get("cache-vcs-dir")
.as_string()
.unwrap_or("")
@@ -60,7 +64,7 @@ impl FossilDriver {
.into());
}
- let local_name = Preg::replace(r"{[^a-z0-9]}i", "-", self.inner.url.clone());
+ let local_name = Preg::replace(r"{[^a-z0-9]}i", "-", &self.inner.url);
self.repo_file = Some(format!("{}/{}.fossil", cache_repo_dir, local_name));
self.checkout_dir = format!("{}/{}/", cache_vcs_dir, local_name);
@@ -75,7 +79,7 @@ impl FossilDriver {
pub(crate) fn check_fossil(&self) -> anyhow::Result<()> {
let mut ignored_output = String::new();
- if self.inner.process.execute(
+ if self.inner.process.borrow_mut().execute_args(
&["fossil", "version"].map(|s| s.to_string()).to_vec(),
&mut ignored_output,
None,
@@ -84,7 +88,7 @@ impl FossilDriver {
return Err(RuntimeException {
message: format!(
"fossil was not found, check that it is installed and in your PATH env.\n\n{}",
- self.inner.process.get_error_output()
+ self.inner.process.borrow().get_error_output()
),
code: 0,
}
@@ -115,27 +119,23 @@ impl FossilDriver {
// update the repo if it is a valid fossil repository
if is_file(&repo_file)
&& is_dir(&self.checkout_dir)
- && self.inner.process.execute(
+ && self.inner.process.borrow_mut().execute_args(
&["fossil", "info"].map(|s| s.to_string()).to_vec(),
&mut String::new(),
Some(self.checkout_dir.clone()),
) == 0
{
- if self.inner.process.execute(
+ if self.inner.process.borrow_mut().execute_args(
&["fossil", "pull"].map(|s| s.to_string()).to_vec(),
&mut String::new(),
Some(self.checkout_dir.clone()),
) != 0
{
- self.inner.io.write_error(
- PhpMixed::String(format!(
- "<error>Failed to update {}, package information from this repository may be outdated ({})</error>",
- self.inner.url,
- self.inner.process.get_error_output()
- )),
- true,
- io_interface::NORMAL,
- );
+ self.inner.io.write_error3(&format!(
+ "<error>Failed to update {}, package information from this repository may be outdated ({})</error>",
+ self.inner.url,
+ self.inner.process.borrow().get_error_output()
+ ), true, io_interface::NORMAL);
}
} else {
// clean up directory and do a fresh clone into it
@@ -144,7 +144,7 @@ impl FossilDriver {
fs.ensure_directory_exists(&self.checkout_dir)?;
let mut output = String::new();
- if self.inner.process.execute(
+ if self.inner.process.borrow_mut().execute_args(
&["fossil", "clone", "--", &self.inner.url, &repo_file]
.map(|s| s.to_string())
.to_vec(),
@@ -152,7 +152,7 @@ impl FossilDriver {
None,
) != 0
{
- let output = self.inner.process.get_error_output();
+ let output = self.inner.process.borrow().get_error_output();
return Err(RuntimeException {
message: format!(
"Failed to clone {} to repository {}\n\n{}",
@@ -163,7 +163,7 @@ impl FossilDriver {
.into());
}
- if self.inner.process.execute(
+ if self.inner.process.borrow_mut().execute_args(
&["fossil", "open", "--nested", "--", &repo_file]
.map(|s| s.to_string())
.to_vec(),
@@ -171,7 +171,7 @@ impl FossilDriver {
Some(self.checkout_dir.clone()),
) != 0
{
- let output = self.inner.process.get_error_output();
+ let output = self.inner.process.borrow().get_error_output();
return Err(RuntimeException {
message: format!(
"Failed to open repository {} in {}\n\n{}",
@@ -222,7 +222,7 @@ impl FossilDriver {
}
let mut content = String::new();
- self.inner.process.execute(
+ self.inner.process.borrow_mut().execute_args(
&["fossil", "cat", "-r", identifier, "--", file]
.map(|s| s.to_string())
.to_vec(),
@@ -239,7 +239,7 @@ impl FossilDriver {
pub fn get_change_date(&self, _identifier: &str) -> anyhow::Result<Option<DateTime<Utc>>> {
let mut output = String::new();
- self.inner.process.execute(
+ self.inner.process.borrow_mut().execute_args(
&["fossil", "finfo", "-b", "-n", "1", "composer.json"]
.map(|s| s.to_string())
.to_vec(),
@@ -257,12 +257,12 @@ impl FossilDriver {
if self.tags.is_none() {
let mut tags: IndexMap<String, String> = IndexMap::new();
let mut output = String::new();
- self.inner.process.execute(
+ self.inner.process.borrow_mut().execute_args(
&["fossil", "tag", "list"].map(|s| s.to_string()).to_vec(),
&mut output,
Some(self.checkout_dir.clone()),
);
- for tag in self.inner.process.split_lines(&output) {
+ for tag in self.inner.process.borrow().split_lines(&output) {
tags.insert(tag.clone(), tag);
}
self.tags = Some(tags);
@@ -274,13 +274,13 @@ impl FossilDriver {
if self.branches.is_none() {
let mut branches: IndexMap<String, String> = IndexMap::new();
let mut output = String::new();
- self.inner.process.execute(
+ self.inner.process.borrow_mut().execute_args(
&["fossil", "branch", "list"].map(|s| s.to_string()).to_vec(),
&mut output,
Some(self.checkout_dir.clone()),
);
- for branch in self.inner.process.split_lines(&output) {
- let branch = Preg::replace(r"/^\*/", "", branch.trim().to_string());
+ for branch in self.inner.process.borrow().split_lines(&output) {
+ let branch = Preg::replace(r"/^\*/", "", &branch.trim());
let branch = branch.trim().to_string();
branches.insert(branch.clone(), branch);
}
@@ -312,7 +312,7 @@ impl FossilDriver {
let process = ProcessExecutor::new(io);
let mut output = String::new();
- if process.execute(
+ if process.execute_args(
&["fossil", "info"].map(|s| s.to_string()).to_vec(),
&mut output,
Some(url),
diff --git a/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs b/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs
index 05e7c61..689c0e8 100644
--- a/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs
+++ b/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs
@@ -4,7 +4,7 @@ use crate::io::io_interface;
use anyhow::Result;
use chrono::{DateTime, Utc};
use indexmap::IndexMap;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_php_shim::{
InvalidArgumentException, LogicException, PhpMixed, RuntimeException, array_key_exists,
array_search_mixed, extension_loaded, http_build_query_mixed, implode, in_array, is_array,
@@ -58,11 +58,14 @@ pub struct GitBitbucketDriver {
impl GitBitbucketDriver {
/// @inheritDoc
pub fn initialize(&mut self) -> Result<()> {
- let matched = Preg::is_match_strict_groups(
+ let mut m: indexmap::IndexMap<CaptureKey, String> = indexmap::IndexMap::new();
+ if !Preg::is_match_strict_groups3(
r"#^https?://bitbucket\.org/([^/]+)/([^/]+?)(?:\.git|/?)?$#i",
&self.inner.url,
- );
- if matched.is_none() {
+ Some(&mut m),
+ )
+ .unwrap_or(false)
+ {
return Err(InvalidArgumentException {
message: sprintf(
"The Bitbucket repository URL %s is invalid. It must be the HTTPS URL of a Bitbucket repository.",
@@ -72,10 +75,9 @@ impl GitBitbucketDriver {
}
.into());
}
- let m = matched.unwrap();
- self.owner = m.get(1).cloned().unwrap_or_default();
- self.repository = m.get(2).cloned().unwrap_or_default();
+ self.owner = m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default();
+ self.repository = m.get(&CaptureKey::ByIndex(2)).cloned().unwrap_or_default();
self.inner.origin_url = "bitbucket.org".to_string();
self.inner.cache = Some(Cache::new(
&*self.inner.io,
@@ -84,6 +86,7 @@ impl GitBitbucketDriver {
&[
self.inner
.config
+ .borrow_mut()
.get("cache-repo-dir")
.as_string()
.unwrap_or("")
@@ -98,6 +101,7 @@ impl GitBitbucketDriver {
self.inner.cache.as_mut().unwrap().set_read_only(
self.inner
.config
+ .borrow_mut()
.get("cache-read-only")
.as_bool()
.unwrap_or(false),
@@ -236,7 +240,12 @@ impl GitBitbucketDriver {
} {
// composer already set above
} else {
- composer = self.inner.get_base_composer_information(identifier)?;
+ let file_content = self.get_file_content("composer.json", identifier)?;
+ composer = VcsDriverBase::finish_base_composer_information(
+ identifier,
+ file_content,
+ || self.get_change_date(identifier),
+ )?;
if self.inner.should_cache(identifier) {
self.inner.cache.as_ref().unwrap().write(
@@ -664,16 +673,17 @@ impl GitBitbucketDriver {
url: &str,
fetching_repo_data: bool,
) -> Result<Response> {
- match self.inner.get_contents(url, false) {
+ match self.inner.get_contents(url) {
Ok(r) => Ok(r),
Err(e) => {
// TODO(phase-b): only handle TransportException
- let bitbucket_util = Bitbucket::new(
- &*self.inner.io,
- &self.inner.config,
- Some(self.inner.process.clone()),
- Some(self.inner.http_downloader.clone()),
- );
+ let mut bitbucket_util = Bitbucket::new(
+ self.inner.io.clone_box(),
+ std::rc::Rc::clone(&self.inner.config),
+ Some(std::rc::Rc::clone(&self.inner.process)),
+ Some(std::rc::Rc::clone(&self.inner.http_downloader)),
+ None,
+ )?;
if let Some(te) = e.downcast_ref::<TransportException>() {
let code = te.get_code();
@@ -693,7 +703,7 @@ impl GitBitbucketDriver {
if !self.inner.io.has_authentication(&self.inner.origin_url)
&& bitbucket_util.authorize_oauth(&self.inner.origin_url)
{
- return self.inner.get_contents(url, false);
+ return self.inner.get_contents(url);
}
if !self.inner.io.is_interactive() && fetching_repo_data {
@@ -833,7 +843,9 @@ impl GitBitbucketDriver {
if !Preg::is_match(
r"#^https?://bitbucket\.org/([^/]+)/([^/]+?)(\.git|/?)?$#i",
url,
- ) {
+ )
+ .unwrap_or(false)
+ {
return false;
}
diff --git a/crates/shirabe/src/repository/vcs/git_driver.rs b/crates/shirabe/src/repository/vcs/git_driver.rs
index 33474b1..07836bf 100644
--- a/crates/shirabe/src/repository/vcs/git_driver.rs
+++ b/crates/shirabe/src/repository/vcs/git_driver.rs
@@ -4,7 +4,7 @@ use crate::io::io_interface;
use chrono::TimeZone;
use chrono::{DateTime, Utc};
use indexmap::IndexMap;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_php_shim::{
InvalidArgumentException, RuntimeException, dirname, is_dir, is_writable, realpath,
sys_get_temp_dir,
@@ -32,7 +32,7 @@ impl GitDriver {
pub fn initialize(&mut self) -> anyhow::Result<()> {
let cache_url;
if Filesystem::is_local_path(&self.inner.url) {
- self.inner.url = Preg::replace(r"{[\\/]\.git/?$}", "", self.inner.url.clone())?;
+ self.inner.url = Preg::replace(r"{[\\/]\.git/?$}", "", &self.inner.url)?;
if !is_dir(&self.inner.url) {
return Err(RuntimeException {
message: format!(
@@ -49,6 +49,7 @@ impl GitDriver {
let cache_vcs_dir = self
.inner
.config
+ .borrow_mut()
.get("cache-vcs-dir")
.as_string()
.unwrap_or("")
@@ -97,9 +98,9 @@ impl GitDriver {
let git_util = GitUtil::new(
&*self.inner.io,
- &self.inner.config,
- &self.inner.process,
- &Filesystem::new(None),
+ std::rc::Rc::clone(&self.inner.config),
+ std::rc::Rc::clone(&self.inner.process),
+ std::rc::Rc::new(std::cell::RefCell::new(Filesystem::new(None))),
);
if !git_util.sync_mirror(&self.inner.url, &self.repo_dir)? {
if !is_dir(&self.repo_dir) {
@@ -112,14 +113,10 @@ impl GitDriver {
}
.into());
}
- self.inner.io.write_error(
- shirabe_php_shim::PhpMixed::String(format!(
- "<error>Failed to update {}, package information from this repository may be outdated</error>",
- self.inner.url
- )),
- true,
- io_interface::NORMAL,
- );
+ self.inner.io.write_error3(shirabe_php_shim::PhpMixed::String(format!(
+ "<error>Failed to update {}, package information from this repository may be outdated</error>",
+ self.inner.url
+ )), true, io_interface::NORMAL);
}
cache_url = self.inner.url.clone();
@@ -131,6 +128,7 @@ impl GitDriver {
let cache_repo_dir = self
.inner
.config
+ .borrow_mut()
.get("cache-repo-dir")
.as_string()
.unwrap_or("")
@@ -147,6 +145,7 @@ impl GitDriver {
c.set_read_only(
self.inner
.config
+ .borrow_mut()
.get("cache-read-only")
.as_bool()
.unwrap_or(false),
@@ -162,9 +161,9 @@ impl GitDriver {
let git_util = GitUtil::new(
&*self.inner.io,
- &self.inner.config,
- &self.inner.process,
- &Filesystem::new(None),
+ std::rc::Rc::clone(&self.inner.config),
+ std::rc::Rc::clone(&self.inner.process),
+ std::rc::Rc::new(std::cell::RefCell::new(Filesystem::new(None))),
);
if !Filesystem::is_local_path(&self.inner.url) {
let default_branch =
@@ -176,7 +175,7 @@ impl GitDriver {
}
let mut output = String::new();
- self.inner.process.execute(
+ self.inner.process.borrow_mut().execute_args(
&[
"git".to_string(),
"branch".to_string(),
@@ -185,12 +184,15 @@ impl GitDriver {
&mut output,
Some(self.repo_dir.clone()),
);
- let branches = self.inner.process.split_lines(&output);
+ let branches = self.inner.process.borrow().split_lines(&output);
if !branches.contains(&"* master".to_string()) {
for branch in &branches {
if !branch.is_empty() {
- if let Some(caps) = Preg::match_strict_groups(r"{^\* +(\S+)}", branch) {
- if let Some(name) = caps.get("1") {
+ let mut caps: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::match_strict_groups3(r"{^\* +(\S+)}", branch, Some(&mut caps))
+ .unwrap_or(false)
+ {
+ if let Some(name) = caps.get(&CaptureKey::ByIndex(1)) {
self.root_identifier = Some(name.clone());
break;
}
@@ -236,7 +238,7 @@ impl GitDriver {
}
let mut content = String::new();
- self.inner.process.execute(
+ self.inner.process.borrow_mut().execute_args(
&[
"git".to_string(),
"show".to_string(),
@@ -274,9 +276,11 @@ impl GitDriver {
],
);
let mut output = String::new();
- self.inner
- .process
- .execute(&command, &mut output, Some(self.repo_dir.clone()));
+ self.inner.process.borrow_mut().execute_args(
+ &command,
+ &mut output,
+ Some(self.repo_dir.clone()),
+ );
let timestamp_str = GitUtil::parse_rev_list_output(&output, &self.inner.process);
let timestamp: i64 = timestamp_str.trim().parse().unwrap_or(0);
@@ -288,7 +292,7 @@ impl GitDriver {
self.tags = Some(IndexMap::new());
let mut output = String::new();
- self.inner.process.execute(
+ self.inner.process.borrow_mut().execute_args(
&[
"git".to_string(),
"show-ref".to_string(),
@@ -298,13 +302,20 @@ impl GitDriver {
&mut output,
Some(self.repo_dir.clone()),
);
- for tag in self.inner.process.split_lines(&output) {
+ for tag in self.inner.process.borrow().split_lines(&output) {
if !tag.is_empty() {
- if let Some(caps) = Preg::match_strict_groups(
+ let mut caps: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::match_strict_groups3(
r"{^([a-f0-9]{40}) refs/tags/(\S+?)(\^\{\})?$}",
&tag,
- ) {
- if let (Some(hash), Some(name)) = (caps.get("1"), caps.get("2")) {
+ Some(&mut caps),
+ )
+ .unwrap_or(false)
+ {
+ if let (Some(hash), Some(name)) = (
+ caps.get(&CaptureKey::ByIndex(1)),
+ caps.get(&CaptureKey::ByIndex(2)),
+ ) {
self.tags
.as_mut()
.unwrap()
@@ -323,7 +334,7 @@ impl GitDriver {
let mut branches = IndexMap::new();
let mut output = String::new();
- self.inner.process.execute(
+ self.inner.process.borrow_mut().execute_args(
&[
"git".to_string(),
"branch".to_string(),
@@ -334,15 +345,22 @@ impl GitDriver {
&mut output,
Some(self.repo_dir.clone()),
);
- for branch in self.inner.process.split_lines(&output) {
+ for branch in self.inner.process.borrow().split_lines(&output) {
if !branch.is_empty()
&& !Preg::is_match(r"{^ *[^/]+/HEAD }", &branch).unwrap_or(false)
{
- if let Some(caps) = Preg::match_strict_groups(
+ let mut caps: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::match_strict_groups3(
r"{^(?:\* )? *(\S+) *([a-f0-9]+)(?: .*)?$}",
&branch,
- ) {
- if let (Some(name), Some(hash)) = (caps.get("1"), caps.get("2")) {
+ Some(&mut caps),
+ )
+ .unwrap_or(false)
+ {
+ if let (Some(name), Some(hash)) = (
+ caps.get(&CaptureKey::ByIndex(1)),
+ caps.get(&CaptureKey::ByIndex(2)),
+ ) {
if !name.starts_with('-') {
branches.insert(name.clone(), hash.clone());
}
@@ -378,9 +396,9 @@ impl GitDriver {
return Ok(false);
}
- let process = ProcessExecutor::new(io);
+ let process = std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(io)));
let mut output = String::new();
- if process.execute(
+ if process.borrow_mut().execute_args(
&["git".to_string(), "tag".to_string()],
&mut output,
Some(url.clone()),
@@ -388,15 +406,27 @@ impl GitDriver {
{
return Ok(true);
}
- GitUtil::check_for_repo_ownership_error(&process.get_error_output(), &url);
+ GitUtil::check_for_repo_ownership_error(&process.borrow().get_error_output(), &url);
}
if !deep {
return Ok(false);
}
- let process = ProcessExecutor::new(io);
- let git_util = GitUtil::new(io, _config, &process, &Filesystem::new(None));
+ let process = std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(io)));
+ // TODO(phase-b): supports() takes &Config; GitUtil now needs Rc<RefCell<Config>>.
+ // Skipping clean Rc construction since we cannot reconstruct one from a borrowed &Config.
+ let _ = _config;
+ return Err(anyhow::anyhow!(
+ "GitDriver::supports requires Rc<RefCell<Config>>: not yet ported"
+ ));
+ #[allow(unreachable_code)]
+ let git_util = GitUtil::new(
+ io.clone_box(),
+ todo!(),
+ std::rc::Rc::clone(&process),
+ std::rc::Rc::new(std::cell::RefCell::new(Filesystem::new(None))),
+ );
GitUtil::clean_env(&process);
let result = git_util.run_commands(
diff --git a/crates/shirabe/src/repository/vcs/github_driver.rs b/crates/shirabe/src/repository/vcs/github_driver.rs
index 7bf15bf..93bfcdb 100644
--- a/crates/shirabe/src/repository/vcs/github_driver.rs
+++ b/crates/shirabe/src/repository/vcs/github_driver.rs
@@ -4,7 +4,7 @@ use crate::io::io_interface;
use anyhow::Result;
use chrono::{DateTime, Utc};
use indexmap::IndexMap;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_php_shim::{
InvalidArgumentException, PhpMixed, RuntimeException, array_diff, array_key_exists, array_map,
array_search_mixed, base64_decode, basename, count, empty, explode, extension_loaded, in_array,
@@ -18,6 +18,7 @@ use crate::io::io_interface::IOInterface;
use crate::json::json_file::JsonFile;
use crate::repository::vcs::git_driver::GitDriver;
use crate::repository::vcs::vcs_driver::VcsDriverBase;
+use crate::repository::vcs::vcs_driver_interface::VcsDriverInterface;
use crate::util::github::GitHub;
use crate::util::http::response::Response;
@@ -45,31 +46,43 @@ pub struct GitHubDriver {
impl GitHubDriver {
pub fn initialize(&mut self) -> Result<()> {
- let match_ = match Preg::is_match_strict_groups(
+ let mut match_: IndexMap<CaptureKey, String> = IndexMap::new();
+ if !Preg::is_match_strict_groups3(
r"#^(?:(?:https?|git)://([^/]+)/|git@([^:]+):/?)([^/]+)/([^/]+?)(?:\.git|/)?$#",
&self.inner.url,
- ) {
- Some(m) => m,
- None => {
- return Err(InvalidArgumentException {
- message: sprintf(
- "The GitHub repository URL %s is invalid.",
- &[PhpMixed::String(self.inner.url.clone())],
- ),
- code: 0,
- }
- .into());
+ Some(&mut match_),
+ )
+ .unwrap_or(false)
+ {
+ return Err(InvalidArgumentException {
+ message: sprintf(
+ "The GitHub repository URL %s is invalid.",
+ &[PhpMixed::String(self.inner.url.clone())],
+ ),
+ code: 0,
}
- };
+ .into());
+ }
- self.owner = match_.get(3).cloned().unwrap_or_default();
- self.repository = match_.get(4).cloned().unwrap_or_default();
+ self.owner = match_
+ .get(&CaptureKey::ByIndex(3))
+ .cloned()
+ .unwrap_or_default();
+ self.repository = match_
+ .get(&CaptureKey::ByIndex(4))
+ .cloned()
+ .unwrap_or_default();
self.inner.origin_url = strtolower(
&match_
- .get(1)
+ .get(&CaptureKey::ByIndex(1))
.cloned()
.filter(|s| !s.is_empty())
- .unwrap_or_else(|| match_.get(2).cloned().unwrap_or_default()),
+ .unwrap_or_else(|| {
+ match_
+ .get(&CaptureKey::ByIndex(2))
+ .cloned()
+ .unwrap_or_default()
+ }),
);
if self.inner.origin_url == "www.github.com" {
self.inner.origin_url = "github.com".to_string();
@@ -80,6 +93,7 @@ impl GitHubDriver {
"{}/{}/{}/{}",
self.inner
.config
+ .borrow_mut()
.get("cache-repo-dir")
.as_string()
.unwrap_or(""),
@@ -95,6 +109,7 @@ impl GitHubDriver {
c.set_read_only(
self.inner
.config
+ .borrow_mut()
.get("cache-read-only")
.as_bool()
.unwrap_or(false),
@@ -111,7 +126,13 @@ impl GitHubDriver {
self.allow_git_fallback = false;
}
- if self.inner.config.get("use-github-api").as_bool() == Some(false)
+ if self
+ .inner
+ .config
+ .borrow_mut()
+ .get("use-github-api")
+ .as_bool()
+ == Some(false)
|| self
.inner
.repo_config
@@ -230,7 +251,12 @@ impl GitHubDriver {
.unwrap_or_default();
JsonFile::parse_json(&res, None)?
} else {
- let composer = self.inner.get_base_composer_information(identifier)?;
+ let file_content = self.get_file_content("composer.json", identifier)?;
+ let composer = VcsDriverBase::finish_base_composer_information(
+ identifier,
+ file_content,
+ || self.get_change_date(identifier),
+ )?;
if self.inner.should_cache(identifier) {
if let Some(ref composer_map) = composer {
@@ -384,7 +410,7 @@ impl GitHubDriver {
] {
let mut options: IndexMap<String, PhpMixed> = IndexMap::new();
options.insert("retry-auth-failure".to_string(), PhpMixed::Bool(false));
- let response = self.inner.http_downloader.get(
+ let response = self.inner.http_downloader.borrow_mut().get(
file_url,
&PhpMixed::Array(options.into_iter().map(|(k, v)| (k, Box::new(v))).collect()),
);
@@ -436,20 +462,26 @@ impl GitHubDriver {
let mut result: Vec<IndexMap<String, PhpMixed>> = vec![];
let mut key: Option<String> = None;
- for line in Preg::split(r"{\r?\n}", &funding) {
+ for line in Preg::split(r"{\r?\n}", &funding).unwrap_or_default() {
let line = trim(&line, None);
- if let Some(m) = Preg::is_match_strict_groups(r"{^(\w+)\s*:\s*(.+)$}", &line) {
- let g1 = m.get(1).cloned().unwrap_or_default();
- let g2 = m.get(2).cloned().unwrap_or_default();
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(r"{^(\w+)\s*:\s*(.+)$}", &line, Some(&mut m))
+ .unwrap_or(false)
+ {
+ let g1 = m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default();
+ let g2 = m.get(&CaptureKey::ByIndex(2)).cloned().unwrap_or_default();
if g2 == "[" {
key = Some(g1);
continue;
}
- if let Some(m2) = Preg::is_match_strict_groups(r"{^\[(.*?)\](?:\s*#.*)?$}", &g2) {
- let inner = m2.get(1).cloned().unwrap_or_default();
+ let mut m2: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(r"{^\[(.*?)\](?:\s*#.*)?$}", &g2, Some(&mut m2))
+ .unwrap_or(false)
+ {
+ let inner = m2.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default();
for item in array_map(
|s: &String| trim(s, None),
- &Preg::split(r#"{[\'\"]?\s*,\s*[\'\"]?}"#, &inner),
+ &Preg::split(r#"{[\'\"]?\s*,\s*[\'\"]?}"#, &inner).unwrap_or_default(),
) {
let mut entry = IndexMap::new();
entry.insert("type".to_string(), PhpMixed::String(g1.clone()));
@@ -459,30 +491,40 @@ impl GitHubDriver {
);
result.push(entry);
}
- } else if let Some(m2) =
- Preg::is_match_strict_groups(r"{^([^#].*?)(?:\s+#.*)?$}", &g2)
+ } else if Preg::is_match_strict_groups3(
+ r"{^([^#].*?)(?:\s+#.*)?$}",
+ &g2,
+ Some(&mut m2),
+ )
+ .unwrap_or(false)
{
let mut entry = IndexMap::new();
entry.insert("type".to_string(), PhpMixed::String(g1.clone()));
entry.insert(
"url".to_string(),
PhpMixed::String(trim(
- &m2.get(1).cloned().unwrap_or_default(),
+ &m2.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default(),
Some("\"' "),
)),
);
result.push(entry);
}
key = None;
- } else if let Some(m) = Preg::is_match_strict_groups(r"{^(\w+)\s*:\s*#\s*$}", &line) {
- key = Some(m.get(1).cloned().unwrap_or_default());
- } else if key.is_some()
- && (Preg::is_match_strict_groups(r"{^-\s*(.+)(?:\s+#.*)?$}", &line).is_some()
- || Preg::is_match_strict_groups(r"{^(.+),(?:\s*#.*)?$}", &line).is_some())
+ } else if Preg::is_match_strict_groups3(r"{^(\w+)\s*:\s*#\s*$}", &line, Some(&mut m))
+ .unwrap_or(false)
{
- let m = Preg::is_match_strict_groups(r"{^-\s*(.+)(?:\s+#.*)?$}", &line)
- .or_else(|| Preg::is_match_strict_groups(r"{^(.+),(?:\s*#.*)?$}", &line))
- .unwrap();
+ key = Some(m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default());
+ } else if key.is_some() && {
+ let mut tmp: IndexMap<CaptureKey, String> = IndexMap::new();
+ Preg::is_match_strict_groups3(r"{^-\s*(.+)(?:\s+#.*)?$}", &line, Some(&mut m))
+ .unwrap_or(false)
+ || Preg::is_match_strict_groups3(r"{^(.+),(?:\s*#.*)?$}", &line, Some(&mut tmp))
+ .unwrap_or(false)
+ && {
+ m = tmp;
+ true
+ }
+ } {
let mut entry = IndexMap::new();
entry.insert(
"type".to_string(),
@@ -490,7 +532,10 @@ impl GitHubDriver {
);
entry.insert(
"url".to_string(),
- PhpMixed::String(trim(&m.get(1).cloned().unwrap_or_default(), Some("\"' "))),
+ PhpMixed::String(trim(
+ &m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default(),
+ Some("\"' "),
+ )),
);
result.push(entry);
} else if key.is_some() && line == "]" {
@@ -623,11 +668,11 @@ impl GitHubDriver {
continue;
}
- self.inner.io.write_error(
- PhpMixed::String(format!(
+ self.inner.io.write_error3(
+ &format!(
"<warning>Funding URL {} not in a supported format.</warning>",
item_url
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -874,21 +919,31 @@ impl GitHubDriver {
}
pub fn supports(io: &dyn IOInterface, config: &Config, url: &str, _deep: bool) -> bool {
- let matches = match Preg::is_match_strict_groups(
+ let mut matches: IndexMap<CaptureKey, String> = IndexMap::new();
+ if !Preg::is_match_strict_groups3(
r"#^((?:https?|git)://([^/]+)/|git@([^:]+):/?)([^/]+)/([^/]+?)(?:\.git|/)?$#",
url,
- ) {
- Some(m) => m,
- None => return false,
- };
+ Some(&mut matches),
+ )
+ .unwrap_or(false)
+ {
+ return false;
+ }
let origin_url = matches
- .get(2)
+ .get(&CaptureKey::ByIndex(2))
.cloned()
.filter(|s| !s.is_empty())
- .unwrap_or_else(|| matches.get(3).cloned().unwrap_or_default());
+ .unwrap_or_else(|| {
+ matches
+ .get(&CaptureKey::ByIndex(3))
+ .cloned()
+ .unwrap_or_default()
+ });
if !in_array(
- PhpMixed::String(strtolower(&Preg::replace(r"{^www\.}i", "", origin_url))),
+ PhpMixed::String(strtolower(
+ &Preg::replace(r"{^www\.}i", "", &origin_url).unwrap_or_default(),
+ )),
&config.get("github-domains"),
false,
) {
@@ -896,11 +951,11 @@ impl GitHubDriver {
}
if !extension_loaded("openssl") {
- io.write_error(
- PhpMixed::String(format!(
+ io.write_error3(
+ &format!(
"Skipping GitHub driver for {} because the OpenSSL PHP extension is missing.",
url
- )),
+ ),
true,
io_interface::VERBOSE,
);
@@ -945,11 +1000,11 @@ impl GitHubDriver {
Ok(r) => Ok(r),
Err(e) => {
let mut git_hub_util = GitHub::new(
- self.inner.io.as_ref(),
- &self.inner.config,
- &self.inner.process,
- &self.inner.http_downloader,
- );
+ self.inner.io.clone_box(),
+ std::rc::Rc::clone(&self.inner.config),
+ Some(std::rc::Rc::clone(&self.inner.process)),
+ Some(std::rc::Rc::clone(&self.inner.http_downloader)),
+ )?;
match e.code {
401 | 404 => {
@@ -1057,11 +1112,11 @@ impl GitHubDriver {
if !self.inner.io.has_authentication(&self.inner.origin_url) {
if !self.inner.io.is_interactive() {
- self.inner.io.write_error(
- PhpMixed::String(format!(
+ self.inner.io.write_error3(
+ &format!(
"<error>GitHub API limit exhausted. Failed to get metadata for the {} repository, try running in interactive mode so that you can enter your GitHub credentials to increase the API limit</error>",
self.inner.url
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -1083,14 +1138,14 @@ impl GitHubDriver {
let rate_limit = git_hub_util.get_rate_limit(
e.get_headers().map(|h| h.as_slice()).unwrap_or(&[]),
);
- self.inner.io.write_error(
- PhpMixed::String(sprintf(
+ self.inner.io.write_error3(
+ &sprintf(
"<error>GitHub API limit (%d calls/hr) is exhausted. You are already authorized so you have to wait until %s before doing more requests</error>",
&[
rate_limit.get("limit").cloned().unwrap_or(PhpMixed::Null),
rate_limit.get("reset").cloned().unwrap_or(PhpMixed::Null),
],
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -1206,11 +1261,11 @@ impl GitHubDriver {
Err(setup_err) => {
self.git_driver = None;
- self.inner.io.write_error(
- PhpMixed::String(format!(
+ self.inner.io.write_error3(
+ &format!(
"<error>Failed to clone the {} repository, try running in interactive mode so that you can enter your GitHub credentials</error>",
self.generate_ssh_url()
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -1233,8 +1288,8 @@ impl GitHubDriver {
repo_config,
self.inner.io.clone(),
self.inner.config.clone(),
- self.inner.http_downloader.clone(),
- self.inner.process.clone(),
+ std::rc::Rc::clone(&self.inner.http_downloader),
+ std::rc::Rc::clone(&self.inner.process),
);
git_driver.initialize()?;
self.git_driver = Some(git_driver);
@@ -1249,8 +1304,11 @@ impl GitHubDriver {
let links = explode(",", &header);
for link in &links {
- if let Some(m) = Preg::is_match_strict_groups(r#"{<(.+?)>; *rel="next"}"#, link) {
- return Some(m.get(1).cloned().unwrap_or_default());
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(r#"{<(.+?)>; *rel="next"}"#, link, Some(&mut m))
+ .unwrap_or(false)
+ {
+ return Some(m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default());
}
}
diff --git a/crates/shirabe/src/repository/vcs/gitlab_driver.rs b/crates/shirabe/src/repository/vcs/gitlab_driver.rs
index dea1bf6..e00bbf8 100644
--- a/crates/shirabe/src/repository/vcs/gitlab_driver.rs
+++ b/crates/shirabe/src/repository/vcs/gitlab_driver.rs
@@ -4,7 +4,7 @@ use crate::io::io_interface;
use anyhow::Result;
use chrono::{DateTime, Utc};
use indexmap::IndexMap;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_php_shim::{
InvalidArgumentException, LogicException, PhpMixed, RuntimeException, array_search_mixed,
array_shift, ctype_alnum, empty, explode, extension_loaded, implode, in_array, is_array,
@@ -18,6 +18,7 @@ use crate::io::io_interface::IOInterface;
use crate::json::json_file::JsonFile;
use crate::repository::vcs::git_driver::GitDriver;
use crate::repository::vcs::vcs_driver::VcsDriverBase;
+use crate::repository::vcs::vcs_driver_interface::VcsDriverInterface;
use crate::util::gitlab::GitLab;
use crate::util::http::response::Response;
use crate::util::http_downloader::HttpDownloader;
@@ -57,30 +58,43 @@ impl GitLabDriver {
///
/// SSH urls use https by default. Set "secure-http": false on the repository config to use http instead.
pub fn initialize(&mut self) -> Result<()> {
- let match_ = match Preg::is_match_strict_groups(Self::URL_REGEX, &self.inner.url) {
- Some(m) => m,
- None => {
- return Err(InvalidArgumentException {
- message: sprintf(
- "The GitLab repository URL %s is invalid. It must be the HTTP URL of a GitLab project.",
- &[PhpMixed::String(self.inner.url.clone())],
- ),
- code: 0,
- }
- .into());
+ let mut match_: IndexMap<CaptureKey, String> = IndexMap::new();
+ if !Preg::is_match_strict_groups3(Self::URL_REGEX, &self.inner.url, Some(&mut match_))
+ .unwrap_or(false)
+ {
+ return Err(InvalidArgumentException {
+ message: sprintf(
+ "The GitLab repository URL %s is invalid. It must be the HTTP URL of a GitLab project.",
+ &[PhpMixed::String(self.inner.url.clone())],
+ ),
+ code: 0,
}
- };
+ .into());
+ }
let guessed_domain = match_
- .get("domain")
+ .get(&CaptureKey::ByName("domain".to_string()))
.cloned()
.filter(|s| !s.is_empty())
- .unwrap_or_else(|| match_.get("domain2").cloned().unwrap_or_default());
- let configured_domains = self.inner.config.get("gitlab-domains");
- let mut url_parts: Vec<String> =
- explode("/", &match_.get("parts").cloned().unwrap_or_default());
+ .unwrap_or_else(|| {
+ match_
+ .get(&CaptureKey::ByName("domain2".to_string()))
+ .cloned()
+ .unwrap_or_default()
+ });
+ let configured_domains = self.inner.config.borrow_mut().get("gitlab-domains");
+ let mut url_parts: Vec<String> = explode(
+ "/",
+ &match_
+ .get(&CaptureKey::ByName("parts".to_string()))
+ .cloned()
+ .unwrap_or_default(),
+ );
- let scheme_match = match_.get("scheme").cloned().unwrap_or_default();
+ let scheme_match = match_
+ .get(&CaptureKey::ByName("scheme".to_string()))
+ .cloned()
+ .unwrap_or_default();
self.scheme = if in_array(
PhpMixed::String(scheme_match.clone()),
&PhpMixed::List(vec![
@@ -101,7 +115,7 @@ impl GitLabDriver {
} else {
"https".to_string()
};
- let port = match_.get("port").cloned();
+ let port = match_.get(&CaptureKey::ByName("port".to_string())).cloned();
let origin = Self::determine_origin(
&configured_domains,
guessed_domain,
@@ -123,7 +137,7 @@ impl GitLabDriver {
};
self.inner.origin_url = origin;
- let protocol_value = self.inner.config.get("gitlab-protocol");
+ let protocol_value = self.inner.config.borrow_mut().get("gitlab-protocol");
if let Some(protocol) = protocol_value
.as_string()
.filter(|_| is_string(&protocol_value))
@@ -161,8 +175,12 @@ impl GitLabDriver {
self.repository = Preg::replace(
r"#(\.git)$#",
"",
- match_.get("repo").cloned().unwrap_or_default(),
- );
+ &match_
+ .get(&CaptureKey::ByName("repo".to_string()))
+ .cloned()
+ .unwrap_or_default(),
+ )
+ .unwrap_or_default();
self.inner.cache = Some(Cache::new(
self.inner.io.as_ref(),
@@ -170,6 +188,7 @@ impl GitLabDriver {
"{}/{}/{}/{}",
self.inner
.config
+ .borrow_mut()
.get("cache-repo-dir")
.as_string()
.unwrap_or(""),
@@ -185,6 +204,7 @@ impl GitLabDriver {
c.set_read_only(
self.inner
.config
+ .borrow_mut()
.get("cache-read-only")
.as_bool()
.unwrap_or(false),
@@ -200,7 +220,10 @@ impl GitLabDriver {
/// Mainly useful for tests.
///
/// @internal
- pub fn set_http_downloader(&mut self, http_downloader: HttpDownloader) {
+ pub fn set_http_downloader(
+ &mut self,
+ http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
+ ) {
self.inner.http_downloader = http_downloader;
}
@@ -229,7 +252,12 @@ impl GitLabDriver {
.unwrap_or_default();
JsonFile::parse_json(&res, None)?
} else {
- let composer = self.inner.get_base_composer_information(identifier)?;
+ let file_content = self.get_file_content("composer.json", identifier)?;
+ let composer = VcsDriverBase::finish_base_composer_information(
+ identifier,
+ file_content,
+ || self.get_change_date(identifier),
+ )?;
if self.inner.should_cache(identifier) {
if let Some(ref composer_map) = composer {
@@ -679,11 +707,11 @@ impl GitLabDriver {
Err(e) => {
self.git_driver = None;
- self.inner.io.write_error(
- PhpMixed::String(format!(
+ self.inner.io.write_error3(
+ &format!(
"<error>Failed to clone the {} repository, try running in interactive mode so that you can enter your credentials</error>",
url
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -721,8 +749,8 @@ impl GitLabDriver {
repo_config,
self.inner.io.clone(),
self.inner.config.clone(),
- self.inner.http_downloader.clone(),
- self.inner.process.clone(),
+ std::rc::Rc::clone(&self.inner.http_downloader),
+ std::rc::Rc::clone(&self.inner.process),
);
git_driver.initialize()?;
self.git_driver = Some(git_driver);
@@ -780,11 +808,8 @@ impl GitLabDriver {
}
if !more_than_guest_access {
- self.inner.io.write_error(
- PhpMixed::String(
- "<warning>GitLab token with Guest or Planner only access detected</warning>"
- .to_string(),
- ),
+ self.inner.io.write_error3(
+ "<warning>GitLab token with Guest or Planner only access detected</warning>",
true,
io_interface::NORMAL,
);
@@ -840,11 +865,11 @@ impl GitLabDriver {
}
Err(e) => {
let mut git_lab_util = GitLab::new(
- self.inner.io.as_ref(),
- &self.inner.config,
- &self.inner.process,
- &self.inner.http_downloader,
- );
+ self.inner.io.clone_box(),
+ std::rc::Rc::clone(&self.inner.config),
+ Some(std::rc::Rc::clone(&self.inner.process)),
+ Some(std::rc::Rc::clone(&self.inner.http_downloader)),
+ )?;
match e.code {
401 | 404 => {
@@ -882,11 +907,11 @@ impl GitLabDriver {
.unwrap()
.unwrap());
}
- self.inner.io.write_error(
- PhpMixed::String(format!(
+ self.inner.io.write_error3(
+ &format!(
"<warning>Failed to download {}/{}:{}</warning>",
self.namespace, self.repository, e.message
- )),
+ ),
true,
io_interface::NORMAL,
);
@@ -938,25 +963,39 @@ impl GitLabDriver {
/// Uses the config `gitlab-domains` to see if the driver supports the url for the
/// repository given.
pub fn supports(io: &dyn IOInterface, config: &Config, url: &str, _deep: bool) -> bool {
- let match_ = match Preg::is_match_strict_groups(Self::URL_REGEX, url) {
- Some(m) => m,
- None => return false,
- };
+ let mut match_: IndexMap<CaptureKey, String> = IndexMap::new();
+ if !Preg::is_match_strict_groups3(Self::URL_REGEX, url, Some(&mut match_)).unwrap_or(false)
+ {
+ return false;
+ }
- let scheme = match_.get("scheme").cloned().unwrap_or_default();
+ let scheme = match_
+ .get(&CaptureKey::ByName("scheme".to_string()))
+ .cloned()
+ .unwrap_or_default();
let guessed_domain = match_
- .get("domain")
+ .get(&CaptureKey::ByName("domain".to_string()))
.cloned()
.filter(|s| !s.is_empty())
- .unwrap_or_else(|| match_.get("domain2").cloned().unwrap_or_default());
- let mut url_parts: Vec<String> =
- explode("/", &match_.get("parts").cloned().unwrap_or_default());
+ .unwrap_or_else(|| {
+ match_
+ .get(&CaptureKey::ByName("domain2".to_string()))
+ .cloned()
+ .unwrap_or_default()
+ });
+ let mut url_parts: Vec<String> = explode(
+ "/",
+ &match_
+ .get(&CaptureKey::ByName("parts".to_string()))
+ .cloned()
+ .unwrap_or_default(),
+ );
if Self::determine_origin(
&config.get("gitlab-domains"),
guessed_domain,
&mut url_parts,
- match_.get("port").cloned(),
+ match_.get(&CaptureKey::ByName("port".to_string())).cloned(),
)
.is_none()
{
@@ -964,11 +1003,11 @@ impl GitLabDriver {
}
if scheme == "https" && !extension_loaded("openssl") {
- io.write_error(
- PhpMixed::String(format!(
+ io.write_error3(
+ &format!(
"Skipping GitLab driver for {} because the OpenSSL PHP extension is missing.",
url
- )),
+ ),
true,
io_interface::VERBOSE,
);
@@ -993,8 +1032,16 @@ impl GitLabDriver {
let links = explode(",", &header);
for link in &links {
- if let Some(match_) = Preg::is_match_strict_groups(r#"{<(.+?)>; *rel="next"}"#, link) {
- return Some(match_.get(1).cloned().unwrap_or_default());
+ let mut match_: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(r#"{<(.+?)>; *rel="next"}"#, link, Some(&mut match_))
+ .unwrap_or(false)
+ {
+ return Some(
+ match_
+ .get(&CaptureKey::ByIndex(1))
+ .cloned()
+ .unwrap_or_default(),
+ );
}
}
@@ -1048,7 +1095,7 @@ impl GitLabDriver {
false,
) || (port_number.is_some()
&& in_array(
- PhpMixed::String(Preg::replace(r"{:\d+}", "", guessed_domain.clone())),
+ PhpMixed::String(Preg::replace(r"{:\d+}", "", &guessed_domain)),
configured_domains,
false,
))
diff --git a/crates/shirabe/src/repository/vcs/hg_driver.rs b/crates/shirabe/src/repository/vcs/hg_driver.rs
index 68393ed..f7c0c16 100644
--- a/crates/shirabe/src/repository/vcs/hg_driver.rs
+++ b/crates/shirabe/src/repository/vcs/hg_driver.rs
@@ -30,6 +30,7 @@ impl HgDriver {
let cache_vcs_dir = self
.inner
.config
+ .borrow_mut()
.get("cache-vcs-dir")
.as_string()
.unwrap_or("")
@@ -58,30 +59,32 @@ impl HgDriver {
}.into());
}
- self.inner
- .config
- .prohibit_url_by_config(&self.inner.url, &*self.inner.io)?;
+ self.inner.config.borrow_mut().prohibit_url_by_config(
+ &self.inner.url,
+ Some(&*self.inner.io),
+ &indexmap::IndexMap::new(),
+ )?;
- let hg_utils = HgUtils::new(&*self.inner.io, &self.inner.config, &self.inner.process);
+ let hg_utils = HgUtils::new(
+ &*self.inner.io,
+ &*self.inner.config.borrow(),
+ &self.inner.process,
+ );
if is_dir(&self.repo_dir)
- && self.inner.process.execute(
+ && self.inner.process.borrow_mut().execute_args(
&["hg", "summary"].map(|s| s.to_string()).to_vec(),
&mut String::new(),
Some(self.repo_dir.clone()),
) == 0
{
- if self.inner.process.execute(
+ if self.inner.process.borrow_mut().execute_args(
&["hg", "pull"].map(|s| s.to_string()).to_vec(),
&mut String::new(),
Some(self.repo_dir.clone()),
) != 0
{
- self.inner.io.write_error(
- format!("<error>Failed to update {}, package information from this repository may be outdated ({})</error>", self.inner.url, self.inner.process.get_error_output()).into(),
- true,
- crate::io::io_interface::NORMAL,
- );
+ self.inner.io.write_error3(format!("<error>Failed to update {}, package information from this repository may be outdated ({})</error>", self.inner.url, self.inner.process.borrow().get_error_output()).into(), true, crate::io::io_interface::NORMAL);
}
} else {
let fs2 = Filesystem::new(None);
@@ -112,14 +115,14 @@ impl HgDriver {
pub fn get_root_identifier(&mut self) -> anyhow::Result<String> {
if self.root_identifier.is_none() {
let mut output = String::new();
- self.inner.process.execute(
+ self.inner.process.borrow_mut().execute_args(
&["hg", "tip", "--template", "{node}"]
.map(|s| s.to_string())
.to_vec(),
&mut output,
Some(self.repo_dir.clone()),
);
- let lines = self.inner.process.split_lines(&output);
+ let lines = self.inner.process.borrow().split_lines(&output);
self.root_identifier = lines.into_iter().next();
}
@@ -163,9 +166,11 @@ impl HgDriver {
file.to_string(),
];
let mut content = String::new();
- self.inner
- .process
- .execute(&resource, &mut content, Some(self.repo_dir.clone()));
+ self.inner.process.borrow_mut().execute_args(
+ &resource,
+ &mut content,
+ Some(self.repo_dir.clone()),
+ );
if content.trim().is_empty() {
return Ok(None);
@@ -187,7 +192,7 @@ impl HgDriver {
}
let mut output = String::new();
- self.inner.process.execute(
+ self.inner.process.borrow_mut().execute_args(
&[
"hg",
"log",
@@ -210,12 +215,12 @@ impl HgDriver {
if self.tags.is_none() {
let mut tags: IndexMap<String, String> = IndexMap::new();
let mut output = String::new();
- self.inner.process.execute(
+ self.inner.process.borrow_mut().execute_args(
&["hg", "tags"].map(|s| s.to_string()).to_vec(),
&mut output,
Some(self.repo_dir.clone()),
);
- for tag in self.inner.process.split_lines(&output) {
+ for tag in self.inner.process.borrow().split_lines(&output) {
if !tag.is_empty() {
if let Some(m) = Preg::match_(r"^([^\s]+)\s+\d+:(.*)$", &tag) {
tags.insert(
@@ -239,12 +244,12 @@ impl HgDriver {
let mut bookmarks: IndexMap<String, String> = IndexMap::new();
let mut output = String::new();
- self.inner.process.execute(
+ self.inner.process.borrow_mut().execute_args(
&["hg", "branches"].map(|s| s.to_string()).to_vec(),
&mut output,
Some(self.repo_dir.clone()),
);
- for branch in self.inner.process.split_lines(&output) {
+ for branch in self.inner.process.borrow().split_lines(&output) {
if !branch.is_empty() {
if let Some(m) = Preg::match_(r"^([^\s]+)\s+\d+:([a-f0-9]+)", &branch) {
let name = m.get("1").cloned().unwrap_or_default();
@@ -256,12 +261,12 @@ impl HgDriver {
}
output.clear();
- self.inner.process.execute(
+ self.inner.process.borrow_mut().execute_args(
&["hg", "bookmarks"].map(|s| s.to_string()).to_vec(),
&mut output,
Some(self.repo_dir.clone()),
);
- for branch in self.inner.process.split_lines(&output) {
+ for branch in self.inner.process.borrow().split_lines(&output) {
if !branch.is_empty() {
if let Some(m) = Preg::match_(r"^(?:[\s*]*)([^\s]+)\s+\d+:(.*)$", &branch) {
let name = m.get("1").cloned().unwrap_or_default();
@@ -298,7 +303,7 @@ impl HgDriver {
let process = crate::util::process_executor::ProcessExecutor::new(io);
let mut output = String::new();
- if process.execute(
+ if process.execute_args(
&["hg", "summary"].map(|s| s.to_string()).to_vec(),
&mut output,
Some(url),
@@ -314,7 +319,7 @@ impl HgDriver {
let process = crate::util::process_executor::ProcessExecutor::new(io);
let mut ignored = String::new();
- let exit = process.execute(
+ let exit = process.execute_args(
&["hg", "identify", "--", url]
.map(|s| s.to_string())
.to_vec(),
diff --git a/crates/shirabe/src/repository/vcs/perforce_driver.rs b/crates/shirabe/src/repository/vcs/perforce_driver.rs
index ee09dee..e3aa868 100644
--- a/crates/shirabe/src/repository/vcs/perforce_driver.rs
+++ b/crates/shirabe/src/repository/vcs/perforce_driver.rs
@@ -59,6 +59,7 @@ impl PerforceDriver {
let cache_vcs_dir = self
.inner
.config
+ .borrow_mut()
.get("cache-vcs-dir")
.as_string()
.unwrap_or("")
diff --git a/crates/shirabe/src/repository/vcs/svn_driver.rs b/crates/shirabe/src/repository/vcs/svn_driver.rs
index 2e649df..8218563 100644
--- a/crates/shirabe/src/repository/vcs/svn_driver.rs
+++ b/crates/shirabe/src/repository/vcs/svn_driver.rs
@@ -3,7 +3,7 @@
use anyhow::Result;
use chrono::{DateTime, TimeZone, Utc};
use indexmap::IndexMap;
-use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_external_packages::composer::pcre::preg::{CaptureKey, Preg};
use shirabe_php_shim::{
JSON_UNESCAPED_SLASHES, JSON_UNESCAPED_UNICODE, PhpMixed, RuntimeException, array_key_exists,
is_array, max, sprintf, stripos, strrpos, strtr, substr, trim,
@@ -90,6 +90,7 @@ impl SvnDriver {
"{}/{}",
self.inner
.config
+ .borrow_mut()
.get("cache-repo-dir")
.as_string()
.unwrap_or(""),
@@ -102,6 +103,7 @@ impl SvnDriver {
self.inner.cache.as_mut().unwrap().set_read_only(
self.inner
.config
+ .borrow_mut()
.get("cache-read-only")
.as_bool()
.unwrap_or(false),
@@ -135,7 +137,10 @@ impl SvnDriver {
}
pub(crate) fn should_cache(&self, identifier: &str) -> bool {
- self.inner.cache.is_some() && Preg::is_match(r"{@\d+$}", identifier)
+ self.inner.cache.is_some()
+ && Preg::is_match(r"{@\d+$}", identifier)
+ .unwrap_or(false)
+ .unwrap_or(false)
}
pub fn get_composer_information(
@@ -170,22 +175,30 @@ impl SvnDriver {
}
// TODO(phase-b): use anyhow::Result<Result<T, E>> to model PHP try/catch
- let composer: Option<IndexMap<String, PhpMixed>> =
- match self.inner.get_base_composer_information(identifier) {
- Ok(c) => c,
- Err(e) => {
- // TODO(phase-b): downcast to TransportException
- let _te: &TransportException = todo!("downcast e to TransportException");
- let message = e.to_string();
- if stripos(&message, "path not found").is_none()
- && stripos(&message, "svn: warning: W160013").is_none()
- {
- return Err(e);
- }
- // remember a not-existent composer.json
- None
+ let base_result =
+ self.get_file_content("composer.json", identifier)
+ .and_then(|file_content| {
+ VcsDriverBase::finish_base_composer_information(
+ identifier,
+ file_content,
+ || self.get_change_date(identifier),
+ )
+ });
+ let composer: Option<IndexMap<String, PhpMixed>> = match base_result {
+ Ok(c) => c,
+ Err(e) => {
+ // TODO(phase-b): downcast to TransportException
+ let _te: &TransportException = todo!("downcast e to TransportException");
+ let message = e.to_string();
+ if stripos(&message, "path not found").is_none()
+ && stripos(&message, "svn: warning: W160013").is_none()
+ {
+ return Err(e);
}
- };
+ // remember a not-existent composer.json
+ None
+ }
+ };
if self.should_cache(identifier) {
let encoded = JsonFile::encode(
@@ -282,12 +295,17 @@ impl SvnDriver {
vec!["svn".to_string(), "info".to_string()],
&format!("{}{}{}", self.base_url, path, rev),
)?;
- for line in self.inner.process.split_lines(&output) {
+ for line in self.inner.process.borrow().split_lines(&output) {
if !line.is_empty() {
- if let Some(m) =
- Preg::is_match_strict_groups(r"{^Last Changed Date: ([^(]+)}", &line)
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
+ r"{^Last Changed Date: ([^(]+)}",
+ &line,
+ Some(&mut m),
+ )
+ .unwrap_or(false)
{
- let date_str = m.get(1).cloned().unwrap_or_default();
+ let date_str = m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default();
// PHP: new \DateTimeImmutable($match[1], new \DateTimeZone('UTC'))
return Ok(Utc
.datetime_from_str(date_str.trim(), "%Y-%m-%d %H:%M:%S %z")
@@ -313,15 +331,23 @@ impl SvnDriver {
.unwrap_or_default();
if !output.is_empty() {
let mut last_rev: i64 = 0;
- for line in self.inner.process.split_lines(&output) {
+ for line in self.inner.process.borrow().split_lines(&output) {
let line = trim(&line, None);
if !line.is_empty() {
- if let Some(m) =
- Preg::is_match_strict_groups(r"{^\s*(\S+).*?(\S+)\s*$}", &line)
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
+ r"{^\s*(\S+).*?(\S+)\s*$}",
+ &line,
+ Some(&mut m),
+ )
+ .unwrap_or(false)
{
- let rev: i64 =
- m.get(1).map(|s| s.parse().unwrap_or(0)).unwrap_or(0);
- let path = m.get(2).cloned().unwrap_or_default();
+ let rev: i64 = m
+ .get(&CaptureKey::ByIndex(1))
+ .and_then(|s| s.parse().ok())
+ .unwrap_or(0);
+ let path =
+ m.get(&CaptureKey::ByIndex(2)).cloned().unwrap_or_default();
if path == "./" {
last_rev = rev;
} else {
@@ -360,14 +386,22 @@ impl SvnDriver {
)
.unwrap_or_default();
if !output.is_empty() {
- for line in self.inner.process.split_lines(&output) {
+ for line in self.inner.process.borrow().split_lines(&output) {
let line = trim(&line, None);
if !line.is_empty() {
- if let Some(m) =
- Preg::is_match_strict_groups(r"{^\s*(\S+).*?(\S+)\s*$}", &line)
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
+ r"{^\s*(\S+).*?(\S+)\s*$}",
+ &line,
+ Some(&mut m),
+ )
+ .unwrap_or(false)
{
- let rev: i64 = m.get(1).map(|s| s.parse().unwrap_or(0)).unwrap_or(0);
- let path = m.get(2).cloned().unwrap_or_default();
+ let rev: i64 = m
+ .get(&CaptureKey::ByIndex(1))
+ .and_then(|s| s.parse().ok())
+ .unwrap_or(0);
+ let path = m.get(&CaptureKey::ByIndex(2)).cloned().unwrap_or_default();
if path == "./" {
let identifier = self.build_identifier(
&format!("/{}", self.trunk_path.clone().unwrap_or_default()),
@@ -393,15 +427,28 @@ impl SvnDriver {
.unwrap_or_default();
if !output.is_empty() {
let mut last_rev: i64 = 0;
- for line in self.inner.process.split_lines(&trim(&output, None)) {
+ for line in self
+ .inner
+ .process
+ .borrow()
+ .split_lines(&trim(&output, None))
+ {
let line = trim(&line, None);
if !line.is_empty() {
- if let Some(m) =
- Preg::is_match_strict_groups(r"{^\s*(\S+).*?(\S+)\s*$}", &line)
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::is_match_strict_groups3(
+ r"{^\s*(\S+).*?(\S+)\s*$}",
+ &line,
+ Some(&mut m),
+ )
+ .unwrap_or(false)
{
- let rev: i64 =
- m.get(1).map(|s| s.parse().unwrap_or(0)).unwrap_or(0);
- let path = m.get(2).cloned().unwrap_or_default();
+ let rev: i64 = m
+ .get(&CaptureKey::ByIndex(1))
+ .and_then(|s| s.parse().ok())
+ .unwrap_or(0);
+ let path =
+ m.get(&CaptureKey::ByIndex(2)).cloned().unwrap_or_default();
if path == "./" {
last_rev = rev;
} else {
@@ -426,7 +473,10 @@ impl SvnDriver {
pub fn supports(io: &dyn IOInterface, _config: &Config, url: &str, deep: bool) -> bool {
let url = Self::normalize_url(url);
- if Preg::is_match(r"#(^svn://|^svn\+ssh://|svn\.)#i", &url) {
+ if Preg::is_match(r"#(^svn://|^svn\+ssh://|svn\.)#i", &url)
+ .unwrap_or(false)
+ .unwrap_or(false)
+ {
return true;
}
@@ -437,7 +487,7 @@ impl SvnDriver {
let mut process = ProcessExecutor::new(io);
let mut ignored_output = String::new();
- let exit = process.execute(
+ let exit = process.execute_args(
&[
"svn".to_string(),
"info".to_string(),
@@ -516,7 +566,7 @@ impl SvnDriver {
message: format!(
"Failed to load {}, svn was not found, check that it is installed and in your PATH env.\n\n{}",
self.inner.url,
- self.inner.process.get_error_output(),
+ self.inner.process.borrow().get_error_output(),
),
code: 0,
}
diff --git a/crates/shirabe/src/repository/vcs/vcs_driver.rs b/crates/shirabe/src/repository/vcs/vcs_driver.rs
index 162792e..e356a6f 100644
--- a/crates/shirabe/src/repository/vcs/vcs_driver.rs
+++ b/crates/shirabe/src/repository/vcs/vcs_driver.rs
@@ -1,5 +1,6 @@
//! ref: composer/src/Composer/Repository/Vcs/VcsDriver.php
+use chrono::{DateTime, Utc};
use indexmap::IndexMap;
use shirabe_external_packages::composer::pcre::preg::Preg;
use shirabe_php_shim::{
@@ -23,9 +24,9 @@ pub struct VcsDriverBase {
pub origin_url: String,
pub repo_config: IndexMap<String, PhpMixed>,
pub io: Box<dyn IOInterface>,
- pub config: Config,
- pub process: ProcessExecutor,
- pub http_downloader: HttpDownloader,
+ pub config: std::rc::Rc<std::cell::RefCell<Config>>,
+ pub process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
+ pub http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
pub info_cache: IndexMap<String, Option<IndexMap<String, PhpMixed>>>,
pub cache: Option<Cache>,
}
@@ -34,9 +35,9 @@ impl VcsDriverBase {
pub fn new(
repo_config: IndexMap<String, PhpMixed>,
io: Box<dyn IOInterface>,
- config: Config,
- http_downloader: HttpDownloader,
- process: ProcessExecutor,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
+ http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
+ process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
) -> Self {
let url = repo_config
.get("url")
@@ -74,7 +75,52 @@ impl VcsDriverBase {
.get("options")
.cloned()
.unwrap_or(PhpMixed::Array(IndexMap::new()));
- self.http_downloader.get(url, &options)
+ self.http_downloader.borrow_mut().get(url, &options)
+ }
+
+ // Helper for concrete drivers: produces the same value as the trait default
+ // `get_base_composer_information`, but receives a pre-fetched composer.json
+ // body and a lazy change-date callback. Concrete drivers in the Rust port
+ // wrap `VcsDriverBase` as `self.inner` instead of inheriting from it, so
+ // they cannot dispatch back into a base method that calls `get_file_content`
+ // / `get_change_date` hooks; the caller threads those calls in itself.
+ pub fn finish_base_composer_information(
+ identifier: &str,
+ composer_file_content: Option<String>,
+ change_date: impl FnOnce() -> anyhow::Result<Option<DateTime<Utc>>>,
+ ) -> anyhow::Result<Option<IndexMap<String, PhpMixed>>> {
+ let content = match composer_file_content {
+ None => return Ok(None),
+ Some(c) if c.is_empty() => return Ok(None),
+ Some(c) => c,
+ };
+
+ let parsed = JsonFile::parse_json(
+ Some(&content),
+ Some(&format!("{}:composer.json", identifier)),
+ )?;
+
+ let array = match parsed {
+ PhpMixed::Array(a) if !a.is_empty() => a,
+ _ => return Ok(None),
+ };
+
+ // PHP arrays own their nested values; the Rust representation wraps them
+ // in Box<PhpMixed>. Unbox the outer level so callers can mutate keys.
+ let mut composer: IndexMap<String, PhpMixed> =
+ array.into_iter().map(|(k, v)| (k, *v)).collect();
+
+ if !composer.contains_key("time")
+ || composer
+ .get("time")
+ .map_or(true, |v| v.as_string().map_or(true, |s| s.is_empty()))
+ {
+ if let Some(d) = change_date()? {
+ composer.insert("time".to_string(), PhpMixed::String(d.to_rfc3339()));
+ }
+ }
+
+ Ok(Some(composer))
}
}
@@ -93,8 +139,7 @@ pub trait VcsDriver: VcsDriverInterface {
fn config_mut(&mut self) -> &mut Config;
fn process(&self) -> &ProcessExecutor;
fn process_mut(&mut self) -> &mut ProcessExecutor;
- fn http_downloader(&self) -> &HttpDownloader;
- fn http_downloader_mut(&mut self) -> &mut HttpDownloader;
+ fn http_downloader(&self) -> &std::rc::Rc<std::cell::RefCell<HttpDownloader>>;
fn info_cache(&self) -> &IndexMap<String, Option<IndexMap<String, PhpMixed>>>;
fn info_cache_mut(&mut self) -> &mut IndexMap<String, Option<IndexMap<String, PhpMixed>>>;
fn cache(&self) -> Option<&Cache>;
@@ -195,7 +240,7 @@ pub trait VcsDriver: VcsDriverInterface {
.get("options")
.cloned()
.unwrap_or(PhpMixed::Array(IndexMap::new()));
- self.http_downloader().get(url, &options)
+ self.http_downloader().borrow_mut().get(url, &options)
}
fn cleanup(&self) {}
diff --git a/crates/shirabe/src/repository/vcs/vcs_driver_interface.rs b/crates/shirabe/src/repository/vcs/vcs_driver_interface.rs
index 5b15c2c..236b9b4 100644
--- a/crates/shirabe/src/repository/vcs/vcs_driver_interface.rs
+++ b/crates/shirabe/src/repository/vcs/vcs_driver_interface.rs
@@ -6,7 +6,7 @@ use chrono::{DateTime, Utc};
use indexmap::IndexMap;
use shirabe_php_shim::PhpMixed;
-pub trait VcsDriverInterface {
+pub trait VcsDriverInterface: std::fmt::Debug {
fn initialize(&mut self) -> anyhow::Result<()>;
fn get_composer_information(
diff --git a/crates/shirabe/src/repository/vcs_repository.rs b/crates/shirabe/src/repository/vcs_repository.rs
index e53392f..1a511fd 100644
--- a/crates/shirabe/src/repository/vcs_repository.rs
+++ b/crates/shirabe/src/repository/vcs_repository.rs
@@ -45,7 +45,7 @@ pub struct VcsRepository {
/// @var IOInterface
pub(crate) io: Box<dyn IOInterface>,
/// @var Config
- pub(crate) config: Config,
+ pub(crate) config: std::rc::Rc<std::cell::RefCell<Config>>,
/// @var VersionParser
pub(crate) version_parser: Option<VersionParser>,
/// @var string
@@ -55,9 +55,9 @@ pub struct VcsRepository {
/// @var array<string, mixed>
pub(crate) repo_config: IndexMap<String, PhpMixed>,
/// @var HttpDownloader
- pub(crate) http_downloader: HttpDownloader,
+ pub(crate) http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
/// @var ProcessExecutor
- pub(crate) process_executor: ProcessExecutor,
+ pub(crate) process_executor: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
/// @var bool
pub(crate) branch_error_occurred: bool,
/// @var array<string, class-string<VcsDriverInterface>>
@@ -86,10 +86,10 @@ impl VcsRepository {
pub fn new(
mut repo_config: IndexMap<String, PhpMixed>,
io: Box<dyn IOInterface>,
- config: Config,
- http_downloader: HttpDownloader,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
+ http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
dispatcher: Option<EventDispatcher>,
- process: Option<ProcessExecutor>,
+ process: Option<std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>,
drivers: Option<IndexMap<String, String>>,
version_cache: Option<Box<dyn VersionCacheInterface>>,
) -> Result<Self> {
@@ -154,8 +154,11 @@ impl VcsRepository {
.to_string();
let is_verbose = io.is_verbose();
let is_very_verbose = io.is_very_verbose();
- let process_executor =
- process.unwrap_or_else(|| ProcessExecutor::new(Some(Box::new(&*io)), None));
+ let process_executor = process.unwrap_or_else(|| {
+ std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(Some(
+ Box::new(&*io),
+ ))))
+ });
Ok(Self {
inner,
@@ -477,7 +480,7 @@ impl VcsRepository {
// broken package, version doesn't match tag
if version_normalized != parsed_tag {
if is_very_verbose {
- if Preg::is_match(r"{(^dev-|[.-]?dev$)}i", &parsed_tag) {
+ if Preg::is_match(r"{(^dev-|[.-]?dev$)}i", &parsed_tag).unwrap_or(false) {
self.io.write_error(&format!(
"<warning>Skipped tag {}, invalid tag name, tags can not use dev prefixes or suffixes</warning>",
tag
@@ -706,7 +709,7 @@ impl VcsRepository {
}
// TODO(phase-b): Box<dyn BasePackage> -> Box<dyn PackageInterface> coercion
self.inner.add_package(
- crate::package::package_interface::PackageInterface::clone_box(&*package),
+ <dyn crate::package::package_interface::PackageInterface>::clone_box(&*package),
)?;
Ok(())
})();
diff --git a/crates/shirabe/src/repository/version_cache_interface.rs b/crates/shirabe/src/repository/version_cache_interface.rs
index ce0b591..6dfeec0 100644
--- a/crates/shirabe/src/repository/version_cache_interface.rs
+++ b/crates/shirabe/src/repository/version_cache_interface.rs
@@ -1,6 +1,6 @@
//! ref: composer/src/Composer/Repository/VersionCacheInterface.php
-pub trait VersionCacheInterface {
+pub trait VersionCacheInterface: std::fmt::Debug {
// No class implementing this interface exists in Composer's codebase; a plugin may provide
// one, but plugin support is not yet decided. Using () as a placeholder until then.
fn get_version_package(&self, version: &str, identifier: &str) -> ();
diff --git a/crates/shirabe/src/repository/writable_array_repository.rs b/crates/shirabe/src/repository/writable_array_repository.rs
index 793478f..05c6bfa 100644
--- a/crates/shirabe/src/repository/writable_array_repository.rs
+++ b/crates/shirabe/src/repository/writable_array_repository.rs
@@ -2,7 +2,9 @@
use crate::installer::installation_manager::InstallationManager;
use crate::repository::array_repository::ArrayRepository;
+use crate::repository::repository_interface::RepositoryInterface;
use anyhow::Result;
+use shirabe_php_shim::Countable;
#[derive(Debug)]
pub struct WritableArrayRepository {
@@ -12,6 +14,16 @@ pub struct WritableArrayRepository {
}
impl WritableArrayRepository {
+ pub fn new(
+ packages: Vec<Box<dyn crate::package::package_interface::PackageInterface>>,
+ ) -> Result<Self> {
+ Ok(Self {
+ inner: ArrayRepository::new(packages)?,
+ dev_package_names: Vec::new(),
+ dev_mode: None,
+ })
+ }
+
/// Returns true if dev requirements were installed, false if --no-dev was used, None if yet unknown.
pub fn get_dev_mode(&self) -> Option<bool> {
self.dev_mode
@@ -72,4 +84,12 @@ impl WritableArrayRepository {
// TODO(phase-b): delegate to inner ArrayRepository::get_packages
Vec::new()
}
+
+ pub fn get_repo_name(&self) -> String {
+ self.inner.get_repo_name()
+ }
+
+ pub fn count(&self) -> i64 {
+ Countable::count(&self.inner)
+ }
}