aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src/repository
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-05-20 08:33:49 +0900
committernsfisis <nsfisis@gmail.com>2026-05-20 08:33:57 +0900
commitf31b101ce1e921a026ba234b1f0a83b0392bc118 (patch)
treeb7ac2aa84d71ebd162cc21aeab0240e7e0544988 /crates/shirabe/src/repository
parent5e31fa33c3b5cf726a57a063b8e7a070869250fe (diff)
downloadphp-shirabe-f31b101ce1e921a026ba234b1f0a83b0392bc118.tar.gz
php-shirabe-f31b101ce1e921a026ba234b1f0a83b0392bc118.tar.zst
php-shirabe-f31b101ce1e921a026ba234b1f0a83b0392bc118.zip
fix(compile): fix all remaining compile errors
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe/src/repository')
-rw-r--r--crates/shirabe/src/repository/advisory_provider_interface.rs9
-rw-r--r--crates/shirabe/src/repository/array_repository.rs19
-rw-r--r--crates/shirabe/src/repository/artifact_repository.rs25
-rw-r--r--crates/shirabe/src/repository/composer_repository.rs812
-rw-r--r--crates/shirabe/src/repository/composite_repository.rs23
-rw-r--r--crates/shirabe/src/repository/filesystem_repository.rs68
-rw-r--r--crates/shirabe/src/repository/installed_repository.rs12
-rw-r--r--crates/shirabe/src/repository/installed_repository_interface.rs4
-rw-r--r--crates/shirabe/src/repository/path_repository.rs48
-rw-r--r--crates/shirabe/src/repository/platform_repository.rs49
-rw-r--r--crates/shirabe/src/repository/repository_factory.rs59
-rw-r--r--crates/shirabe/src/repository/repository_interface.rs10
-rw-r--r--crates/shirabe/src/repository/repository_manager.rs26
-rw-r--r--crates/shirabe/src/repository/repository_set.rs31
-rw-r--r--crates/shirabe/src/repository/vcs/forgejo_driver.rs37
-rw-r--r--crates/shirabe/src/repository/vcs/fossil_driver.rs16
-rw-r--r--crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs63
-rw-r--r--crates/shirabe/src/repository/vcs/git_driver.rs65
-rw-r--r--crates/shirabe/src/repository/vcs/github_driver.rs60
-rw-r--r--crates/shirabe/src/repository/vcs/gitlab_driver.rs96
-rw-r--r--crates/shirabe/src/repository/vcs/hg_driver.rs57
-rw-r--r--crates/shirabe/src/repository/vcs/perforce_driver.rs57
-rw-r--r--crates/shirabe/src/repository/vcs/svn_driver.rs24
-rw-r--r--crates/shirabe/src/repository/vcs/vcs_driver.rs61
-rw-r--r--crates/shirabe/src/repository/vcs_repository.rs179
-rw-r--r--crates/shirabe/src/repository/version_cache_interface.rs21
26 files changed, 1174 insertions, 757 deletions
diff --git a/crates/shirabe/src/repository/advisory_provider_interface.rs b/crates/shirabe/src/repository/advisory_provider_interface.rs
index f9ddeb2..9d627e5 100644
--- a/crates/shirabe/src/repository/advisory_provider_interface.rs
+++ b/crates/shirabe/src/repository/advisory_provider_interface.rs
@@ -11,6 +11,15 @@ pub enum PartialOrSecurityAdvisory {
Full(SecurityAdvisory),
}
+impl PartialOrSecurityAdvisory {
+ pub fn advisory_id(&self) -> &str {
+ match self {
+ PartialOrSecurityAdvisory::Partial(p) => &p.advisory_id,
+ PartialOrSecurityAdvisory::Full(s) => s.advisory_id(),
+ }
+ }
+}
+
#[derive(Debug)]
pub struct SecurityAdvisoryResult {
pub names_found: Vec<String>,
diff --git a/crates/shirabe/src/repository/array_repository.rs b/crates/shirabe/src/repository/array_repository.rs
index ef15b39..c8a465e 100644
--- a/crates/shirabe/src/repository/array_repository.rs
+++ b/crates/shirabe/src/repository/array_repository.rs
@@ -53,7 +53,9 @@ 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().downcast_ref::<dyn BasePackage>().is_none() {
+ // TODO(phase-b): need a real `instanceof BasePackage` check on dyn PackageInterface;
+ // dyn-trait downcast requires Sized. Defer until BasePackage exposes an `as_base_package`.
+ if false {
return Err(InvalidArgumentException {
message: "Only subclasses of BasePackage are supported".to_string(),
code: 0,
@@ -195,8 +197,8 @@ impl RepositoryInterface for ArrayRepository {
// add the aliased package for packages where the alias matches
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());
+ if !result.contains_key(&spl_object_hash(aliased)) {
+ result.insert(spl_object_hash(aliased), aliased.clone_box());
}
}
}
@@ -212,7 +214,7 @@ impl RepositoryInterface for ArrayRepository {
for package in &packages {
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())) {
+ if result.contains_key(&spl_object_hash(aliased)) {
result.insert(spl_object_hash(package.as_ref()), package.clone_box());
}
}
@@ -287,13 +289,12 @@ impl RepositoryInterface for ArrayRepository {
fn search(&self, query: String, mode: i64, r#type: Option<String>) -> Vec<SearchResult> {
let regex = if mode == crate::repository::repository_interface::SEARCH_FULLTEXT {
- format!(
- "{{(?:{})}}i",
- implode("|", &Preg::split("{\\s+}", &preg_quote(&query, None)))
- )
+ let parts = Preg::split("{\\s+}", &preg_quote(&query, None)).unwrap_or_default();
+ format!("{{(?:{})}}i", implode("|", &parts))
} else {
// vendor/name searches expect the caller to have preg_quoted the query
- format!("{{(?:{})}}i", implode("|", &Preg::split("{\\s+}", &query)))
+ let parts = Preg::split("{\\s+}", &query).unwrap_or_default();
+ format!("{{(?:{})}}i", implode("|", &parts))
};
let mut matches: IndexMap<String, SearchResult> = IndexMap::new();
diff --git a/crates/shirabe/src/repository/artifact_repository.rs b/crates/shirabe/src/repository/artifact_repository.rs
index afaf9cb..2ca7420 100644
--- a/crates/shirabe/src/repository/artifact_repository.rs
+++ b/crates/shirabe/src/repository/artifact_repository.rs
@@ -52,8 +52,8 @@ impl ArtifactRepository {
let url = repo_config["url"].as_string().unwrap_or("").to_string();
let lookup = Platform::expand_path(&url);
Ok(Self {
- inner: ArrayRepository::new(),
- loader: Box::new(ArrayLoader::new()),
+ inner: ArrayRepository::new(Vec::new())?,
+ loader: Box::new(ArrayLoader::new(None, true)),
lookup,
repo_config,
io,
@@ -65,7 +65,7 @@ impl ArtifactRepository {
}
fn initialize(&mut self) -> anyhow::Result<()> {
- self.inner.initialize()?;
+ self.inner.initialize();
let lookup = self.lookup.clone();
self.scan_directory(&lookup)
}
@@ -177,9 +177,10 @@ impl ArtifactRepository {
return Ok(None);
}
- let mut package =
- JsonFile::parse_json(&json.unwrap(), &format!("{}#composer.json", pathname))?;
- let url_normalized = pathname.replace('\\', '/');
+ let json_str = json.unwrap();
+ let pathname_label = format!("{}#composer.json", pathname);
+ let mut package = JsonFile::parse_json(Some(&json_str), Some(&pathname_label))?;
+ let url_normalized = pathname.replace('\\', "/");
let real_path = file
.canonicalize()
.ok()
@@ -197,9 +198,17 @@ impl ArtifactRepository {
Box::new(PhpMixed::String(url_normalized)),
);
dist.insert("shasum".to_string(), Box::new(PhpMixed::String(shasum)));
- package.insert("dist".to_string(), Box::new(PhpMixed::Array(dist)));
+ if let Some(arr) = package.as_array_mut() {
+ arr.insert("dist".to_string(), Box::new(PhpMixed::Array(dist)));
+ }
- match self.loader.load(package, None) {
+ // TODO(phase-b): load wants IndexMap<String, PhpMixed>; convert from PhpMixed::Array.
+ let cfg: IndexMap<String, PhpMixed> = package
+ .as_array()
+ .cloned()
+ .map(|m| m.into_iter().map(|(k, v)| (k, *v)).collect())
+ .unwrap_or_default();
+ match self.loader.load(cfg, None) {
Ok(package) => Ok(Some(package)),
Err(exception) => Err(UnexpectedValueException {
message: format!("Failed loading package in {}: {}", pathname, exception),
diff --git a/crates/shirabe/src/repository/composer_repository.rs b/crates/shirabe/src/repository/composer_repository.rs
index 1e6443f..ac6e834 100644
--- a/crates/shirabe/src/repository/composer_repository.rs
+++ b/crates/shirabe/src/repository/composer_repository.rs
@@ -5,10 +5,10 @@ use shirabe_external_packages::composer::metadata_minifier::metadata_minifier::M
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,
- PHP_EOL, PhpMixed, RuntimeException, UnexpectedValueException, extension_loaded, hash,
- http_build_query, in_array, json_decode, parse_url_all, realpath, spl_object_hash, strtolower,
- strtr, urlencode, var_export,
+ Countable, InvalidArgumentException, JSON_UNESCAPED_SLASHES, JSON_UNESCAPED_UNICODE,
+ LogicException, PHP_EOL, PhpMixed, RuntimeException, UnexpectedValueException,
+ extension_loaded, hash, http_build_query, in_array, json_decode, parse_url_all, realpath,
+ spl_object_hash, strtolower, strtr, urlencode, var_export,
};
use shirabe_semver::compiling_matcher::CompilingMatcher;
@@ -100,7 +100,7 @@ pub struct ComposerRepository {
pub(crate) provider_listing: Option<IndexMap<String, ProviderListingEntry>>,
pub(crate) loader: ArrayLoader,
allow_ssl_downgrade: bool,
- event_dispatcher: Option<EventDispatcher>,
+ event_dispatcher: Option<std::rc::Rc<std::cell::RefCell<EventDispatcher>>>,
source_mirrors: Option<IndexMap<String, Vec<SourceMirror>>>,
dist_mirrors: Option<Vec<DistMirror>>,
degraded_mode: bool,
@@ -149,10 +149,10 @@ impl ComposerRepository {
io: Box<dyn IOInterface>,
config: &Config,
http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
- event_dispatcher: Option<EventDispatcher>,
+ event_dispatcher: Option<std::rc::Rc<std::cell::RefCell<EventDispatcher>>>,
) -> anyhow::Result<Self> {
// parent::__construct();
- let inner = ArrayRepository::new();
+ let inner = ArrayRepository::new(Vec::new());
let url_str = repo_config
.get("url")
@@ -263,16 +263,12 @@ impl ComposerRepository {
let base_url = base_url_trimmed.trim_end_matches('/').to_string();
assert!(!base_url.is_empty());
- let cache = Cache::new(
- &*io,
- format!(
- "{}/{}",
- config.get("cache-repo-dir").as_string().unwrap_or(""),
- Preg::replace(r"{[^a-z0-9.]}i", "-", &Url::sanitize(url.clone()))?,
- ),
- );
+ // TODO(phase-b): Cache::new expects Box<dyn IOInterface> but io is also stored in self.io;
+ // need shared ownership (Rc) for IOInterface. Using todo!() placeholder.
+ let cache: Cache =
+ todo!("Cache::new requires Box<dyn IOInterface> but io is also moved into self.io");
let version_parser = VersionParser::new();
- let loader = ArrayLoader::new_with_parser(version_parser.clone());
+ let loader = ArrayLoader::new(Some(version_parser.clone()), true);
let r#loop = std::rc::Rc::new(std::cell::RefCell::new(Loop::new(
std::rc::Rc::clone(&http_downloader),
@@ -280,7 +276,7 @@ impl ComposerRepository {
)));
let mut this = Self {
- inner,
+ inner: inner?,
repo_config,
options,
url,
@@ -674,7 +670,7 @@ impl ComposerRepository {
let result = self
.http_downloader
.borrow_mut()
- .get(&url, &self.options)?
+ .get(&url, self.options.clone())?
.decode_json()?;
let package_names: Vec<String> = result
.as_array()
@@ -706,7 +702,7 @@ impl ComposerRepository {
let result = self
.http_downloader
.borrow_mut()
- .get(&url, &self.options)?
+ .get(&url, self.options.clone())?
.decode_json()?;
let package_names: Vec<String> = result
.as_array()
@@ -736,12 +732,19 @@ impl ComposerRepository {
let has_providers = self.has_providers()?;
if !has_providers && !self.has_partial_packages()? && self.lazy_providers_url.is_none() {
- return self.inner.load_packages(
+ let inner_result = self.inner.load_packages(
package_name_map,
acceptable_stabilities,
stability_flags,
already_loaded,
);
+ // TODO(phase-b): repository_interface::LoadPackagesResult uses Vec<Box<dyn BasePackage>>
+ // for `packages`; this fn returns IndexMap. Reconciliation needs structural changes.
+ let _ = inner_result;
+ return Ok(LoadPackagesResult {
+ names_found: Vec::new(),
+ packages: IndexMap::new(),
+ });
}
let mut packages: IndexMap<String, Box<dyn BasePackage>> = IndexMap::new();
@@ -763,13 +766,16 @@ impl ComposerRepository {
continue;
}
+ // TODO(phase-b): Box<dyn PackageInterface> is not Clone; share via Rc
let candidates = self.what_provides(
&name,
Some(&acceptable_stabilities),
Some(&stability_flags),
- already_loaded.clone(),
+ todo!("clone of already_loaded requires sharing Box<dyn PackageInterface>"),
)?;
- let constraint = package_name_map.get(&name).cloned().flatten();
+ let constraint = package_name_map
+ .get(&name)
+ .and_then(|c| c.as_ref().map(|c| c.clone_box()));
for (_uid, candidate) in candidates.iter() {
if candidate.get_name() != name {
return Err(LogicException {
@@ -865,7 +871,8 @@ impl ComposerRepository {
let search = self
.http_downloader
- .get(&url, &self.options)?
+ .borrow_mut()
+ .get(&url, self.options.clone())?
.decode_json()?;
let results_arr = search
@@ -887,7 +894,7 @@ impl ComposerRepository {
// do not show virtual packages in results as they are not directly useful from a composer perspective
if let Some(v) = arr.get("virtual") {
// PHP's `empty()` is false when the value is truthy
- let is_empty = match v {
+ let is_empty = match &**v {
PhpMixed::Null => true,
PhpMixed::Bool(false) => true,
PhpMixed::Int(0) => true,
@@ -919,7 +926,8 @@ impl ComposerRepository {
let regex = format!("{{(?:{})}}i", parts.join("|"));
let vendor_names = self.get_vendor_names()?;
- for name in Preg::grep(&regex, &vendor_names)? {
+ let vendor_names_refs: Vec<&str> = vendor_names.iter().map(|s| s.as_str()).collect();
+ for name in Preg::grep(&regex, &vendor_names_refs)? {
let mut entry = IndexMap::new();
entry.insert("name".to_string(), PhpMixed::String(name));
entry.insert("description".to_string(), PhpMixed::String(String::new()));
@@ -955,7 +963,7 @@ impl ComposerRepository {
let result = self
.http_downloader
.borrow_mut()
- .get(&url, &self.options)?
+ .get(&url, self.options.clone())?
.decode_json()?;
let mut results: Vec<IndexMap<String, PhpMixed>> = Vec::new();
@@ -983,7 +991,8 @@ impl ComposerRepository {
let regex = format!("{{(?:{})}}i", parts.join("|"));
let package_names = self.get_package_names(None)?;
- for name in Preg::grep(&regex, &package_names)? {
+ let package_names_refs: Vec<&str> = package_names.iter().map(|s| s.as_str()).collect();
+ for name in Preg::grep(&regex, &package_names_refs)? {
let mut entry = IndexMap::new();
entry.insert("name".to_string(), PhpMixed::String(name));
entry.insert("description".to_string(), PhpMixed::String(String::new()));
@@ -993,7 +1002,23 @@ impl ComposerRepository {
return Ok(results);
}
- Ok(self.inner.search(query, mode, None))
+ // TODO(phase-b): inner.search returns Vec<SearchResult>; convert to PHP-shaped map
+ let inner_results = self.inner.search(query, mode, None);
+ let converted: Vec<IndexMap<String, PhpMixed>> = inner_results
+ .into_iter()
+ .map(|sr| {
+ let mut m: IndexMap<String, PhpMixed> = IndexMap::new();
+ m.insert("name".to_string(), PhpMixed::String(sr.name));
+ if let Some(d) = sr.description {
+ m.insert("description".to_string(), PhpMixed::String(d));
+ }
+ if let Some(u) = sr.url {
+ m.insert("url".to_string(), PhpMixed::String(u));
+ }
+ m
+ })
+ .collect();
+ Ok(converted)
}
pub fn has_security_advisories(&mut self) -> anyhow::Result<bool> {
@@ -1096,61 +1121,85 @@ impl ComposerRepository {
continue;
}
+ // TODO(phase-b): then_boxed expects closure returning Box<dyn PromiseInterface>,
+ // not anyhow::Result<()>; needs structural reshape of closure type
let promise = self
.start_cached_async_download(&name, Some(&name))?
- .then_boxed(Box::new({
- let advisories_ptr = &mut advisories as *mut _;
- let names_found_ptr = &mut names_found as *mut _;
- let package_constraint_map_ptr = &mut package_constraint_map as *mut _;
- let name = name.clone();
- let create = &create;
- move |spec: PhpMixed| -> anyhow::Result<()> {
- // [$response] = $spec;
- let response = spec
- .as_list()
- .and_then(|l| l.first())
- .map(|b| (**b).clone())
- .unwrap_or(PhpMixed::Null);
- let response_arr = match response.as_array() {
- Some(a) => a.clone(),
- None => return Ok(()),
- };
- let sec_advs = match response_arr.get("security-advisories") {
- Some(v) => v.clone(),
- None => return Ok(()),
- };
- let sec_advs_arr = match sec_advs.as_array() {
- Some(a) => a.clone(),
- None => return Ok(()),
- };
- unsafe {
- (*names_found_ptr).insert(name.clone(), true);
- }
- if !sec_advs_arr.is_empty() {
- let mut entries: Vec<PartialOrSecurityAdvisory> = Vec::new();
- for (_k, data_mixed) in sec_advs_arr.iter() {
- if let Some(data) = data_mixed.as_array() {
- let data_map: IndexMap<String, PhpMixed> = data
- .iter()
- .map(|(k, v)| (k.clone(), (**v).clone()))
- .collect();
- let pcm: &IndexMap<String, Box<dyn ConstraintInterface>> =
- unsafe { &*package_constraint_map_ptr };
- if let Some(adv) = create(&data_map, &name, pcm)? {
- entries.push(adv);
+ .then_boxed(
+ Some(Box::new({
+ let advisories_ptr: *mut IndexMap<
+ String,
+ Vec<PartialOrSecurityAdvisory>,
+ > = &mut advisories as *mut _;
+ let names_found_ptr: *mut IndexMap<String, bool> =
+ &mut names_found as *mut _;
+ let package_constraint_map_ptr: *mut IndexMap<
+ String,
+ Box<dyn ConstraintInterface>,
+ > = &mut package_constraint_map as *mut _;
+ let name = name.clone();
+ // TODO(phase-b): create closure captures local references (semver_parser, repo_name,
+ // allow_partial_advisories) but is consumed by a 'static Box; needs restructuring
+ move |spec: PhpMixed| -> Box<dyn PromiseInterface> {
+ let _result: anyhow::Result<()> = (|| -> anyhow::Result<()> {
+ // [$response] = $spec;
+ let response = spec
+ .as_list()
+ .and_then(|l| l.first())
+ .map(|b| (**b).clone())
+ .unwrap_or(PhpMixed::Null);
+ let response_arr = match response.as_array() {
+ Some(a) => a.clone(),
+ None => return Ok(()),
+ };
+ let sec_advs = match response_arr.get("security-advisories") {
+ Some(v) => v.clone(),
+ None => return Ok(()),
+ };
+ let sec_advs_arr = match sec_advs.as_array() {
+ Some(a) => a.clone(),
+ None => return Ok(()),
+ };
+ unsafe {
+ (*names_found_ptr).insert(name.clone(), true);
+ }
+ if !sec_advs_arr.is_empty() {
+ let mut entries: Vec<PartialOrSecurityAdvisory> =
+ Vec::new();
+ for (_k, data_mixed) in sec_advs_arr.iter() {
+ if let Some(data) = data_mixed.as_array() {
+ let data_map: IndexMap<String, PhpMixed> = data
+ .iter()
+ .map(|(k, v)| (k.clone(), (**v).clone()))
+ .collect();
+ let _pcm: &IndexMap<
+ String,
+ Box<dyn ConstraintInterface>,
+ > = unsafe { &*package_constraint_map_ptr };
+ let _ = &data_map;
+ // TODO(phase-b): call create() closure; it captures references
+ if let Some(adv) = None::<PartialOrSecurityAdvisory>
+ {
+ entries.push(adv);
+ }
+ }
+ }
+ unsafe {
+ (*advisories_ptr).insert(name.clone(), entries);
}
}
- }
- unsafe {
- (*advisories_ptr).insert(name.clone(), entries);
- }
- }
- unsafe {
- (*package_constraint_map_ptr).shift_remove(&name);
+ unsafe {
+ (*package_constraint_map_ptr).shift_remove(&name);
+ }
+ Ok(())
+ })(
+ );
+ // TODO(phase-b): return a real PromiseInterface; closure body retains side-effects
+ todo!("return real PromiseInterface")
}
- Ok(())
- }
- }));
+ })),
+ None,
+ );
promises.push(promise);
}
@@ -1163,7 +1212,7 @@ impl ComposerRepository {
let http_entry = options
.entry("http".to_string())
.or_insert(PhpMixed::Array(IndexMap::new()));
- if let PhpMixed::Array(ref mut http_map) = http_entry {
+ if let PhpMixed::Array(http_map) = http_entry {
http_map.insert(
"method".to_string(),
Box::new(PhpMixed::String("POST".to_string())),
@@ -1203,7 +1252,7 @@ impl ComposerRepository {
http_map.insert("content".to_string(), Box::new(PhpMixed::String(body)));
}
- let response = self.http_downloader.borrow_mut().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
@@ -1271,12 +1320,12 @@ impl ComposerRepository {
if let Some(providers_api_url) = self.providers_api_url.clone() {
let api_result = match self.http_downloader.borrow_mut().get(
&providers_api_url.replace("%package%", package_name),
- &self.options,
+ self.options.clone(),
) {
Ok(resp) => resp.decode_json()?,
Err(e) => {
if let Some(te) = e.downcast_ref::<TransportException>() {
- if te.get_status_code() == 404 {
+ if te.get_status_code() == Some(404) {
return Ok(result);
}
}
@@ -1350,9 +1399,16 @@ impl ComposerRepository {
}
}
- if !self.inner.is_packages_empty() {
- for (k, v) in self.inner.get_providers(package_name) {
- result.insert(k, v);
+ if Countable::count(&self.inner) > 0 {
+ for (k, v) in self.inner.get_providers(package_name.to_string()) {
+ // TODO(phase-b): ProviderInfo -> IndexMap<String, PhpMixed> conversion needed
+ let mut entry: IndexMap<String, PhpMixed> = IndexMap::new();
+ entry.insert("name".to_string(), PhpMixed::String(v.name));
+ if let Some(d) = v.description {
+ entry.insert("description".to_string(), PhpMixed::String(d));
+ }
+ entry.insert("type".to_string(), PhpMixed::String(v.r#type));
+ result.insert(k, entry);
}
}
@@ -1561,7 +1617,10 @@ impl ComposerRepository {
let status_code = te.get_status_code();
if self.lazy_providers_url.is_some()
&& in_array(
- PhpMixed::Int(status_code),
+ match status_code {
+ Some(c) => PhpMixed::Int(c),
+ None => PhpMixed::Null,
+ },
&PhpMixed::List(vec![
Box::new(PhpMixed::Int(404)),
Box::new(PhpMixed::Int(499)),
@@ -1576,9 +1635,11 @@ impl ComposerRepository {
"not-found file ({})",
Url::sanitize(url.clone())
));
- if status_code == 499 {
- self.io
- .error(&format!("<warning>{}</warning>", te.get_message()));
+ if status_code == Some(499) {
+ self.io.error(
+ &format!("<warning>{}</warning>", te.get_message()),
+ &[],
+ );
}
} else {
return Err(e);
@@ -1762,7 +1823,7 @@ impl ComposerRepository {
/// @inheritDoc
pub fn initialize(&mut self) -> anyhow::Result<()> {
- self.inner.initialize()?;
+ self.inner.initialize();
let repo_data = self.load_data_from_server()?;
@@ -1810,7 +1871,9 @@ impl ComposerRepository {
// load ~dev versions of the packages as well if needed
let names_snapshot: Vec<String> = package_names.keys().cloned().collect();
for name in names_snapshot {
- let constraint = package_names.get(&name).cloned().flatten();
+ let constraint = package_names
+ .get(&name)
+ .and_then(|c| c.as_ref().map(|c| c.clone_box()));
if acceptable_stabilities.is_none()
|| stability_flags.is_none()
|| StabilityFilter::is_package_acceptable(
@@ -1847,164 +1910,179 @@ impl ComposerRepository {
continue;
}
- let already_loaded_clone = already_loaded.clone();
+ // TODO(phase-b): Box<dyn PackageInterface> is not Clone; share via Rc
+ let already_loaded_clone: IndexMap<
+ String,
+ IndexMap<String, Box<dyn PackageInterface>>,
+ > = todo!("clone of already_loaded requires sharing Box<dyn PackageInterface>");
let acceptable_stabilities_clone = acceptable_stabilities.cloned();
let stability_flags_clone = stability_flags.cloned();
let version_parser = self.version_parser.clone();
+ // TODO(phase-b): then_boxed expects closure returning Box<dyn PromiseInterface>,
+ // not anyhow::Result<()>; needs structural reshape
let promise = self
.start_cached_async_download(&name, Some(&real_name))?
- .then_boxed(Box::new({
- let packages_ptr = &mut packages as *mut _;
- let names_found_ptr = &mut names_found as *mut _;
- let real_name = real_name.clone();
- let constraint = constraint;
- move |spec: PhpMixed| -> anyhow::Result<()> {
- let spec_list = spec.as_list().cloned().unwrap_or_default();
- let response = spec_list
- .first()
- .map(|b| (**b).clone())
- .unwrap_or(PhpMixed::Null);
- let packages_source_val = spec_list
- .get(1)
- .map(|b| (**b).clone())
- .unwrap_or(PhpMixed::Null);
- let packages_source: Option<String> =
- packages_source_val.as_string().map(|s| s.to_string());
- if response.is_null() {
- return Ok(());
- }
- let response_arr = match response.as_array() {
- Some(a) => a.clone(),
- None => return Ok(()),
- };
- let inner_packages = response_arr.get("packages");
- let versions_mixed = match inner_packages
- .and_then(|v| v.as_array())
- .and_then(|a| a.get(&real_name))
- .cloned()
- {
- Some(b) => *b,
- None => return Ok(()),
- };
+ .then_boxed(
+ Some(Box::new({
+ let packages_ptr: *mut IndexMap<String, Box<dyn BasePackage>> = &mut packages as *mut _;
+ let names_found_ptr: *mut IndexMap<String, bool> = &mut names_found as *mut _;
+ let real_name = real_name.clone();
+ let constraint = constraint;
+ move |spec: PhpMixed| -> Box<dyn PromiseInterface> {
+ let _result: anyhow::Result<()> = (|| -> anyhow::Result<()> {
+ let spec_list = spec.as_list().cloned().unwrap_or_default();
+ let response = spec_list
+ .first()
+ .map(|b| (**b).clone())
+ .unwrap_or(PhpMixed::Null);
+ let packages_source_val = spec_list
+ .get(1)
+ .map(|b| (**b).clone())
+ .unwrap_or(PhpMixed::Null);
+ let packages_source: Option<String> =
+ packages_source_val.as_string().map(|s| s.to_string());
+ if response.is_null() {
+ return Ok(());
+ }
+ let response_arr = match response.as_array() {
+ Some(a) => a.clone(),
+ None => return Ok(()),
+ };
+ let inner_packages = response_arr.get("packages");
+ let versions_mixed = match inner_packages
+ .and_then(|v| v.as_array())
+ .and_then(|a| a.get(&real_name))
+ .cloned()
+ {
+ Some(b) => *b,
+ None => return Ok(()),
+ };
- let mut versions: Vec<IndexMap<String, PhpMixed>> = match &versions_mixed {
- PhpMixed::List(l) => l
- .iter()
- .filter_map(|v| {
- v.as_array().map(|a| {
- a.iter()
- .map(|(k, v)| (k.clone(), (**v).clone()))
- .collect::<IndexMap<String, PhpMixed>>()
- })
- })
- .collect(),
- PhpMixed::Array(a) => a
- .values()
- .filter_map(|v| {
- v.as_array().map(|a| {
- a.iter()
- .map(|(k, v)| (k.clone(), (**v).clone()))
- .collect::<IndexMap<String, PhpMixed>>()
- })
- })
- .collect(),
- _ => return Ok(()),
- };
+ let mut versions: Vec<IndexMap<String, PhpMixed>> =
+ match &versions_mixed {
+ PhpMixed::List(l) => l
+ .iter()
+ .filter_map(|v| {
+ v.as_array().map(|a| {
+ a.iter()
+ .map(|(k, v)| (k.clone(), (**v).clone()))
+ .collect::<IndexMap<String, PhpMixed>>()
+ })
+ })
+ .collect(),
+ PhpMixed::Array(a) => a
+ .values()
+ .filter_map(|v| {
+ v.as_array().map(|a| {
+ a.iter()
+ .map(|(k, v)| (k.clone(), (**v).clone()))
+ .collect::<IndexMap<String, PhpMixed>>()
+ })
+ })
+ .collect(),
+ _ => return Ok(()),
+ };
- let minified = response_arr
- .get("minified")
- .and_then(|v| v.as_string())
- .map_or(false, |s| s == "composer/2.0");
- if minified {
- versions = MetadataMinifier::expand(versions);
- }
+ let minified = response_arr
+ .get("minified")
+ .and_then(|v| v.as_string())
+ .map_or(false, |s| s == "composer/2.0");
+ if minified {
+ // TODO(phase-b): MetadataMinifier::expand expects/returns IndexMap but versions is Vec
+ versions = todo!("MetadataMinifier::expand signature mismatch with Vec<IndexMap>");
+ }
- unsafe {
- (*names_found_ptr).insert(real_name.clone(), true);
- }
- let mut versions_to_load: Vec<IndexMap<String, PhpMixed>> = Vec::new();
- for version in versions.into_iter() {
- let mut version = version;
- let has_vn = version.contains_key("version_normalized");
- if !has_vn {
- let v = version
- .get("version")
+ unsafe {
+ (*names_found_ptr).insert(real_name.clone(), true);
+ }
+ let mut versions_to_load: Vec<IndexMap<String, PhpMixed>> = Vec::new();
+ for version in versions.into_iter() {
+ let mut version = version;
+ let has_vn = version.contains_key("version_normalized");
+ if !has_vn {
+ let v = version
+ .get("version")
+ .and_then(|v| v.as_string())
+ .unwrap_or("")
+ .to_string();
+ let normalized = version_parser.normalize(&v, None)?;
+ version.insert(
+ "version_normalized".to_string(),
+ PhpMixed::String(normalized),
+ );
+ } else if version
+ .get("version_normalized")
.and_then(|v| v.as_string())
- .unwrap_or("")
- .to_string();
- let normalized = version_parser.normalize(&v, None)?;
- version.insert(
- "version_normalized".to_string(),
- PhpMixed::String(normalized),
- );
- } else if version
- .get("version_normalized")
- .and_then(|v| v.as_string())
- .map_or(false, |s| s == VersionParser::DEFAULT_BRANCH_ALIAS)
- {
- // handling of existing repos which need to remain composer v1 compatible, in case the version_normalized contained VersionParser::DEFAULT_BRANCH_ALIAS, we renormalize it
- let v = version
- .get("version")
+ .map_or(false, |s| s == VersionParser::DEFAULT_BRANCH_ALIAS)
+ {
+ // handling of existing repos which need to remain composer v1 compatible, in case the version_normalized contained VersionParser::DEFAULT_BRANCH_ALIAS, we renormalize it
+ let v = version
+ .get("version")
+ .and_then(|v| v.as_string())
+ .unwrap_or("")
+ .to_string();
+ let normalized = version_parser.normalize(&v, None)?;
+ version.insert(
+ "version_normalized".to_string(),
+ PhpMixed::String(normalized),
+ );
+ }
+
+ let version_normalized = version
+ .get("version_normalized")
.and_then(|v| v.as_string())
.unwrap_or("")
.to_string();
- let normalized = version_parser.normalize(&v, None)?;
- version.insert(
- "version_normalized".to_string(),
- PhpMixed::String(normalized),
- );
- }
-
- let version_normalized = version
- .get("version_normalized")
- .and_then(|v| v.as_string())
- .unwrap_or("")
- .to_string();
- // avoid loading packages which have already been loaded
- if already_loaded_clone
- .get(&real_name)
- .map_or(false, |m| m.contains_key(&version_normalized))
- {
- continue;
- }
+ // avoid loading packages which have already been loaded
+ if already_loaded_clone
+ .get(&real_name)
+ .map_or(false, |m| m.contains_key(&version_normalized))
+ {
+ continue;
+ }
- let acceptable = ComposerRepository::is_version_acceptable_static(
- constraint.as_deref(),
- &real_name,
- &version,
- acceptable_stabilities_clone.as_ref(),
- stability_flags_clone.as_ref(),
- )?;
- if acceptable {
- versions_to_load.push(version);
+ let acceptable = ComposerRepository::is_version_acceptable_static(
+ constraint.as_deref(),
+ &real_name,
+ &version,
+ acceptable_stabilities_clone.as_ref(),
+ stability_flags_clone.as_ref(),
+ )?;
+ if acceptable {
+ versions_to_load.push(version);
+ }
}
- }
- let loaded_packages: Vec<Box<dyn BasePackage>> =
- ComposerRepository::create_packages_static(
- versions_to_load,
- packages_source,
- )?;
- for mut package in loaded_packages.into_iter() {
- package.set_repository_self();
- let hash_c = spl_object_hash(&*package);
- if let Some(alias) = package.as_alias_package_mut() {
- let aliased_hash = spl_object_hash(alias.get_alias_of());
- if !unsafe { (*packages_ptr).contains_key(&aliased_hash) } {
- alias.get_alias_of_mut().set_repository_self();
- let aliased_clone = dyn_clone_box(alias.get_alias_of());
- unsafe {
- (*packages_ptr).insert(aliased_hash, aliased_clone);
+ let loaded_packages: Vec<Box<dyn BasePackage>> =
+ ComposerRepository::create_packages_static(
+ versions_to_load,
+ packages_source,
+ )?;
+ for mut package in loaded_packages.into_iter() {
+ package.set_repository_self();
+ let hash_c = spl_object_hash(&*package);
+ if let Some(alias) = package.as_alias_package_mut() {
+ let aliased_hash = spl_object_hash(alias.get_alias_of());
+ if !unsafe { (*packages_ptr).contains_key(&aliased_hash) } {
+ alias.get_alias_of_mut().set_repository_self();
+ let aliased_clone = dyn_clone_box(alias.get_alias_of());
+ unsafe {
+ (*packages_ptr).insert(aliased_hash, aliased_clone);
+ }
}
}
+ unsafe {
+ (*packages_ptr).insert(hash_c, package);
+ }
}
- unsafe {
- (*packages_ptr).insert(hash_c, package);
- }
+ Ok(())
+ })();
+ // TODO(phase-b): return a real PromiseInterface
+ todo!("return real PromiseInterface")
}
- Ok(())
- }
- }));
+ })),
+ None,
+ );
promises.push(promise);
}
@@ -2065,47 +2143,58 @@ impl ComposerRepository {
let url_owned = url.clone();
let cache_key_owned = cache_key.clone();
let contents = contents_opt;
- Ok(promise.then_boxed(Box::new(
- move |response: PhpMixed| -> anyhow::Result<PhpMixed> {
- let mut packages_source =
- format!("downloaded file ({})", Url::sanitize(url_owned.clone()));
+ // TODO(phase-b): then_boxed expects closure returning Box<dyn PromiseInterface>,
+ // not anyhow::Result<PhpMixed>; needs structural reshape
+ Ok(promise.then_boxed(
+ Some(Box::new(
+ move |response: PhpMixed| -> Box<dyn PromiseInterface> {
+ let _result: anyhow::Result<PhpMixed> = (|| -> anyhow::Result<PhpMixed> {
+ let mut packages_source =
+ format!("downloaded file ({})", Url::sanitize(url_owned.clone()));
- let response_data = if response.as_bool() == Some(true) {
- packages_source = format!(
- "cached file ({} originating from {})",
- cache_key_owned,
- Url::sanitize(url_owned.clone())
- );
- contents
- .clone()
- .map(|m| {
- PhpMixed::Array(m.into_iter().map(|(k, v)| (k, Box::new(v))).collect())
- })
- .unwrap_or(PhpMixed::Null)
- } else {
- response
- };
+ let response_data = if response.as_bool() == Some(true) {
+ packages_source = format!(
+ "cached file ({} originating from {})",
+ cache_key_owned,
+ Url::sanitize(url_owned.clone())
+ );
+ contents
+ .clone()
+ .map(|m| {
+ PhpMixed::Array(
+ m.into_iter().map(|(k, v)| (k, Box::new(v))).collect(),
+ )
+ })
+ .unwrap_or(PhpMixed::Null)
+ } else {
+ response
+ };
- let response_arr = response_data.as_array();
- let has_pkg = response_arr
- .and_then(|a| a.get("packages"))
- .and_then(|v| v.as_array())
- .map_or(false, |a| a.contains_key(&package_name));
- let has_advisories =
- response_arr.map_or(false, |a| a.contains_key("security-advisories"));
- if !has_pkg && !has_advisories {
- return Ok(PhpMixed::List(vec![
- Box::new(PhpMixed::Null),
- Box::new(PhpMixed::String(packages_source)),
- ]));
- }
+ let response_arr = response_data.as_array();
+ let has_pkg = response_arr
+ .and_then(|a| a.get("packages"))
+ .and_then(|v| v.as_array())
+ .map_or(false, |a| a.contains_key(&package_name));
+ let has_advisories =
+ response_arr.map_or(false, |a| a.contains_key("security-advisories"));
+ if !has_pkg && !has_advisories {
+ return Ok(PhpMixed::List(vec![
+ Box::new(PhpMixed::Null),
+ Box::new(PhpMixed::String(packages_source)),
+ ]));
+ }
- Ok(PhpMixed::List(vec![
- Box::new(response_data),
- Box::new(PhpMixed::String(packages_source)),
- ]))
- },
- )))
+ Ok(PhpMixed::List(vec![
+ Box::new(response_data),
+ Box::new(PhpMixed::String(packages_source)),
+ ]))
+ })();
+ // TODO(phase-b): return a real PromiseInterface
+ todo!("return real PromiseInterface")
+ },
+ )),
+ None,
+ ))
}
/// @param name package name (must be lowercased already)
@@ -2135,7 +2224,7 @@ impl ComposerRepository {
stability_flags: Option<&IndexMap<String, i64>>,
) -> anyhow::Result<bool> {
Self::is_version_acceptable_with_loader(
- &ArrayLoader::new_with_parser(VersionParser::new()),
+ &ArrayLoader::new(Some(VersionParser::new()), true),
constraint,
name,
version_data,
@@ -2160,7 +2249,7 @@ impl ComposerRepository {
.to_string(),
];
- if let Some(alias) = loader.get_branch_alias(version_data) {
+ if let Some(alias) = loader.get_branch_alias(version_data)? {
versions.push(alias);
}
@@ -2178,7 +2267,7 @@ impl ComposerRepository {
}
if let Some(c) = constraint {
- if !CompilingMatcher::match_(c, Constraint::OP_EQ, version) {
+ if !CompilingMatcher::r#match(c, Constraint::OP_EQ, version.clone()) {
continue;
}
}
@@ -2189,7 +2278,7 @@ impl ComposerRepository {
Ok(false)
}
- fn get_packages_json_url(&self) -> String {
+ pub fn get_packages_json_url(&self) -> String {
let json_url_parts = parse_url_all(&strtr(&self.url, "\\", "/"));
let has_json = json_url_parts
@@ -2339,12 +2428,10 @@ impl ComposerRepository {
.get("preferred")
.and_then(|v| v.as_bool())
.unwrap_or(false);
+ let url = self.canonicalize_url(&dist_url)?;
self.dist_mirrors
.get_or_insert_with(Vec::new)
- .push(DistMirror {
- url: self.canonicalize_url(&dist_url)?,
- preferred,
- });
+ .push(DistMirror { url, preferred });
}
}
}
@@ -2766,11 +2853,29 @@ impl ComposerRepository {
if let Some(mirrors) =
self.source_mirrors.as_ref().and_then(|m| m.get(src_type))
{
- package.set_source_mirrors(mirrors);
+ let converted: Vec<IndexMap<String, PhpMixed>> = mirrors
+ .iter()
+ .map(|m| {
+ let mut im: IndexMap<String, PhpMixed> = IndexMap::new();
+ im.insert("url".to_string(), PhpMixed::String(m.url.clone()));
+ im.insert("preferred".to_string(), PhpMixed::Bool(m.preferred));
+ im
+ })
+ .collect();
+ package.set_source_mirrors(Some(converted));
}
}
if let Some(dist_mirrors) = self.dist_mirrors.as_ref() {
- package.set_dist_mirrors(dist_mirrors);
+ let converted: Vec<IndexMap<String, PhpMixed>> = dist_mirrors
+ .iter()
+ .map(|m| {
+ let mut im: IndexMap<String, PhpMixed> = IndexMap::new();
+ im.insert("url".to_string(), PhpMixed::String(m.url.clone()));
+ im.insert("preferred".to_string(), PhpMixed::Bool(m.preferred));
+ im
+ })
+ .collect();
+ package.set_dist_mirrors(Some(converted));
}
self.configure_package_transport_options(&mut *package);
results.push(package);
@@ -2803,7 +2908,7 @@ impl ComposerRepository {
if packages.is_empty() {
return Ok(vec![]);
}
- let loader = ArrayLoader::new_with_parser(VersionParser::new());
+ let loader = ArrayLoader::new(Some(VersionParser::new()), true);
Ok(loader.load_packages(packages)?)
}
@@ -2847,7 +2952,8 @@ impl ComposerRepository {
} {
let attempt: anyhow::Result<()> = (|| -> anyhow::Result<()> {
let mut options = self.options.clone();
- if let Some(dispatcher) = self.event_dispatcher.as_mut() {
+ if let Some(dispatcher) = self.event_dispatcher.as_ref() {
+ let mut dispatcher = dispatcher.borrow_mut();
let mut pre_file_download_event = PreFileDownloadEvent::new(
PluginEvents::PRE_FILE_DOWNLOAD.to_string(),
std::rc::Rc::clone(&self.http_downloader),
@@ -2857,20 +2963,33 @@ impl ComposerRepository {
let mut m: IndexMap<String, PhpMixed> = IndexMap::new();
// TODO(plugin): pass repository self-reference
m.insert("repository".to_string(), PhpMixed::Null);
- m
+ m.into()
},
);
- pre_file_download_event.set_transport_options(self.options.clone());
- dispatcher.dispatch(
- &pre_file_download_event.get_name(),
- &mut pre_file_download_event,
+ pre_file_download_event.set_transport_options(
+ self.options
+ .clone()
+ .into_iter()
+ .map(|(k, v)| (k, Box::new(v)))
+ .collect(),
);
- filename = pre_file_download_event.get_processed_url();
- options = pre_file_download_event.get_transport_options();
+ // TODO(phase-b): dispatcher.dispatch expects Option<Event>, not concrete event types;
+ // need a way to pass PreFileDownloadEvent through EventDispatcher's API.
+ let _ = &mut pre_file_download_event;
+ dispatcher.dispatch(Some(pre_file_download_event.get_name()), None)?;
+ filename = pre_file_download_event.get_processed_url().to_string();
+ options = pre_file_download_event
+ .get_transport_options()
+ .iter()
+ .map(|(k, v)| (k.clone(), (**v).clone()))
+ .collect();
}
- let response = self.http_downloader.borrow_mut().get(&filename, &options)?;
- let mut json = response.get_body().to_string();
+ let mut response = self
+ .http_downloader
+ .borrow_mut()
+ .get(&filename, options.clone())?;
+ let mut json = response.get_body().unwrap_or("").to_string();
if let Some(sha256_val) = sha256 {
if sha256_val != hash("sha256", &json) {
// undo downgrade before trying again if http seems to be hijacked or modifying content somehow
@@ -2896,7 +3015,8 @@ impl ComposerRepository {
}
}
- if let Some(dispatcher) = self.event_dispatcher.as_mut() {
+ if let Some(dispatcher) = self.event_dispatcher.as_ref() {
+ let mut dispatcher = dispatcher.borrow_mut();
let mut post_file_download_event = PostFileDownloadEvent::new(
PluginEvents::POST_FILE_DOWNLOAD.to_string(),
None,
@@ -2908,13 +3028,12 @@ impl ComposerRepository {
// TODO(plugin): pass response and repository self-reference
m.insert("response".to_string(), PhpMixed::Null);
m.insert("repository".to_string(), PhpMixed::Null);
- m
+ m.into()
},
);
- dispatcher.dispatch(
- &post_file_download_event.get_name(),
- &mut post_file_download_event,
- );
+ // TODO(phase-b): dispatcher.dispatch expects Option<Event>, not concrete event types
+ let _ = &mut post_file_download_event;
+ dispatcher.dispatch(Some(post_file_download_event.get_name()), None)?;
}
let decoded = response.decode_json()?;
@@ -2961,7 +3080,7 @@ impl ComposerRepository {
return Err(e);
}
if let Some(te) = e.downcast_ref::<TransportException>() {
- if te.get_status_code() == 404 {
+ if te.get_status_code() == Some(404) {
return Err(e);
}
}
@@ -2981,7 +3100,7 @@ impl ComposerRepository {
}
self.degraded_mode = true;
let parsed = JsonFile::parse_json(
- &contents,
+ Some(&contents),
Some(&format!("{}{}", self.cache.get_root(), ck)),
)?;
let map: IndexMap<String, PhpMixed> = parsed
@@ -3028,7 +3147,8 @@ impl ComposerRepository {
let mut filename = filename.to_string();
let result: anyhow::Result<FetchFileIfLastModifiedResult> = (|| {
let mut options = self.options.clone();
- if let Some(dispatcher) = self.event_dispatcher.as_mut() {
+ if let Some(dispatcher) = self.event_dispatcher.as_ref() {
+ let mut dispatcher = dispatcher.borrow_mut();
let mut pre_file_download_event = PreFileDownloadEvent::new(
PluginEvents::PRE_FILE_DOWNLOAD.to_string(),
std::rc::Rc::clone(&self.http_downloader),
@@ -3037,23 +3157,32 @@ impl ComposerRepository {
{
let mut m: IndexMap<String, PhpMixed> = IndexMap::new();
m.insert("repository".to_string(), PhpMixed::Null);
- m
+ m.into()
},
);
- pre_file_download_event.set_transport_options(self.options.clone());
- dispatcher.dispatch(
- &pre_file_download_event.get_name(),
- &mut pre_file_download_event,
+ pre_file_download_event.set_transport_options(
+ self.options
+ .clone()
+ .into_iter()
+ .map(|(k, v)| (k, Box::new(v)))
+ .collect(),
);
- filename = pre_file_download_event.get_processed_url();
- options = pre_file_download_event.get_transport_options();
+ // TODO(phase-b): dispatcher.dispatch expects Option<Event>, not concrete event types
+ let _ = &mut pre_file_download_event;
+ dispatcher.dispatch(Some(pre_file_download_event.get_name()), None)?;
+ filename = pre_file_download_event.get_processed_url().to_string();
+ options = pre_file_download_event
+ .get_transport_options()
+ .iter()
+ .map(|(k, v)| (k.clone(), (**v).clone()))
+ .collect();
}
// cast http.header to array, then append
let http_entry = options
.entry("http".to_string())
.or_insert(PhpMixed::Array(IndexMap::new()));
- if let PhpMixed::Array(ref mut http_map) = http_entry {
+ if let PhpMixed::Array(http_map) = http_entry {
if let Some(existing) = http_map.get("header") {
let arr = match &**existing {
PhpMixed::List(l) => l.clone(),
@@ -3075,13 +3204,17 @@ impl ComposerRepository {
http_map.insert("header".to_string(), Box::new(PhpMixed::List(headers)));
}
- let response = self.http_downloader.borrow_mut().get(&filename, &options)?;
- let mut json = response.get_body().to_string();
+ let mut response = self
+ .http_downloader
+ .borrow_mut()
+ .get(&filename, options.clone())?;
+ let mut json = response.get_body().unwrap_or("").to_string();
if json.is_empty() && response.get_status_code() == 304 {
return Ok(FetchFileIfLastModifiedResult::NotModified);
}
- if let Some(dispatcher) = self.event_dispatcher.as_mut() {
+ if let Some(dispatcher) = self.event_dispatcher.as_ref() {
+ let mut dispatcher = dispatcher.borrow_mut();
let mut post_file_download_event = PostFileDownloadEvent::new(
PluginEvents::POST_FILE_DOWNLOAD.to_string(),
None,
@@ -3092,13 +3225,12 @@ impl ComposerRepository {
let mut m: IndexMap<String, PhpMixed> = IndexMap::new();
m.insert("response".to_string(), PhpMixed::Null);
m.insert("repository".to_string(), PhpMixed::Null);
- m
+ m.into()
},
);
- dispatcher.dispatch(
- &post_file_download_event.get_name(),
- &mut post_file_download_event,
- );
+ // TODO(phase-b): dispatcher.dispatch expects Option<Event>, not concrete event types
+ let _ = &mut post_file_download_event;
+ dispatcher.dispatch(Some(post_file_download_event.get_name()), None)?;
}
let decoded = response.decode_json()?;
@@ -3133,7 +3265,7 @@ impl ComposerRepository {
return Err(e);
}
if let Some(te) = e.downcast_ref::<TransportException>() {
- if te.get_status_code() == 404 {
+ if te.get_status_code() == Some(404) {
return Err(e);
}
}
@@ -3183,7 +3315,8 @@ impl ComposerRepository {
let mut filename = filename.to_string();
let mut options = self.options.clone();
- if let Some(dispatcher) = self.event_dispatcher.as_mut() {
+ if let Some(dispatcher) = self.event_dispatcher.as_ref() {
+ let mut dispatcher = dispatcher.borrow_mut();
let mut pre_file_download_event = PreFileDownloadEvent::new(
PluginEvents::PRE_FILE_DOWNLOAD.to_string(),
std::rc::Rc::clone(&self.http_downloader),
@@ -3192,23 +3325,32 @@ impl ComposerRepository {
{
let mut m: IndexMap<String, PhpMixed> = IndexMap::new();
m.insert("repository".to_string(), PhpMixed::Null);
- m
+ m.into()
},
);
- pre_file_download_event.set_transport_options(self.options.clone());
- dispatcher.dispatch(
- &pre_file_download_event.get_name(),
- &mut pre_file_download_event,
+ pre_file_download_event.set_transport_options(
+ self.options
+ .clone()
+ .into_iter()
+ .map(|(k, v)| (k, Box::new(v)))
+ .collect(),
);
- filename = pre_file_download_event.get_processed_url();
- options = pre_file_download_event.get_transport_options();
+ // TODO(phase-b): dispatcher.dispatch expects Option<Event>, not concrete event types
+ let _ = &mut pre_file_download_event;
+ dispatcher.dispatch(Some(pre_file_download_event.get_name()), None)?;
+ filename = pre_file_download_event.get_processed_url().to_string();
+ options = pre_file_download_event
+ .get_transport_options()
+ .iter()
+ .map(|(k, v)| (k.clone(), (**v).clone()))
+ .collect();
}
if let Some(last_modified_time) = last_modified_time {
let http_entry = options
.entry("http".to_string())
.or_insert(PhpMixed::Array(IndexMap::new()));
- if let PhpMixed::Array(ref mut http_map) = http_entry {
+ if let PhpMixed::Array(http_map) = http_entry {
if let Some(existing) = http_map.get("header") {
let arr = match &**existing {
PhpMixed::List(l) => l.clone(),
@@ -3236,10 +3378,11 @@ impl ComposerRepository {
let url_owned = self.url.clone();
let last_modified_time_owned = last_modified_time.map(|s| s.to_string());
- let packages_not_found_ptr = &mut self.packagesNotFoundCache as *mut _;
- let fresh_metadata_ptr = &mut self.freshMetadataUrls as *mut _;
- let degraded_ptr = &mut self.degraded_mode as *mut _;
- let cache_ptr = &mut self.cache as *mut _;
+ let packages_not_found_ptr: *mut IndexMap<String, bool> =
+ &mut self.packagesNotFoundCache as *mut _;
+ let fresh_metadata_ptr: *mut IndexMap<String, bool> = &mut self.freshMetadataUrls as *mut _;
+ let degraded_ptr: *mut bool = &mut self.degraded_mode as *mut _;
+ let cache_ptr: *mut Cache = &mut self.cache as *mut _;
let io_ptr = self.io.as_ref() as *const dyn IOInterface;
let accept = {
@@ -3248,7 +3391,7 @@ impl ComposerRepository {
let url_owned = url_owned.clone();
move |response_mixed: PhpMixed| -> anyhow::Result<PhpMixed> {
// emulate: $response is a Response object; status code/body/header accessed via methods
- let response = Response::from_php_mixed(response_mixed)?;
+ let mut response = Response::from_php_mixed(response_mixed);
// package not found is acceptable for a v2 protocol repository
if response.get_status_code() == 404 {
unsafe {
@@ -3262,7 +3405,7 @@ impl ComposerRepository {
));
}
- let mut json = response.get_body().to_string();
+ let mut json = response.get_body().unwrap_or("").to_string();
if json.is_empty() && response.get_status_code() == 304 {
unsafe {
(*fresh_metadata_ptr).insert(filename.clone(), true);
@@ -3318,7 +3461,7 @@ impl ComposerRepository {
let accept_clone = accept.clone();
move |e: anyhow::Error| -> anyhow::Result<PhpMixed> {
if let Some(te) = e.downcast_ref::<TransportException>() {
- if te.get_status_code() == 404 {
+ if te.get_status_code() == Some(404) {
unsafe {
(*packages_not_found_ptr).insert(filename.clone(), true);
}
@@ -3348,7 +3491,7 @@ impl ComposerRepository {
// special error code returned when network is being artificially disabled
if let Some(te) = e.downcast_ref::<TransportException>() {
- if te.get_status_code() == 499 {
+ if te.get_status_code() == Some(499) {
let resp =
Response::new_fake(&url_owned, 404, IndexMap::new(), String::new());
return accept_clone(resp.to_php_mixed());
@@ -3359,7 +3502,10 @@ impl ComposerRepository {
}
};
- let initial = self.http_downloader.borrow_mut().add(&filename, &options)?;
+ let initial = self
+ .http_downloader
+ .borrow_mut()
+ .add(&filename, options.clone())?;
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 d956cdd..2c26469 100644
--- a/crates/shirabe/src/repository/composite_repository.rs
+++ b/crates/shirabe/src/repository/composite_repository.rs
@@ -120,11 +120,30 @@ impl RepositoryInterface for CompositeRepository {
let mut all_names_found = vec![];
for repository in &self.repositories {
+ // TODO(phase-b): manual deep clone since trait objects in maps don't derive Clone.
+ let name_map_cloned: IndexMap<String, Option<Box<dyn ConstraintInterface>>> =
+ package_name_map
+ .iter()
+ .map(|(k, v)| (k.clone(), v.as_ref().map(|c| c.clone_box())))
+ .collect();
+ let already_loaded_cloned: IndexMap<
+ String,
+ IndexMap<String, Box<dyn PackageInterface>>,
+ > = already_loaded
+ .iter()
+ .map(|(k, inner)| {
+ let inner_cloned: IndexMap<String, Box<dyn PackageInterface>> = inner
+ .iter()
+ .map(|(ik, iv)| (ik.clone(), iv.clone_package_box()))
+ .collect();
+ (k.clone(), inner_cloned)
+ })
+ .collect();
let result = repository.load_packages(
- package_name_map.clone(),
+ name_map_cloned,
acceptable_stabilities.clone(),
stability_flags.clone(),
- already_loaded.clone(),
+ already_loaded_cloned,
);
all_packages.extend(result.packages);
all_names_found.extend(result.names_found);
diff --git a/crates/shirabe/src/repository/filesystem_repository.rs b/crates/shirabe/src/repository/filesystem_repository.rs
index 4ce8585..f30e401 100644
--- a/crates/shirabe/src/repository/filesystem_repository.rs
+++ b/crates/shirabe/src/repository/filesystem_repository.rs
@@ -9,8 +9,8 @@ use shirabe_external_packages::composer::pcre::preg::Preg;
use shirabe_php_shim::{
Exception, InvalidArgumentException, LogicException, PhpMixed, SORT_NATURAL,
UnexpectedValueException, array_flip, dirname, r#eval, file_get_contents, get_class,
- get_debug_type, in_array, is_array, is_int, is_null, is_string, ksort, php_dir, realpath, sort,
- sort_with_flags, str_repeat, strtr, trim, usort, var_export,
+ get_class_err, get_debug_type, in_array, is_array, is_int, is_null, is_string, ksort, php_dir,
+ realpath, sort, sort_with_flags, str_repeat, strtr, trim, usort, var_export,
};
use crate::installed_versions::InstalledVersions;
@@ -139,7 +139,7 @@ impl FilesystemRepository {
message: format!(
"Invalid repository data in {}, packages could not be loaded: [{}] {}",
self.file.get_path(),
- get_class(&e),
+ get_class_err(&e),
e,
),
code: 0,
@@ -151,18 +151,34 @@ impl FilesystemRepository {
let mut loader = ArrayLoader::new(None, true);
if let Some(packages_list) = packages.as_list() {
for package_data in packages_list.iter() {
- let package = loader.load(
- (**package_data).clone(),
- "Composer\\Package\\CompletePackage",
- )?;
+ // TODO(phase-b): expected IndexMap<String, PhpMixed> but package_data is PhpMixed.
+ let cfg = (**package_data)
+ .as_array()
+ .cloned()
+ .map(|m| {
+ m.into_iter()
+ .map(|(k, v)| (k, *v))
+ .collect::<IndexMap<String, PhpMixed>>()
+ })
+ .unwrap_or_default();
+ let package =
+ loader.load(cfg, Some("Composer\\Package\\CompletePackage".to_string()))?;
self.inner.add_package(package)?;
}
} else if let Some(packages_array) = packages.as_array() {
for (_, package_data) in packages_array.iter() {
- let package = loader.load(
- (**package_data).clone(),
- "Composer\\Package\\CompletePackage",
- )?;
+ // TODO(phase-b): expected IndexMap<String, PhpMixed> but package_data is PhpMixed.
+ let cfg = (**package_data)
+ .as_array()
+ .cloned()
+ .map(|m| {
+ m.into_iter()
+ .map(|(k, v)| (k, *v))
+ .collect::<IndexMap<String, PhpMixed>>()
+ })
+ .unwrap_or_default();
+ let package =
+ loader.load(cfg, Some("Composer\\Package\\CompletePackage".to_string()))?;
self.inner.add_package(package)?;
}
}
@@ -180,7 +196,7 @@ impl FilesystemRepository {
pub fn write(
&mut self,
dev_mode: bool,
- installation_manager: &InstallationManager,
+ installation_manager: &mut InstallationManager,
) -> Result<()> {
let mut data: IndexMap<String, PhpMixed> = IndexMap::new();
data.insert("packages".to_string(), PhpMixed::List(vec![]));
@@ -226,6 +242,7 @@ impl FilesystemRepository {
&repo_dir,
&normalized_path,
true,
+ false,
));
}
}
@@ -267,9 +284,8 @@ impl FilesystemRepository {
}
// PHP: sort($data['dev-package-names']);
- if let Some(PhpMixed::List(list)) = data.get_mut("dev-package-names") {
- // TODO(phase-b): sort PhpMixed::List in-place using string comparison
- sort(list);
+ if let Some(PhpMixed::List(_list)) = data.get_mut("dev-package-names") {
+ // TODO(phase-b): sort PhpMixed::List in-place using string comparison; PhpMixed: !Ord.
}
// PHP: usort($data['packages'], static function ($a, $b): int { return strcmp($a['name'], $b['name']); });
if let Some(PhpMixed::List(list)) = data.get_mut("packages") {
@@ -576,11 +592,7 @@ impl FilesystemRepository {
};
// TODO(phase-b): mutate nested versions['versions'][name]['aliases']
todo!("append alias->getPrettyVersion() to versions['versions'][name]['aliases']");
- if package
- .as_any()
- .downcast_ref::<dyn RootPackageInterface>()
- .is_some()
- {
+ if package.as_root_package_interface().is_some() {
// TODO(phase-b): same mutation on versions['root']['aliases']
todo!("append alias->getPrettyVersion() to versions['root']['aliases']");
}
@@ -596,9 +608,11 @@ impl FilesystemRepository {
for (_name, version) in versions_map.iter_mut() {
if let PhpMixed::Array(version_map) = version.as_mut() {
for key in ["aliases", "replaced", "provided"] {
- if let Some(PhpMixed::List(list)) = version_map.get_mut(key) {
- // PHP: sort($versions['versions'][$name][$key], SORT_NATURAL);
- sort_with_flags(list, SORT_NATURAL);
+ if let Some(boxed) = version_map.get_mut(key) {
+ if let PhpMixed::List(_list) = boxed.as_mut() {
+ // PHP: sort($versions['versions'][$name][$key], SORT_NATURAL);
+ // TODO(phase-b): PhpMixed lacks Ord; needs custom comparator.
+ }
}
}
}
@@ -642,18 +656,14 @@ impl FilesystemRepository {
};
}
- let install_path = if package
- .as_any()
- .downcast_ref::<dyn RootPackageInterface>()
- .is_some()
- {
+ let install_path = if package.as_root_package_interface().is_some() {
let to = self.filesystem.borrow_mut().normalize_path(
&realpath(&Platform::get_cwd(false).unwrap_or_default()).unwrap_or_default(),
);
Some(
self.filesystem
.borrow_mut()
- .find_shortest_path(repo_dir, &to, true),
+ .find_shortest_path(repo_dir, &to, true, false),
)
} else {
install_paths.get(package.get_name()).cloned().flatten()
diff --git a/crates/shirabe/src/repository/installed_repository.rs b/crates/shirabe/src/repository/installed_repository.rs
index 12abf1e..6091b15 100644
--- a/crates/shirabe/src/repository/installed_repository.rs
+++ b/crates/shirabe/src/repository/installed_repository.rs
@@ -61,8 +61,7 @@ impl InstalledRepository {
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())
}
};
@@ -81,11 +80,13 @@ impl InstalledRepository {
continue;
}
+ let provides = candidate.get_provides();
+ let replaces = candidate.get_replaces();
let mut provides_and_replaces: Vec<&Link> = vec![];
- for link in candidate.get_provides().values() {
+ for link in provides.values() {
provides_and_replaces.push(link);
}
- for link in candidate.get_replaces().values() {
+ for link in replaces.values() {
provides_and_replaces.push(link);
}
for link in provides_and_replaces {
@@ -381,8 +382,9 @@ impl InstalledRepository {
&mut self,
repository: Box<dyn RepositoryInterface>,
) -> anyhow::Result<()> {
+ // TODO(phase-b): cannot Any::is::<dyn InstalledRepositoryInterface>; replace with a
+ // dedicated downcast/marker method on RepositoryInterface.
if repository.as_any().is::<LockArrayRepository>()
- || repository.as_any().is::<dyn InstalledRepositoryInterface>()
|| repository.as_any().is::<RootPackageRepository>()
|| repository.as_any().is::<PlatformRepository>()
{
diff --git a/crates/shirabe/src/repository/installed_repository_interface.rs b/crates/shirabe/src/repository/installed_repository_interface.rs
index 711193a..80efef9 100644
--- a/crates/shirabe/src/repository/installed_repository_interface.rs
+++ b/crates/shirabe/src/repository/installed_repository_interface.rs
@@ -6,4 +6,8 @@ pub trait InstalledRepositoryInterface: WritableRepositoryInterface {
fn get_dev_mode(&self) -> Option<bool>;
fn is_fresh(&self) -> bool;
+
+ fn clone_installed_repository_box(&self) -> Box<dyn InstalledRepositoryInterface> {
+ todo!()
+ }
}
diff --git a/crates/shirabe/src/repository/path_repository.rs b/crates/shirabe/src/repository/path_repository.rs
index c29e6c0..620bff8 100644
--- a/crates/shirabe/src/repository/path_repository.rs
+++ b/crates/shirabe/src/repository/path_repository.rs
@@ -47,7 +47,7 @@ impl PathRepository {
io: Box<dyn IOInterface>,
config: std::rc::Rc<std::cell::RefCell<Config>>,
http_downloader: Option<std::rc::Rc<std::cell::RefCell<HttpDownloader>>>,
- dispatcher: Option<EventDispatcher>,
+ dispatcher: Option<std::rc::Rc<std::cell::RefCell<EventDispatcher>>>,
process: Option<std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>,
) -> anyhow::Result<Self> {
if !repo_config.contains_key("url") {
@@ -73,7 +73,7 @@ impl PathRepository {
let version_guesser = VersionGuesser::new(
config,
std::rc::Rc::clone(&process),
- shirabe_semver::version_parser::VersionParser,
+ VersionParser::new(),
Some(io.clone_box()),
);
let mut options = repo_config
@@ -173,7 +173,7 @@ impl PathRepository {
.unwrap_or("auto")
.to_string();
if reference == "none" {
- if let Some(PhpMixed::Array(ref mut dist)) = package.get_mut("dist") {
+ if let Some(PhpMixed::Array(dist)) = package.get_mut("dist") {
dist.insert("reference".to_string(), Box::new(PhpMixed::Null));
}
} else if reference == "config" || reference == "auto" {
@@ -184,7 +184,7 @@ impl PathRepository {
.collect(),
);
let ref_hash = hash("sha1", &format!("{}{}", json, serialize(&options_mixed)));
- if let Some(PhpMixed::Array(ref mut dist)) = package.get_mut("dist") {
+ if let Some(PhpMixed::Array(dist)) = package.get_mut("dist") {
dist.insert(
"reference".to_string(),
Box::new(PhpMixed::String(ref_hash)),
@@ -237,12 +237,12 @@ impl PathRepository {
let code2 = self
.process
.borrow_mut()
- .execute(cmd, Some(&mut ref2), None)
+ .execute(cmd, Some(&mut ref2), ())
.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()),
+ PhpMixed::String(self.version_guesser.get_root_version_from_env()?),
);
}
}
@@ -276,32 +276,33 @@ impl PathRepository {
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") {
+ if let Some(PhpMixed::Array(dist)) = package.get_mut("dist") {
dist.insert("reference".to_string(), Box::new(PhpMixed::String(ref_val)));
}
}
if !package.contains_key("version") {
- let version_data = self.version_guesser.guess_version(&package, &path);
+ let version_data = self.version_guesser.guess_version(&package, &path)?;
if let Some(version_data) = version_data {
if let Some(pretty_version) = version_data
- .get("pretty_version")
- .and_then(|v| v.as_string())
+ .pretty_version
+ .as_ref()
.filter(|s| !s.is_empty())
- .map(|s| s.to_string())
+ .cloned()
{
// if there is a feature branch detected, we add a second package with the feature branch version
if let Some(feature_pretty_version) = version_data
- .get("feature_pretty_version")
- .and_then(|v| v.as_string())
+ .feature_pretty_version
+ .as_ref()
.filter(|s| !s.is_empty())
- .map(|s| s.to_string())
+ .cloned()
{
package.insert(
"version".to_string(),
PhpMixed::String(feature_pretty_version),
);
- self.inner.add_package(self.loader.load(package.clone())?);
+ self.inner
+ .add_package(self.loader.load(package.clone(), None)?);
}
package.insert("version".to_string(), PhpMixed::String(pretty_version));
@@ -320,17 +321,12 @@ impl PathRepository {
}
self.inner
- .add_package(
- self.loader
- .load(package.clone())
- .map_err(|e| RuntimeException {
- message: format!(
- "Failed loading the package in {}",
- composer_file_path
- ),
- code: 0,
- })?,
- );
+ .add_package(self.loader.load(package.clone(), None).map_err(|e| {
+ RuntimeException {
+ message: format!("Failed loading the package in {}", composer_file_path),
+ code: 0,
+ }
+ })?);
}
Ok(())
diff --git a/crates/shirabe/src/repository/platform_repository.rs b/crates/shirabe/src/repository/platform_repository.rs
index 64bc861..79c06fe 100644
--- a/crates/shirabe/src/repository/platform_repository.rs
+++ b/crates/shirabe/src/repository/platform_repository.rs
@@ -444,17 +444,18 @@ impl PlatformRepository {
let mut is_fips = false;
let parsed_version = Version::parse_openssl(&ssl_version, &mut is_fips)
.unwrap_or_default();
+ let fips_provides: Vec<String> = if is_fips {
+ vec!["curl-openssl".to_string()]
+ } else {
+ Vec::new()
+ };
self.add_library(
&mut libraries,
&format!("{}-openssl{}", name, if is_fips { "-fips" } else { "" }),
Some(&parsed_version),
Some(&format!("curl OpenSSL version ({})", parsed_version)),
&[],
- if is_fips {
- &["curl-openssl".to_string()]
- } else {
- &[]
- },
+ &fips_provides,
)?;
} else {
let (shortlib, ssl_lib);
@@ -887,7 +888,8 @@ impl PlatformRepository {
Box::new(PhpMixed::String("getUnicodeVersion".to_string())),
])],
);
- let sliced = array_slice(&intl_char_versions, 0, Some(3));
+ let sliced =
+ shirabe_php_shim::array_slice_mixed(&intl_char_versions, 0, Some(3));
let joined = implode(".", &Self::php_array_to_string_vec(&sliced));
self.add_library(
&mut libraries,
@@ -1605,7 +1607,12 @@ impl PlatformRepository {
return Ok(());
}
- let overrider = self.inner.find_package(package.get_name(), "*".to_string());
+ let overrider = self.inner.find_package(
+ package.get_name(),
+ crate::repository::repository_interface::FindPackageConstraint::String(
+ "*".to_string(),
+ ),
+ );
let actual_text = if let Some(ref ov) = overrider {
if package.get_version() == ov.get_version() {
"same as actual".to_string()
@@ -1670,11 +1677,13 @@ impl PlatformRepository {
package.set_description("Package overridden via config.platform".to_string());
let mut extra: IndexMap<String, PhpMixed> = IndexMap::new();
extra.insert("config.platform".to_string(), PhpMixed::Bool(true));
- package.set_extra(extra);
- // TODO(phase-b): CompletePackage is `Box<dyn PackageInterface>`-cloneable in PHP;
- // here we add a clone for ArrayRepository but also return the original.
- self.inner.add_package(Box::new(package.clone()));
+ package.inner.set_extra(extra);
+ // TODO(phase-b): CompletePackage is a PHP class (shared by ref); cannot Clone.
+ // The container should likely store Rc<RefCell<CompletePackage>> so both the inner
+ // ArrayRepository and the function caller can share ownership.
+ let _: () = todo!("share CompletePackage via Rc between add_package and return");
+ #[allow(unreachable_code)]
if package.get_name() == "php" {
let parts = explode(".", package.get_version());
let head = array_slice_strs(&parts, 0, Some(3));
@@ -1696,7 +1705,7 @@ impl PlatformRepository {
));
let mut extra: IndexMap<String, PhpMixed> = IndexMap::new();
extra.insert("config.platform".to_string(), PhpMixed::Bool(true));
- package.set_extra(extra);
+ package.inner.set_extra(extra);
self.disabled_packages
.insert(package.get_name().to_string(), Box::new(package));
@@ -1742,7 +1751,7 @@ impl PlatformRepository {
name,
extra_description.unwrap_or_default()
));
- ext.set_type("php-ext".to_string());
+ ext.inner.set_type("php-ext".to_string());
if name == "uuid" {
let mut replaces: IndexMap<String, Link> = IndexMap::new();
@@ -1752,11 +1761,11 @@ impl PlatformRepository {
"ext-uuid".to_string(),
"lib-uuid".to_string(),
Box::new(Constraint::new("=", &version)),
- Link::TYPE_REPLACE.to_string(),
+ Some(Link::TYPE_REPLACE.to_string()),
Some(ext.get_pretty_version().to_string()),
),
);
- ext.set_replaces(replaces);
+ ext.inner.set_replaces(replaces);
}
self.add_package(Box::new(ext))?;
@@ -1817,7 +1826,7 @@ impl PlatformRepository {
format!("lib-{}", name),
format!("lib-{}", replace_lower),
Box::new(Constraint::new("=", &version)),
- Link::TYPE_REPLACE.to_string(),
+ Some(Link::TYPE_REPLACE.to_string()),
Some(lib.get_pretty_version().to_string()),
),
);
@@ -1831,13 +1840,13 @@ impl PlatformRepository {
format!("lib-{}", name),
format!("lib-{}", provide_lower),
Box::new(Constraint::new("=", &version)),
- Link::TYPE_PROVIDE.to_string(),
+ Some(Link::TYPE_PROVIDE.to_string()),
Some(lib.get_pretty_version().to_string()),
),
);
}
- lib.set_replaces(replace_links);
- lib.set_provides(provide_links);
+ lib.inner.set_replaces(replace_links);
+ lib.inner.set_provides(provide_links);
self.add_package(Box::new(lib))?;
Ok(())
@@ -1872,7 +1881,7 @@ impl PlatformRepository {
r#type: Option<String>,
) -> Vec<crate::repository::repository_interface::SearchResult> {
// suppress vendor search as there are no vendors to match in platform packages
- if mode == <dyn RepositoryInterface>::SEARCH_VENDOR {
+ if mode == crate::repository::repository_interface::SEARCH_VENDOR {
return Vec::new();
}
diff --git a/crates/shirabe/src/repository/repository_factory.rs b/crates/shirabe/src/repository/repository_factory.rs
index 11e5f61..614166c 100644
--- a/crates/shirabe/src/repository/repository_factory.rs
+++ b/crates/shirabe/src/repository/repository_factory.rs
@@ -39,7 +39,7 @@ impl RepositoryFactory {
.unwrap_or("");
if extension == "json" {
- let json = JsonFile::new(
+ let mut json = JsonFile::new(
repository.to_string(),
Some(std::rc::Rc::new(std::cell::RefCell::new(
Factory::create_http_downloader(io, config, IndexMap::new())?,
@@ -82,7 +82,11 @@ impl RepositoryFactory {
}
if repository.starts_with('{') {
- let repo_config = JsonFile::parse_json(repository, None)?.unwrap_or_default();
+ let parsed = JsonFile::parse_json(Some(repository), None)?;
+ let repo_config: IndexMap<String, PhpMixed> = parsed
+ .as_array()
+ .map(|m| m.iter().map(|(k, v)| (k.clone(), (**v).clone())).collect())
+ .unwrap_or_default();
return Ok(repo_config);
}
@@ -116,7 +120,7 @@ impl RepositoryFactory {
owned_rm = Self::manager(io, config, None, None, None)?;
&mut owned_rm
};
- let mut repos = Self::create_repos(
+ let repos = Self::create_repos(
rm,
vec![PhpMixed::Array(
repo_config
@@ -125,20 +129,29 @@ impl RepositoryFactory {
.collect(),
)],
)?;
- Ok(repos.remove(0))
+ // PHP: return current($repos);
+ let (_, first) = repos
+ .into_iter()
+ .next()
+ .ok_or_else(|| UnexpectedValueException {
+ message: "create_repos returned no repository".to_string(),
+ code: 0,
+ })?;
+ Ok(first)
}
pub fn default_repos(
io: Option<&dyn IOInterface>,
config: Option<std::rc::Rc<std::cell::RefCell<Config>>>,
rm: Option<&mut RepositoryManager>,
- ) -> anyhow::Result<Vec<Box<dyn RepositoryInterface>>> {
+ ) -> anyhow::Result<IndexMap<String, Box<dyn RepositoryInterface>>> {
let config = match config {
Some(c) => c,
None => std::rc::Rc::new(std::cell::RefCell::new(Factory::create_config(None, None)?)),
};
- if let Some(io) = io {
- io.load_configuration(&mut *config.borrow_mut())?;
+ if let Some(_io) = io {
+ // TODO(phase-b): IOInterface::load_configuration requires &mut self, but this
+ // function takes &dyn IOInterface. Wider refactor needed; skip for now.
}
let mut owned_rm;
@@ -163,14 +176,15 @@ impl RepositoryFactory {
};
let repo_configs = config.borrow().get_repositories();
- Self::create_repos(rm, repo_configs)
+ // PHP: array_values($repoConfigs) — keep ordering, discard keys
+ Self::create_repos(rm, repo_configs.into_iter().map(|(_, v)| v).collect())
}
pub fn manager(
io: &dyn IOInterface,
config: &std::rc::Rc<std::cell::RefCell<Config>>,
http_downloader: Option<std::rc::Rc<std::cell::RefCell<HttpDownloader>>>,
- event_dispatcher: Option<EventDispatcher>,
+ event_dispatcher: Option<std::rc::Rc<std::cell::RefCell<EventDispatcher>>>,
process: Option<std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>,
) -> anyhow::Result<RepositoryManager> {
let http_downloader = match http_downloader {
@@ -217,8 +231,8 @@ impl RepositoryFactory {
}
pub fn default_repos_with_default_manager(
- io: &dyn IOInterface,
- ) -> anyhow::Result<Vec<Box<dyn RepositoryInterface>>> {
+ io: &mut dyn IOInterface,
+ ) -> anyhow::Result<IndexMap<String, Box<dyn RepositoryInterface>>> {
let config = std::rc::Rc::new(std::cell::RefCell::new(Factory::create_config(
Some(io),
None,
@@ -231,7 +245,7 @@ impl RepositoryFactory {
fn create_repos(
rm: &mut RepositoryManager,
repo_configs: Vec<PhpMixed>,
- ) -> anyhow::Result<Vec<Box<dyn RepositoryInterface>>> {
+ ) -> anyhow::Result<IndexMap<String, Box<dyn RepositoryInterface>>> {
let mut repo_map: IndexMap<String, Box<dyn RepositoryInterface>> = IndexMap::new();
for (index, repo) in repo_configs.into_iter().enumerate() {
@@ -267,15 +281,22 @@ impl RepositoryFactory {
Self::generate_repository_name_indexed(index, &repo_config_map, &repo_map);
if repo_type == "filesystem" {
- let json_path = repo_arr
+ let _json_path = repo_arr
.get("json")
.and_then(|v| v.as_string())
.unwrap_or("")
.to_string();
- repo_map.insert(name, Box::new(FilesystemRepository::new(json_path)?));
+ // TODO(phase-b): FilesystemRepository does not yet implement
+ // RepositoryInterface; once it does, construct it from JsonFile here.
+ let created: Box<dyn RepositoryInterface> =
+ todo!("FilesystemRepository as dyn RepositoryInterface");
+ repo_map.insert(name, created);
} else {
- let created =
- rm.create_repository(&repo_type, repo_config_map, &index.to_string())?;
+ let created = rm.create_repository(
+ &repo_type,
+ repo_config_map,
+ Some(&index.to_string()),
+ )?;
repo_map.insert(name, created);
}
}
@@ -294,7 +315,7 @@ impl RepositoryFactory {
}
}
- Ok(repo_map.into_values().collect())
+ Ok(repo_map)
}
pub fn generate_repository_name(
@@ -305,7 +326,7 @@ impl RepositoryFactory {
let mut name = match index {
PhpMixed::Int(_) => {
if let Some(url) = repo.get("url").and_then(|v| v.as_string()) {
- Preg::replace("{^https?://}i", "", url, -1).unwrap_or_else(|_| url.to_string())
+ Preg::replace("{^https?://}i", "", url).unwrap_or_else(|_| url.to_string())
} else {
index.as_string().unwrap_or("").to_string()
}
@@ -324,7 +345,7 @@ impl RepositoryFactory {
existing_repos: &IndexMap<String, Box<dyn RepositoryInterface>>,
) -> String {
let mut name = if let Some(url) = repo.get("url").and_then(|v| v.as_string()) {
- Preg::replace("{^https?://}i", "", url, -1).unwrap_or_else(|_| url.to_string())
+ Preg::replace("{^https?://}i", "", url).unwrap_or_else(|_| url.to_string())
} else {
index.to_string()
};
diff --git a/crates/shirabe/src/repository/repository_interface.rs b/crates/shirabe/src/repository/repository_interface.rs
index 6113997..de37b5f 100644
--- a/crates/shirabe/src/repository/repository_interface.rs
+++ b/crates/shirabe/src/repository/repository_interface.rs
@@ -26,11 +26,13 @@ pub struct LoadPackagesResult {
pub packages: Vec<Box<dyn BasePackage>>,
}
+#[derive(Debug, Clone)]
pub enum AbandonedInfo {
Replacement(String),
Abandoned,
}
+#[derive(Debug, Clone)]
pub struct SearchResult {
pub name: String,
pub description: Option<String>,
@@ -38,6 +40,7 @@ pub struct SearchResult {
pub url: Option<String>,
}
+#[derive(Debug, Clone)]
pub struct ProviderInfo {
pub name: String,
pub description: Option<String>,
@@ -83,6 +86,13 @@ pub trait RepositoryInterface: Countable + std::fmt::Debug {
None
}
+ fn as_installed_repository_interface(
+ &self,
+ ) -> Option<&dyn crate::repository::installed_repository_interface::InstalledRepositoryInterface>
+ {
+ None
+ }
+
fn as_any(&self) -> &dyn std::any::Any;
fn clone_box(&self) -> Box<dyn RepositoryInterface> {
diff --git a/crates/shirabe/src/repository/repository_manager.rs b/crates/shirabe/src/repository/repository_manager.rs
index a3c5b78..b697949 100644
--- a/crates/shirabe/src/repository/repository_manager.rs
+++ b/crates/shirabe/src/repository/repository_manager.rs
@@ -22,7 +22,7 @@ pub struct RepositoryManager {
io: Box<dyn IOInterface>,
config: std::rc::Rc<std::cell::RefCell<Config>>,
http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
- event_dispatcher: Option<EventDispatcher>,
+ event_dispatcher: Option<std::rc::Rc<std::cell::RefCell<EventDispatcher>>>,
process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
}
@@ -31,7 +31,7 @@ impl RepositoryManager {
io: &dyn IOInterface,
config: std::rc::Rc<std::cell::RefCell<Config>>,
http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
- event_dispatcher: Option<EventDispatcher>,
+ event_dispatcher: Option<std::rc::Rc<std::cell::RefCell<EventDispatcher>>>,
process: Option<std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>,
) -> Self {
let process = process
@@ -54,8 +54,13 @@ impl RepositoryManager {
constraint: &dyn ConstraintInterface,
) -> Option<Box<dyn PackageInterface>> {
for repository in &self.repositories {
- if let Some(package) = repository.find_package(name, constraint) {
- return Some(package);
+ if let Some(package) = repository.find_package(
+ name,
+ crate::repository::repository_interface::FindPackageConstraint::Constraint(
+ constraint.clone_box(),
+ ),
+ ) {
+ return Some(package.clone_package_box());
}
}
None
@@ -68,7 +73,16 @@ impl RepositoryManager {
) -> Vec<Box<dyn PackageInterface>> {
let mut packages: Vec<Box<dyn PackageInterface>> = vec![];
for repository in self.get_repositories() {
- packages.extend(repository.find_packages(name, constraint));
+ for p in repository.find_packages(
+ name,
+ Some(
+ crate::repository::repository_interface::FindPackageConstraint::Constraint(
+ constraint.clone_box(),
+ ),
+ ),
+ ) {
+ packages.push(p.clone_package_box());
+ }
}
packages
}
@@ -126,7 +140,7 @@ impl RepositoryManager {
let repository = self.create_repository_by_class(&class, cleaned_config)?;
if let Some(filter_config) = filter_config {
- return Ok(Box::new(FilterRepository::new(repository, filter_config)));
+ return Ok(Box::new(FilterRepository::new(repository, filter_config)?));
}
Ok(repository)
diff --git a/crates/shirabe/src/repository/repository_set.rs b/crates/shirabe/src/repository/repository_set.rs
index 18c3ba6..f39840b 100644
--- a/crates/shirabe/src/repository/repository_set.rs
+++ b/crates/shirabe/src/repository/repository_set.rs
@@ -30,7 +30,9 @@ use crate::package::complete_alias_package::CompleteAliasPackage;
use crate::package::complete_package::CompletePackage;
use crate::package::package_interface::PackageInterface;
use crate::package::version::stability_filter::StabilityFilter;
-use crate::repository::advisory_provider_interface::AdvisoryProviderInterface;
+use crate::repository::advisory_provider_interface::{
+ AdvisoryProviderInterface, PartialOrSecurityAdvisory,
+};
use crate::repository::composite_repository::CompositeRepository;
use crate::repository::installed_repository::InstalledRepository;
use crate::repository::installed_repository_interface::InstalledRepositoryInterface;
@@ -221,7 +223,7 @@ impl RepositorySet {
let constraint_clone = constraint
.as_ref()
.map(|c| FindPackageConstraint::Constraint(c.clone_box()));
- let found = repository.find_packages(name.to_string(), constraint_clone);
+ let found = repository.find_packages(name, constraint_clone);
packages.push(found);
}
} else {
@@ -367,8 +369,8 @@ impl RepositorySet {
allow_partial_advisories: bool,
ignore_unreachable: bool,
unreachable_repos: &mut Vec<String>,
- ) -> Result<IndexMap<String, Vec<PartialSecurityAdvisory>>> {
- let mut repo_advisories: Vec<IndexMap<String, Vec<PartialSecurityAdvisory>>> = vec![];
+ ) -> Result<IndexMap<String, Vec<PartialOrSecurityAdvisory>>> {
+ let mut repo_advisories: Vec<IndexMap<String, Vec<PartialOrSecurityAdvisory>>> = vec![];
for repository in &self.repositories {
// TODO(phase-b): use anyhow::Result<Result<T, E>> to model PHP try/catch
let attempt: Result<()> = (|| -> Result<()> {
@@ -451,7 +453,7 @@ impl RepositorySet {
&mut self,
request: Request,
io: Box<dyn IOInterface>,
- event_dispatcher: Option<EventDispatcher>,
+ event_dispatcher: Option<std::rc::Rc<std::cell::RefCell<EventDispatcher>>>,
pool_optimizer: Option<PoolOptimizer>,
ignored_types: Vec<String>,
allowed_types: Option<Vec<String>>,
@@ -474,10 +476,7 @@ impl RepositorySet {
pool_builder.set_allowed_types(allowed_types);
for repo in &self.repositories {
- let is_installed = repo
- .as_any()
- .downcast_ref::<dyn InstalledRepositoryInterface>()
- .is_some()
+ let is_installed = repo.as_installed_repository_interface().is_some()
|| repo
.as_any()
.downcast_ref::<InstalledRepository>()
@@ -494,17 +493,17 @@ impl RepositorySet {
self.locked = true;
- // TODO(phase-b): pass repositories by reference; pool_builder.build_pool expects &Vec<Box<dyn RepositoryInterface>>
- pool_builder.build_pool(&self.repositories, &request)
+ // TODO(phase-b): pool_builder.build_pool takes owned Vec and &mut Request; revisit sharing model
+ pool_builder.build_pool(
+ todo!("share self.repositories"),
+ todo!("share request as &mut"),
+ )
}
/// 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()
- .downcast_ref::<dyn InstalledRepositoryInterface>()
- .is_some()
+ let is_installed = repo.as_installed_repository_interface().is_some()
|| repo
.as_any()
.downcast_ref::<InstalledRepository>()
@@ -643,6 +642,6 @@ impl RepositorySet {
#[derive(Debug)]
pub struct SecurityAdvisoriesResult {
- pub advisories: IndexMap<String, Vec<PartialSecurityAdvisory>>,
+ pub advisories: IndexMap<String, Vec<PartialOrSecurityAdvisory>>,
pub unreachable_repos: Vec<String>,
}
diff --git a/crates/shirabe/src/repository/vcs/forgejo_driver.rs b/crates/shirabe/src/repository/vcs/forgejo_driver.rs
index 179f2db..f4a86c7 100644
--- a/crates/shirabe/src/repository/vcs/forgejo_driver.rs
+++ b/crates/shirabe/src/repository/vcs/forgejo_driver.rs
@@ -50,7 +50,13 @@ impl ForgejoDriver {
);
self.forgejo_url = Some(forgejo_url);
- self.inner.cache = Some(Cache::new(&*self.inner.io, cache_dir));
+ self.inner.cache = Some(Cache::new(
+ self.inner.io.clone_box(),
+ &cache_dir,
+ None,
+ None,
+ false,
+ ));
self.inner.cache.as_mut().map(|c| {
c.set_read_only(
self.inner
@@ -321,8 +327,10 @@ impl ForgejoDriver {
if !self.inner.info_cache.contains_key(identifier) {
let composer = if self.inner.should_cache(identifier) {
- if let Some(res) = self.inner.cache.as_ref().and_then(|c| c.read(identifier)) {
- JsonFile::parse_json(&res, None)?
+ if let Some(res) = self.inner.cache.as_mut().and_then(|c| c.read(identifier)) {
+ // TODO(phase-b): JsonFile::parse_json returns PhpMixed; convert into Option<IndexMap>
+ let _ = JsonFile::parse_json(Some(res.as_str()), None)?;
+ None
} else {
let file_content = self.get_file_content("composer.json", identifier)?;
let c = VcsDriverBase::finish_base_composer_information(
@@ -332,14 +340,21 @@ impl ForgejoDriver {
)?;
if self.inner.should_cache(identifier) {
if let Some(ref composer_map) = c {
- let encoded = JsonFile::encode_with_options(
- composer_map,
- shirabe_php_shim::JSON_UNESCAPED_UNICODE
- | shirabe_php_shim::JSON_UNESCAPED_SLASHES,
+ // TODO(phase-b): JsonFile::encode_with_options does not exist; use encode
+ let encoded = JsonFile::encode(
+ &PhpMixed::Array(
+ composer_map
+ .iter()
+ .map(|(k, v)| (k.clone(), Box::new(v.clone())))
+ .collect(),
+ ),
+ (shirabe_php_shim::JSON_UNESCAPED_UNICODE
+ | shirabe_php_shim::JSON_UNESCAPED_SLASHES)
+ as i64,
);
self.inner
.cache
- .as_ref()
+ .as_mut()
.map(|c| c.write(identifier, &encoded));
}
}
@@ -394,8 +409,7 @@ impl ForgejoDriver {
format!("{}/commit/{}", html_url, identifier)
};
- if let Some(PhpMixed::Array(ref mut support)) = composer_map.get_mut("support")
- {
+ if let Some(PhpMixed::Array(support)) = composer_map.get_mut("support") {
support
.insert("source".to_string(), Box::new(PhpMixed::String(source_url)));
}
@@ -419,8 +433,7 @@ impl ForgejoDriver {
.map(|r| r.html_url.clone())
.unwrap_or_default()
);
- if let Some(PhpMixed::Array(ref mut support)) = composer_map.get_mut("support")
- {
+ if let Some(PhpMixed::Array(support)) = composer_map.get_mut("support") {
support
.insert("issues".to_string(), Box::new(PhpMixed::String(issues_url)));
}
diff --git a/crates/shirabe/src/repository/vcs/fossil_driver.rs b/crates/shirabe/src/repository/vcs/fossil_driver.rs
index f0c3468..f773e3b 100644
--- a/crates/shirabe/src/repository/vcs/fossil_driver.rs
+++ b/crates/shirabe/src/repository/vcs/fossil_driver.rs
@@ -64,7 +64,7 @@ impl FossilDriver {
.into());
}
- let local_name = Preg::replace(r"{[^a-z0-9]}i", "-", &self.inner.url);
+ 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);
@@ -82,7 +82,7 @@ impl FossilDriver {
if self.inner.process.borrow_mut().execute_args(
&["fossil", "version"].map(|s| s.to_string()).to_vec(),
&mut ignored_output,
- None,
+ (),
) != 0
{
return Err(RuntimeException {
@@ -100,7 +100,7 @@ impl FossilDriver {
pub(crate) fn update_local_repo(&mut self) -> anyhow::Result<()> {
assert!(self.repo_file.is_some());
- let fs = Filesystem::new(None);
+ let mut fs = Filesystem::new(None);
fs.ensure_directory_exists(&self.checkout_dir)?;
if !is_writable(&dirname(&self.checkout_dir)) {
@@ -149,10 +149,10 @@ impl FossilDriver {
.map(|s| s.to_string())
.to_vec(),
&mut output,
- None,
+ (),
) != 0
{
- let output = self.inner.process.borrow().get_error_output();
+ let output = self.inner.process.borrow().get_error_output().to_string();
return Err(RuntimeException {
message: format!(
"Failed to clone {} to repository {}\n\n{}",
@@ -171,7 +171,7 @@ impl FossilDriver {
Some(self.checkout_dir.clone()),
) != 0
{
- let output = self.inner.process.borrow().get_error_output();
+ let output = self.inner.process.borrow().get_error_output().to_string();
return Err(RuntimeException {
message: format!(
"Failed to open repository {} in {}\n\n{}",
@@ -280,7 +280,7 @@ impl FossilDriver {
Some(self.checkout_dir.clone()),
);
for branch in self.inner.process.borrow().split_lines(&output) {
- let branch = Preg::replace(r"/^\*/", "", &branch.trim());
+ let branch = Preg::replace(r"/^\*/", "", &branch.trim())?;
let branch = branch.trim().to_string();
branches.insert(branch.clone(), branch);
}
@@ -310,7 +310,7 @@ impl FossilDriver {
return false;
}
- let process = ProcessExecutor::new(io);
+ let mut process = ProcessExecutor::new(io);
let mut output = String::new();
if process.execute_args(
&["fossil", "info"].map(|s| s.to_string()).to_vec(),
diff --git a/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs b/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs
index 689c0e8..a5c6ed3 100644
--- a/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs
+++ b/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs
@@ -80,7 +80,7 @@ impl GitBitbucketDriver {
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,
+ self.inner.io.clone_box(),
&implode(
"/",
&[
@@ -97,6 +97,8 @@ impl GitBitbucketDriver {
],
),
None,
+ None,
+ false,
));
self.inner.cache.as_mut().unwrap().set_read_only(
self.inner
@@ -209,7 +211,11 @@ impl GitBitbucketDriver {
.and_then(|v| v.as_string())
.map(String::from);
- self.repo_data = repo_data;
+ // TODO(phase-b): unwrap PhpMixed::Array into the typed IndexMap stored on self
+ self.repo_data = match repo_data {
+ PhpMixed::Array(m) => m.into_iter().map(|(k, v)| (k, *v)).collect(),
+ _ => IndexMap::new(),
+ };
Ok(true)
}
@@ -226,13 +232,20 @@ impl GitBitbucketDriver {
if !self.inner.info_cache.contains_key(identifier) {
let mut composer: Option<IndexMap<String, PhpMixed>> = None;
if self.inner.should_cache(identifier) && {
- let res = self
- .inner
- .cache
- .as_ref()
- .and_then(|c| c.read(identifier).ok().flatten());
+ let res = self.inner.cache.as_mut().and_then(|c| c.read(identifier));
if let Some(res) = res {
- composer = Some(JsonFile::parse_json(&res, None)?);
+ // TODO(phase-b): wrap parsed PhpMixed::Array into the IndexMap-shaped composer slot
+ composer = Some(
+ JsonFile::parse_json(Some(&res), None)?
+ .as_array()
+ .cloned()
+ .map(|m| {
+ m.into_iter()
+ .map(|(k, v)| (k, *v))
+ .collect::<IndexMap<String, PhpMixed>>()
+ })
+ .unwrap_or_default(),
+ );
true
} else {
false
@@ -248,7 +261,7 @@ impl GitBitbucketDriver {
)?;
if self.inner.should_cache(identifier) {
- self.inner.cache.as_ref().unwrap().write(
+ self.inner.cache.as_mut().unwrap().write(
identifier,
&JsonFile::encode_with_indent(
&PhpMixed::Array(
@@ -422,10 +435,10 @@ impl GitBitbucketDriver {
],
);
- Ok(Some(
- self.fetch_with_oauth_credentials(&resource, false)?
- .get_body(),
- ))
+ Ok(self
+ .fetch_with_oauth_credentials(&resource, false)?
+ .get_body()
+ .map(|s| s.to_string()))
}
/// @inheritDoc
@@ -465,7 +478,8 @@ impl GitBitbucketDriver {
/// @inheritDoc
pub fn get_source(&self, identifier: &str) -> IndexMap<String, String> {
if let Some(fallback) = self.fallback_driver.as_ref() {
- return fallback.get_source(identifier);
+ // TODO(phase-b): trait returns Result; flatten for the inherent signature here
+ return fallback.get_source(identifier).unwrap_or_default();
}
let mut m: IndexMap<String, String> = IndexMap::new();
@@ -481,7 +495,8 @@ impl GitBitbucketDriver {
/// @inheritDoc
pub fn get_dist(&self, identifier: &str) -> Option<IndexMap<String, String>> {
if let Some(fallback) = self.fallback_driver.as_ref() {
- return fallback.get_dist(identifier);
+ // TODO(phase-b): trait returns Result; flatten for the inherent signature here
+ return fallback.get_dist(identifier).ok().flatten();
}
let url = sprintf(
@@ -685,7 +700,8 @@ impl GitBitbucketDriver {
None,
)?;
- if let Some(te) = e.downcast_ref::<TransportException>() {
+ {
+ let te = &e;
let code = te.get_code();
let in_set = in_array(
PhpMixed::Int(code),
@@ -703,7 +719,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);
+ return self.inner.get_contents(url).map_err(anyhow::Error::from);
}
if !self.inner.io.is_interactive() && fetching_repo_data {
@@ -714,15 +730,15 @@ impl GitBitbucketDriver {
.insert("url".to_string(), PhpMixed::String("dummy".to_string()));
return Ok(Response::new(
headers,
- 200,
- IndexMap::new(),
- "null".to_string(),
- ));
+ Some(200),
+ vec![],
+ Some("null".to_string()),
+ )??);
}
}
}
- Err(e)
+ Err(e.into())
}
}
}
@@ -786,7 +802,8 @@ impl GitBitbucketDriver {
r"/https:\/\/([^@]+@)?/",
"https://",
m.get("href").and_then(|v| v.as_string()).unwrap_or(""),
- );
+ )
+ .unwrap_or_default();
}
}
}
diff --git a/crates/shirabe/src/repository/vcs/git_driver.rs b/crates/shirabe/src/repository/vcs/git_driver.rs
index 07836bf..7ab185f 100644
--- a/crates/shirabe/src/repository/vcs/git_driver.rs
+++ b/crates/shirabe/src/repository/vcs/git_driver.rs
@@ -29,6 +29,24 @@ pub struct GitDriver {
}
impl GitDriver {
+ pub fn new(
+ repo_config: IndexMap<String, shirabe_php_shim::PhpMixed>,
+ io: Box<dyn IOInterface>,
+ config: std::rc::Rc<std::cell::RefCell<Config>>,
+ http_downloader: std::rc::Rc<
+ std::cell::RefCell<crate::util::http_downloader::HttpDownloader>,
+ >,
+ process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
+ ) -> Self {
+ Self {
+ inner: VcsDriverBase::new(repo_config, io, config, http_downloader, process),
+ tags: None,
+ branches: None,
+ root_identifier: None,
+ repo_dir: String::new(),
+ }
+ }
+
pub fn initialize(&mut self) -> anyhow::Result<()> {
let cache_url;
if Filesystem::is_local_path(&self.inner.url) {
@@ -65,12 +83,16 @@ impl GitDriver {
self.repo_dir = format!(
"{}/{}/",
cache_vcs_dir,
- Preg::replace(r"{[^a-z0-9.]}i", "-", Url::sanitize(self.inner.url.clone()))?
+ Preg::replace(
+ r"{[^a-z0-9.]}i",
+ "-",
+ &Url::sanitize(self.inner.url.clone())
+ )?
);
GitUtil::clean_env(&self.inner.process);
- let fs = Filesystem::new(None);
+ let mut fs = Filesystem::new(None);
fs.ensure_directory_exists(&dirname(&self.repo_dir))?;
if !is_writable(&dirname(&self.repo_dir)) {
@@ -96,8 +118,8 @@ impl GitDriver {
.into());
}
- let git_util = GitUtil::new(
- &*self.inner.io,
+ let mut git_util = GitUtil::new(
+ self.inner.io.clone_box(),
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))),
@@ -113,10 +135,10 @@ impl GitDriver {
}
.into());
}
- self.inner.io.write_error3(shirabe_php_shim::PhpMixed::String(format!(
+ self.inner.io.write_error3(&format!(
"<error>Failed to update {}, package information from this repository may be outdated</error>",
self.inner.url
- )), true, io_interface::NORMAL);
+ ), true, io_interface::NORMAL);
}
cache_url = self.inner.url.clone();
@@ -134,12 +156,15 @@ impl GitDriver {
.unwrap_or("")
.to_string();
self.inner.cache = Some(Cache::new(
- &*self.inner.io,
- format!(
+ self.inner.io.clone_box(),
+ &format!(
"{}/{}",
cache_repo_dir,
- Preg::replace(r"{[^a-z0-9.]}i", "-", Url::sanitize(cache_url))?
+ Preg::replace(r"{[^a-z0-9.]}i", "-", &Url::sanitize(cache_url))?
),
+ None,
+ None,
+ false,
));
self.inner.cache.as_mut().map(|c| {
c.set_read_only(
@@ -159,15 +184,15 @@ impl GitDriver {
if self.root_identifier.is_none() {
self.root_identifier = Some("master".to_string());
- let git_util = GitUtil::new(
- &*self.inner.io,
+ let mut git_util = GitUtil::new(
+ self.inner.io.clone_box(),
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 =
- git_util.get_mirror_default_branch(&self.inner.url, &self.repo_dir, false)?;
+ git_util.get_mirror_default_branch(&self.inner.url, &self.repo_dir, false);
if let Some(branch) = default_branch {
self.root_identifier = Some(branch.clone());
return Ok(branch);
@@ -269,7 +294,7 @@ impl GitDriver {
let command = GitUtil::build_rev_list_command(
&self.inner.process,
- &[
+ vec![
"-n1".to_string(),
"--format=%at".to_string(),
identifier.to_string(),
@@ -406,7 +431,11 @@ impl GitDriver {
{
return Ok(true);
}
- GitUtil::check_for_repo_ownership_error(&process.borrow().get_error_output(), &url);
+ GitUtil::check_for_repo_ownership_error(
+ &process.borrow().get_error_output(),
+ &url,
+ Some(io),
+ )?;
}
if !deep {
@@ -421,7 +450,7 @@ impl GitDriver {
"GitDriver::supports requires Rc<RefCell<Config>>: not yet ported"
));
#[allow(unreachable_code)]
- let git_util = GitUtil::new(
+ let mut git_util = GitUtil::new(
io.clone_box(),
todo!(),
std::rc::Rc::clone(&process),
@@ -430,7 +459,7 @@ impl GitDriver {
GitUtil::clean_env(&process);
let result = git_util.run_commands(
- &[vec![
+ vec![vec![
"git".to_string(),
"ls-remote".to_string(),
"--heads".to_string(),
@@ -438,7 +467,9 @@ impl GitDriver {
"%url%".to_string(),
]],
url,
- &sys_get_temp_dir(),
+ Some(&sys_get_temp_dir()),
+ false,
+ None,
);
match result {
Ok(_) => Ok(true),
diff --git a/crates/shirabe/src/repository/vcs/github_driver.rs b/crates/shirabe/src/repository/vcs/github_driver.rs
index 93bfcdb..17463c1 100644
--- a/crates/shirabe/src/repository/vcs/github_driver.rs
+++ b/crates/shirabe/src/repository/vcs/github_driver.rs
@@ -88,7 +88,7 @@ impl GitHubDriver {
self.inner.origin_url = "github.com".to_string();
}
self.inner.cache = Some(Cache::new(
- self.inner.io.as_ref(),
+ self.inner.io.clone_box(),
&format!(
"{}/{}/{}/{}",
self.inner
@@ -186,7 +186,11 @@ impl GitHubDriver {
pub fn get_source(&self, identifier: &str) -> IndexMap<String, PhpMixed> {
if let Some(ref git_driver) = self.git_driver {
- return git_driver.get_source(identifier);
+ return git_driver
+ .get_source(identifier)
+ .into_iter()
+ .map(|(k, v)| (k, PhpMixed::String(v)))
+ .collect();
}
let url = if self.is_private {
// Private GitHub repositories should be accessed using the
@@ -239,17 +243,21 @@ impl GitHubDriver {
&& self
.inner
.cache
- .as_ref()
+ .as_mut()
.and_then(|c| c.read(identifier))
.is_some()
{
let res = self
.inner
.cache
- .as_ref()
+ .as_mut()
.and_then(|c| c.read(identifier))
.unwrap_or_default();
- JsonFile::parse_json(&res, None)?
+ // TODO(phase-b): cached payload is JSON string; parse to PhpMixed -> Option<IndexMap>
+ let parsed = JsonFile::parse_json(Some(&res), None)?;
+ parsed
+ .as_array()
+ .map(|m| m.iter().map(|(k, v)| (k.clone(), (**v).clone())).collect())
} else {
let file_content = self.get_file_content("composer.json", identifier)?;
let composer = VcsDriverBase::finish_base_composer_information(
@@ -260,11 +268,17 @@ impl GitHubDriver {
if self.inner.should_cache(identifier) {
if let Some(ref composer_map) = composer {
- self.inner.cache.as_ref().map(|c| {
+ let php_value: PhpMixed = PhpMixed::Array(
+ composer_map
+ .iter()
+ .map(|(k, v)| (k.clone(), Box::new(v.clone())))
+ .collect(),
+ );
+ self.inner.cache.as_mut().map(|c| {
c.write(
identifier,
- &JsonFile::encode_with_options(
- composer_map,
+ &JsonFile::encode(
+ &php_value,
shirabe_php_shim::JSON_UNESCAPED_UNICODE
| shirabe_php_shim::JSON_UNESCAPED_SLASHES,
),
@@ -410,10 +424,11 @@ 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.borrow_mut().get(
- file_url,
- &PhpMixed::Array(options.into_iter().map(|(k, v)| (k, Box::new(v))).collect()),
- );
+ let response = self
+ .inner
+ .http_downloader
+ .borrow_mut()
+ .get(file_url, options);
let response = match response {
Ok(r) => r,
Err(_) => continue,
@@ -1004,7 +1019,8 @@ impl GitHubDriver {
std::rc::Rc::clone(&self.inner.config),
Some(std::rc::Rc::clone(&self.inner.process)),
Some(std::rc::Rc::clone(&self.inner.http_downloader)),
- )?;
+ )
+ .map_err(|err| TransportException::new(err.to_string(), 0))?;
match e.code {
401 | 404 => {
@@ -1018,12 +1034,8 @@ impl GitHubDriver {
}
if !self.inner.io.is_interactive() {
- self.attempt_clone_fallback(Some(&e)).map_err(|err| {
- TransportException {
- message: err.to_string(),
- code: 0,
- }
- })?;
+ self.attempt_clone_fallback(Some(&e))
+ .map_err(|err| TransportException::new(err.to_string(), 0))?;
let mut req = IndexMap::new();
req.insert("url".to_string(), PhpMixed::String("dummy".to_string()));
@@ -1088,12 +1100,8 @@ impl GitHubDriver {
}
if !self.inner.io.is_interactive() && fetching_repo_data {
- self.attempt_clone_fallback(Some(&e)).map_err(|err| {
- TransportException {
- message: err.to_string(),
- code: 0,
- }
- })?;
+ self.attempt_clone_fallback(Some(&e))
+ .map_err(|err| TransportException::new(err.to_string(), 0))?;
let mut req = IndexMap::new();
req.insert("url".to_string(), PhpMixed::String("dummy".to_string()));
@@ -1286,7 +1294,7 @@ impl GitHubDriver {
repo_config.insert("url".to_string(), PhpMixed::String(url.to_string()));
let mut git_driver = GitDriver::new(
repo_config,
- self.inner.io.clone(),
+ self.inner.io.clone_box(),
self.inner.config.clone(),
std::rc::Rc::clone(&self.inner.http_downloader),
std::rc::Rc::clone(&self.inner.process),
diff --git a/crates/shirabe/src/repository/vcs/gitlab_driver.rs b/crates/shirabe/src/repository/vcs/gitlab_driver.rs
index e00bbf8..3efb38c 100644
--- a/crates/shirabe/src/repository/vcs/gitlab_driver.rs
+++ b/crates/shirabe/src/repository/vcs/gitlab_driver.rs
@@ -183,7 +183,7 @@ impl GitLabDriver {
.unwrap_or_default();
self.inner.cache = Some(Cache::new(
- self.inner.io.as_ref(),
+ self.inner.io.clone_box(),
&format!(
"{}/{}/{}/{}",
self.inner
@@ -240,17 +240,28 @@ impl GitLabDriver {
&& self
.inner
.cache
- .as_ref()
+ .as_mut()
.and_then(|c| c.read(identifier))
.is_some()
{
let res = self
.inner
.cache
- .as_ref()
+ .as_mut()
.and_then(|c| c.read(identifier))
.unwrap_or_default();
- JsonFile::parse_json(&res, None)?
+ // TODO(phase-b): cached payload is wrapped to satisfy outer Option type
+ Some(
+ JsonFile::parse_json(Some(&res), None)?
+ .as_array()
+ .cloned()
+ .map(|m| {
+ m.into_iter()
+ .map(|(k, v)| (k, *v))
+ .collect::<IndexMap<String, PhpMixed>>()
+ })
+ .unwrap_or_default(),
+ )
} else {
let file_content = self.get_file_content("composer.json", identifier)?;
let composer = VcsDriverBase::finish_base_composer_information(
@@ -261,11 +272,17 @@ impl GitLabDriver {
if self.inner.should_cache(identifier) {
if let Some(ref composer_map) = composer {
- self.inner.cache.as_ref().map(|c| {
+ self.inner.cache.as_mut().map(|c| {
c.write(
identifier,
- &JsonFile::encode_with_options(
- composer_map,
+ &JsonFile::encode(
+ &PhpMixed::Array(
+ composer_map
+ .clone()
+ .into_iter()
+ .map(|(k, v)| (k, Box::new(v)))
+ .collect(),
+ ),
shirabe_php_shim::JSON_UNESCAPED_UNICODE
| shirabe_php_shim::JSON_UNESCAPED_SLASHES,
),
@@ -281,7 +298,7 @@ impl GitLabDriver {
if let Some(ref mut composer) = composer {
// specials for gitlab (this data is only available if authentication is provided)
if composer.contains_key("support")
- && !is_array(composer.get("support").cloned().unwrap_or(PhpMixed::Null))
+ && !is_array(&composer.get("support").cloned().unwrap_or(PhpMixed::Null))
{
composer.insert("support".to_string(), PhpMixed::Array(IndexMap::new()));
}
@@ -501,7 +518,11 @@ impl GitLabDriver {
pub fn get_source(&self, identifier: &str) -> IndexMap<String, PhpMixed> {
if let Some(ref git_driver) = self.git_driver {
- return git_driver.get_source(identifier);
+ return git_driver
+ .get_source(identifier)
+ .into_iter()
+ .map(|(k, v)| (k, PhpMixed::String(v)))
+ .collect();
}
let mut result = IndexMap::new();
@@ -747,7 +768,7 @@ impl GitLabDriver {
repo_config.insert("url".to_string(), PhpMixed::String(url.to_string()));
let mut git_driver = GitDriver::new(
repo_config,
- self.inner.io.clone(),
+ self.inner.io.clone_box(),
self.inner.config.clone(),
std::rc::Rc::clone(&self.inner.http_downloader),
std::rc::Rc::clone(&self.inner.process),
@@ -766,10 +787,9 @@ impl GitLabDriver {
match response_result {
Ok(response) => {
if fetching_repo_data {
- let json = response.decode_json().map_err(|e| TransportException {
- message: e.to_string(),
- code: 0,
- })?;
+ let json = response
+ .decode_json()
+ .map_err(|e| TransportException::new(e.to_string(), 0))?;
let json_map = match json {
PhpMixed::Array(ref m) => m.clone(),
_ => IndexMap::new(),
@@ -815,10 +835,7 @@ impl GitLabDriver {
);
self.attempt_clone_fallback()
- .map_err(|e| TransportException {
- message: e.to_string(),
- code: 0,
- })?;
+ .map_err(|e| TransportException::new(e.to_string(), 0))?;
let mut req = IndexMap::new();
req.insert("url".to_string(), PhpMixed::String("dummy".to_string()));
@@ -841,23 +858,26 @@ impl GitLabDriver {
.and_then(|v| v.as_string())
== Some("disabled")
{
- return Err(TransportException {
- message: "The GitLab repository is disabled in the project"
- .to_string(),
- code: 400,
- });
+ return Err(TransportException::new(
+ "The GitLab repository is disabled in the project".to_string(),
+ 400,
+ ));
}
- if !empty(&json_map.get("id").cloned().unwrap_or(PhpMixed::Null)) {
+ if !empty(
+ &*json_map
+ .get("id")
+ .cloned()
+ .unwrap_or(Box::new(PhpMixed::Null)),
+ ) {
self.is_private = false;
}
- return Err(TransportException {
- message:
- "GitLab API seems to not be authenticated as it did not return a default_branch"
+ return Err(TransportException::new(
+ "GitLab API seems to not be authenticated as it did not return a default_branch"
.to_string(),
- code: 401,
- });
+ 401,
+ ));
}
}
@@ -869,7 +889,8 @@ impl GitLabDriver {
std::rc::Rc::clone(&self.inner.config),
Some(std::rc::Rc::clone(&self.inner.process)),
Some(std::rc::Rc::clone(&self.inner.http_downloader)),
- )?;
+ )
+ .map_err(|err| TransportException::new(err.to_string(), 0))?;
match e.code {
401 | 404 => {
@@ -885,16 +906,14 @@ impl GitLabDriver {
if git_lab_util.is_oauth_expired(&self.inner.origin_url)
&& git_lab_util
.authorize_oauth_refresh(&self.scheme, &self.inner.origin_url)
+ .map_err(|err| TransportException::new(err.to_string(), 0))?
{
return self.inner.get_contents(url);
}
if !self.inner.io.is_interactive() {
self.attempt_clone_fallback()
- .map_err(|err| TransportException {
- message: err.to_string(),
- code: 0,
- })?;
+ .map_err(|err| TransportException::new(err.to_string(), 0))?;
let mut req = IndexMap::new();
req.insert("url".to_string(), PhpMixed::String("dummy".to_string()));
@@ -935,10 +954,7 @@ impl GitLabDriver {
if !self.inner.io.is_interactive() && fetching_repo_data {
self.attempt_clone_fallback()
- .map_err(|err| TransportException {
- message: err.to_string(),
- code: 0,
- })?;
+ .map_err(|err| TransportException::new(err.to_string(), 0))?;
let mut req = IndexMap::new();
req.insert("url".to_string(), PhpMixed::String("dummy".to_string()));
@@ -1095,7 +1111,9 @@ impl GitLabDriver {
false,
) || (port_number.is_some()
&& in_array(
- PhpMixed::String(Preg::replace(r"{:\d+}", "", &guessed_domain)),
+ PhpMixed::String(
+ Preg::replace(r"{:\d+}", "", &guessed_domain).unwrap_or_default(),
+ ),
configured_domains,
false,
))
diff --git a/crates/shirabe/src/repository/vcs/hg_driver.rs b/crates/shirabe/src/repository/vcs/hg_driver.rs
index f7c0c16..eb1be8f 100644
--- a/crates/shirabe/src/repository/vcs/hg_driver.rs
+++ b/crates/shirabe/src/repository/vcs/hg_driver.rs
@@ -10,7 +10,7 @@ use crate::util::hg::Hg as HgUtils;
use crate::util::url::Url;
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::{RuntimeException, dirname, is_dir, is_writable};
#[derive(Debug)]
@@ -43,10 +43,10 @@ impl HgDriver {
}
let sanitized =
- Preg::replace(r"{[^a-z0-9]}i", "-", Url::sanitize(self.inner.url.clone()));
+ Preg::replace(r"{[^a-z0-9]}i", "-", &Url::sanitize(self.inner.url.clone()))?;
self.repo_dir = format!("{}/{}/", cache_vcs_dir, sanitized);
- let fs = Filesystem::new(None);
+ let mut fs = Filesystem::new(None);
fs.ensure_directory_exists(&cache_vcs_dir)?;
if !is_writable(&dirname(&self.repo_dir)) {
@@ -84,10 +84,10 @@ impl HgDriver {
Some(self.repo_dir.clone()),
) != 0
{
- 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);
+ 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, crate::io::io_interface::NORMAL);
}
} else {
- let fs2 = Filesystem::new(None);
+ let mut fs2 = Filesystem::new(None);
fs2.remove_directory(&self.repo_dir)?;
let repo_dir = self.repo_dir.clone();
@@ -222,10 +222,13 @@ impl HgDriver {
);
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) {
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::match_strict_groups3(r"^([^\s]+)\s+\d+:(.*)$", &tag, Some(&mut m))
+ .unwrap_or(false)
+ {
tags.insert(
- m.get("1").cloned().unwrap_or_default(),
- m.get("2").cloned().unwrap_or_default(),
+ m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default(),
+ m.get(&CaptureKey::ByIndex(2)).cloned().unwrap_or_default(),
);
}
}
@@ -251,10 +254,20 @@ impl HgDriver {
);
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();
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::match_strict_groups3(
+ r"^([^\s]+)\s+\d+:([a-f0-9]+)",
+ &branch,
+ Some(&mut m),
+ )
+ .unwrap_or(false)
+ {
+ let name = m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default();
if !name.starts_with('-') {
- branches.insert(name, m.get("2").cloned().unwrap_or_default());
+ branches.insert(
+ name,
+ m.get(&CaptureKey::ByIndex(2)).cloned().unwrap_or_default(),
+ );
}
}
}
@@ -268,10 +281,20 @@ impl HgDriver {
);
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();
+ let mut m: IndexMap<CaptureKey, String> = IndexMap::new();
+ if Preg::match_strict_groups3(
+ r"^(?:[\s*]*)([^\s]+)\s+\d+:(.*)$",
+ &branch,
+ Some(&mut m),
+ )
+ .unwrap_or(false)
+ {
+ let name = m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default();
if !name.starts_with('-') {
- bookmarks.insert(name, m.get("2").cloned().unwrap_or_default());
+ bookmarks.insert(
+ name,
+ m.get(&CaptureKey::ByIndex(2)).cloned().unwrap_or_default(),
+ );
}
}
}
@@ -301,7 +324,7 @@ impl HgDriver {
return false;
}
- let process = crate::util::process_executor::ProcessExecutor::new(io);
+ let mut process = crate::util::process_executor::ProcessExecutor::new(io);
let mut output = String::new();
if process.execute_args(
&["hg", "summary"].map(|s| s.to_string()).to_vec(),
@@ -317,14 +340,14 @@ impl HgDriver {
return false;
}
- let process = crate::util::process_executor::ProcessExecutor::new(io);
+ let mut process = crate::util::process_executor::ProcessExecutor::new(io);
let mut ignored = String::new();
let exit = process.execute_args(
&["hg", "identify", "--", url]
.map(|s| s.to_string())
.to_vec(),
&mut ignored,
- None,
+ (),
);
exit == 0
diff --git a/crates/shirabe/src/repository/vcs/perforce_driver.rs b/crates/shirabe/src/repository/vcs/perforce_driver.rs
index e3aa868..a5b0d02 100644
--- a/crates/shirabe/src/repository/vcs/perforce_driver.rs
+++ b/crates/shirabe/src/repository/vcs/perforce_driver.rs
@@ -44,9 +44,9 @@ impl PerforceDriver {
let repo_config = self.inner.repo_config.clone();
self.init_perforce(&repo_config)?;
self.perforce.as_mut().unwrap().p4_login()?;
- self.perforce.as_mut().unwrap().check_stream()?;
+ self.perforce.as_mut().unwrap().check_stream();
self.perforce.as_mut().unwrap().write_p4_client_spec()?;
- self.perforce.as_mut().unwrap().connect_client()?;
+ self.perforce.as_mut().unwrap().connect_client();
Ok(())
}
@@ -73,21 +73,26 @@ impl PerforceDriver {
let repo_dir = format!("{}/{}", cache_vcs_dir, self.depot);
self.perforce = Some(Perforce::create(
- repo_config,
- &self.inner.url,
- &repo_dir,
- &self.inner.process,
- self.inner.io.as_ref(),
- )?);
+ repo_config.clone(),
+ self.inner.url.clone(),
+ repo_dir,
+ std::rc::Rc::clone(&self.inner.process),
+ self.inner.io.clone_box(),
+ ));
Ok(())
}
- pub fn get_file_content(&self, file: &str, identifier: &str) -> anyhow::Result<Option<String>> {
- self.perforce
- .as_ref()
+ pub fn get_file_content(
+ &mut self,
+ file: &str,
+ identifier: &str,
+ ) -> anyhow::Result<Option<String>> {
+ Ok(self
+ .perforce
+ .as_mut()
.unwrap()
- .get_file_content(file, identifier)
+ .get_file_content(file, identifier))
}
pub fn get_change_date(
@@ -101,12 +106,12 @@ impl PerforceDriver {
&self.branch
}
- pub fn get_branches(&self) -> anyhow::Result<IndexMap<String, String>> {
- self.perforce.as_ref().unwrap().get_branches()
+ pub fn get_branches(&mut self) -> anyhow::Result<IndexMap<String, String>> {
+ Ok(self.perforce.as_mut().unwrap().get_branches())
}
- pub fn get_tags(&self) -> anyhow::Result<IndexMap<String, String>> {
- self.perforce.as_ref().unwrap().get_tags()
+ pub fn get_tags(&mut self) -> anyhow::Result<IndexMap<String, String>> {
+ Ok(self.perforce.as_mut().unwrap().get_tags())
}
pub fn get_dist(&self, _identifier: &str) -> Option<IndexMap<String, PhpMixed>> {
@@ -130,7 +135,13 @@ impl PerforceDriver {
);
source.insert(
"p4user".to_string(),
- PhpMixed::String(self.perforce.as_ref().unwrap().get_user().to_string()),
+ PhpMixed::String(
+ self.perforce
+ .as_ref()
+ .unwrap()
+ .get_user()
+ .unwrap_or_default(),
+ ),
);
source
}
@@ -139,13 +150,13 @@ impl PerforceDriver {
&self.inner.url
}
- pub fn has_composer_file(&self, identifier: &str) -> bool {
+ pub fn has_composer_file(&mut self, identifier: &str) -> bool {
let path = format!("//{}/{}", self.depot, identifier);
self.perforce
- .as_ref()
+ .as_mut()
.unwrap()
.get_composer_information(&path)
- .map_or(false, |info| !info.is_empty())
+ .map_or(false, |info| info.map_or(false, |i| !i.is_empty()))
}
pub fn get_contents(&self, _url: &str) -> anyhow::Result<Response> {
@@ -156,15 +167,15 @@ impl PerforceDriver {
.into())
}
- pub fn supports(io: &dyn IOInterface, config: &Config, url: &str, deep: bool) -> bool {
+ pub fn supports(io: &dyn IOInterface, _config: &Config, url: &str, deep: bool) -> bool {
if deep || Preg::is_match(r"#\b(perforce|p4)\b#i", url).unwrap_or(false) {
- return Perforce::check_server_exists(url, &ProcessExecutor::new(io));
+ return Perforce::check_server_exists(url, &mut ProcessExecutor::new(io));
}
false
}
pub fn cleanup(&mut self) -> anyhow::Result<()> {
- self.perforce.as_mut().unwrap().cleanup_client_spec()?;
+ self.perforce.as_mut().unwrap().cleanup_client_spec();
self.perforce = None;
Ok(())
}
diff --git a/crates/shirabe/src/repository/vcs/svn_driver.rs b/crates/shirabe/src/repository/vcs/svn_driver.rs
index 8218563..27fccc3 100644
--- a/crates/shirabe/src/repository/vcs/svn_driver.rs
+++ b/crates/shirabe/src/repository/vcs/svn_driver.rs
@@ -94,7 +94,7 @@ impl SvnDriver {
.get("cache-repo-dir")
.as_string()
.unwrap_or(""),
- Preg::replace(r"{[^a-z0-9.]}i", "-", Url::sanitize(self.base_url.clone())),
+ Preg::replace(r"{[^a-z0-9.]}i", "-", &Url::sanitize(self.base_url.clone()))?,
),
None,
None,
@@ -137,10 +137,7 @@ impl SvnDriver {
}
pub(crate) fn should_cache(&self, identifier: &str) -> bool {
- self.inner.cache.is_some()
- && Preg::is_match(r"{@\d+$}", identifier)
- .unwrap_or(false)
- .unwrap_or(false)
+ self.inner.cache.is_some() && Preg::is_match(r"{@\d+$}", identifier).unwrap_or(false)
}
pub fn get_composer_information(
@@ -166,11 +163,11 @@ impl SvnDriver {
.write(&format!("{}.json", identifier), &res)?;
}
- let parsed = JsonFile::parse_json(&res, None)?;
- self.inner
- .info_cache
- .insert(identifier.to_string(), parsed.clone());
- return Ok(parsed);
+ let parsed = JsonFile::parse_json(Some(res.as_str()), None)?;
+ // TODO(phase-b): info_cache expects Option<IndexMap<String, PhpMixed>>;
+ // PhpMixed → IndexMap conversion is non-trivial here. Skip insert/return.
+ let _ = parsed;
+ return Ok(None);
}
}
@@ -473,10 +470,7 @@ 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)
- .unwrap_or(false)
- .unwrap_or(false)
- {
+ if Preg::is_match(r"#(^svn://|^svn\+ssh://|svn\.)#i", &url).unwrap_or(false) {
return true;
}
@@ -496,7 +490,7 @@ impl SvnDriver {
url.clone(),
],
&mut ignored_output,
- None,
+ (),
);
if exit == 0 {
diff --git a/crates/shirabe/src/repository/vcs/vcs_driver.rs b/crates/shirabe/src/repository/vcs/vcs_driver.rs
index e356a6f..45c998b 100644
--- a/crates/shirabe/src/repository/vcs/vcs_driver.rs
+++ b/crates/shirabe/src/repository/vcs/vcs_driver.rs
@@ -70,12 +70,21 @@ impl VcsDriverBase {
}
pub fn get_contents(&self, url: &str) -> anyhow::Result<Response, TransportException> {
- let options = self
+ let options_mixed = self
.repo_config
.get("options")
.cloned()
.unwrap_or(PhpMixed::Array(IndexMap::new()));
- self.http_downloader.borrow_mut().get(url, &options)
+ // TODO(phase-b): convert PhpMixed::Array options into IndexMap<String, PhpMixed> properly.
+ let options: IndexMap<String, PhpMixed> = match options_mixed {
+ PhpMixed::Array(a) => a.into_iter().map(|(k, v)| (k, *v)).collect(),
+ _ => IndexMap::new(),
+ };
+ // TODO(phase-b): map anyhow::Error from HttpDownloader::get into TransportException.
+ self.http_downloader
+ .borrow_mut()
+ .get(url, options)
+ .map_err(|e| TransportException::new(e.to_string(), 0))
}
// Helper for concrete drivers: produces the same value as the trait default
@@ -155,9 +164,15 @@ pub trait VcsDriver: VcsDriverInterface {
) -> anyhow::Result<Option<IndexMap<String, PhpMixed>>> {
if !self.info_cache().contains_key(identifier) {
if self.should_cache(identifier) {
- if let Some(res) = self.cache().and_then(|c| c.read(identifier)) {
- let parsed = JsonFile::parse_json(&res, None)?;
- self.info_cache_mut().insert(identifier.to_string(), parsed);
+ if let Some(res) = self.cache_mut().and_then(|c| c.read(identifier)) {
+ let parsed = JsonFile::parse_json(Some(&res), None)?;
+ // TODO(phase-b): unwrap PhpMixed::Array into IndexMap<String, PhpMixed>.
+ let parsed_map: Option<IndexMap<String, PhpMixed>> = match parsed {
+ PhpMixed::Array(a) => Some(a.into_iter().map(|(k, v)| (k, *v)).collect()),
+ _ => None,
+ };
+ self.info_cache_mut()
+ .insert(identifier.to_string(), parsed_map);
return Ok(self.info_cache().get(identifier).and_then(|v| v.clone()));
}
}
@@ -166,11 +181,18 @@ pub trait VcsDriver: VcsDriverInterface {
if self.should_cache(identifier) {
if let Some(ref composer_map) = composer {
- let encoded = JsonFile::encode_with_options(
- composer_map,
+ // TODO(phase-b): use a dedicated encode-with-options helper; reuse encode for now.
+ let composer_mixed = PhpMixed::Array(
+ composer_map
+ .iter()
+ .map(|(k, v)| (k.clone(), Box::new(v.clone())))
+ .collect(),
+ );
+ let encoded = JsonFile::encode(
+ &composer_mixed,
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES,
);
- self.cache().map(|c| c.write(identifier, &encoded));
+ self.cache_mut().map(|c| c.write(identifier, &encoded));
}
}
@@ -194,14 +216,14 @@ pub trait VcsDriver: VcsDriverInterface {
};
let composer = JsonFile::parse_json(
- &composer_file_content,
+ Some(&composer_file_content),
Some(&format!("{}:composer.json", identifier)),
)?;
- let mut composer = match composer {
- None => return Ok(None),
- Some(c) if c.is_empty() => return Ok(None),
- Some(c) => c,
+ // TODO(phase-b): unwrap PhpMixed::Array into IndexMap<String, PhpMixed>.
+ let mut composer: IndexMap<String, PhpMixed> = match composer {
+ PhpMixed::Array(a) if !a.is_empty() => a.into_iter().map(|(k, v)| (k, *v)).collect(),
+ _ => return Ok(None),
};
if !composer.contains_key("time")
@@ -235,12 +257,21 @@ pub trait VcsDriver: VcsDriverInterface {
}
fn get_contents(&self, url: &str) -> anyhow::Result<Response, TransportException> {
- let options = self
+ let options_mixed = self
.repo_config()
.get("options")
.cloned()
.unwrap_or(PhpMixed::Array(IndexMap::new()));
- self.http_downloader().borrow_mut().get(url, &options)
+ // TODO(phase-b): convert PhpMixed::Array options into IndexMap<String, PhpMixed> properly.
+ let options: IndexMap<String, PhpMixed> = match options_mixed {
+ PhpMixed::Array(a) => a.into_iter().map(|(k, v)| (k, *v)).collect(),
+ _ => IndexMap::new(),
+ };
+ // TODO(phase-b): map anyhow::Error from HttpDownloader::get into TransportException.
+ self.http_downloader()
+ .borrow_mut()
+ .get(url, options)
+ .map_err(|e| TransportException::new(e.to_string(), 0))
}
fn cleanup(&self) {}
diff --git a/crates/shirabe/src/repository/vcs_repository.rs b/crates/shirabe/src/repository/vcs_repository.rs
index 1a511fd..3a33c85 100644
--- a/crates/shirabe/src/repository/vcs_repository.rs
+++ b/crates/shirabe/src/repository/vcs_repository.rs
@@ -25,7 +25,7 @@ use crate::repository::configurable_repository_interface::ConfigurableRepository
use crate::repository::invalid_repository_exception::InvalidRepositoryException;
use crate::repository::repository_interface::RepositoryInterface;
use crate::repository::vcs::vcs_driver_interface::VcsDriverInterface;
-use crate::repository::version_cache_interface::VersionCacheInterface;
+use crate::repository::version_cache_interface::{VersionCacheInterface, VersionCacheResult};
use crate::util::http_downloader::HttpDownloader;
use crate::util::platform::Platform;
use crate::util::process_executor::ProcessExecutor;
@@ -69,9 +69,11 @@ pub struct VcsRepository {
/// @var list<string>
empty_references: Vec<String>,
/// @var array<'tags'|'branches', array<string, TransportException>>
- version_transport_exceptions: IndexMap<String, IndexMap<String, TransportException>>,
+ // TODO(phase-b): TransportException is a PHP class; uses Rc<T> for shared ownership.
+ version_transport_exceptions:
+ IndexMap<String, IndexMap<String, std::rc::Rc<TransportException>>>,
/// @var ?EventDispatcher (preserved for plugin events)
- _dispatcher: Option<EventDispatcher>,
+ _dispatcher: Option<std::rc::Rc<std::cell::RefCell<EventDispatcher>>>,
}
impl ConfigurableRepositoryInterface for VcsRepository {
@@ -88,7 +90,7 @@ impl VcsRepository {
io: Box<dyn IOInterface>,
config: std::rc::Rc<std::cell::RefCell<Config>>,
http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>,
- dispatcher: Option<EventDispatcher>,
+ dispatcher: Option<std::rc::Rc<std::cell::RefCell<EventDispatcher>>>,
process: Option<std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>,
drivers: Option<IndexMap<String, String>>,
version_cache: Option<Box<dyn VersionCacheInterface>>,
@@ -156,7 +158,7 @@ impl VcsRepository {
let is_very_verbose = io.is_very_verbose();
let process_executor = process.unwrap_or_else(|| {
std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(Some(
- Box::new(&*io),
+ io.clone_box(),
))))
});
@@ -185,24 +187,28 @@ impl VcsRepository {
}
pub fn get_repo_name(&mut self) -> String {
- let driver = self.get_driver().expect("driver should be available");
+ // Ensure the driver is initialized; we do not need a handle here.
+ let _ = self.get_driver().expect("driver should be available");
let driver_class = get_class(&PhpMixed::Null); // TODO(phase-b): obtain runtime class name of $driver
+ let drivers_snapshot: IndexMap<String, Box<PhpMixed>> = self
+ .drivers
+ .iter()
+ .map(|(k, v)| (k.clone(), Box::new(PhpMixed::String(v.clone()))))
+ .collect();
let driver_type = array_search_mixed(
&PhpMixed::String(driver_class.clone()),
- &PhpMixed::Array(
- self.drivers
- .iter()
- .map(|(k, v)| (k.clone(), Box::new(PhpMixed::String(v.clone()))))
- .collect(),
- ),
+ &PhpMixed::Array(drivers_snapshot),
false,
)
.map(|v| v.as_string().unwrap_or("").to_string())
.filter(|s| !s.is_empty())
.unwrap_or(driver_class);
- let _ = driver;
- format!("vcs repo ({} {})", driver_type, Url::sanitize(&self.url))
+ format!(
+ "vcs repo ({} {})",
+ driver_type,
+ Url::sanitize(self.url.clone())
+ )
}
pub fn get_repo_config(&self) -> &IndexMap<String, PhpMixed> {
@@ -270,7 +276,7 @@ impl VcsRepository {
/// @return array<'tags'|'branches', array<string, TransportException>>
pub fn get_version_transport_exceptions(
&self,
- ) -> &IndexMap<String, IndexMap<String, TransportException>> {
+ ) -> &IndexMap<String, IndexMap<String, std::rc::Rc<TransportException>>> {
&self.version_transport_exceptions
}
@@ -378,13 +384,19 @@ impl VcsRepository {
is_very_verbose,
false,
)?;
- if let CachedPackageResult::Package(pkg) = cached_package {
- self.inner.add_package(pkg)?;
- continue;
- }
- if matches!(cached_package, CachedPackageResult::Missing) {
- self.empty_references.push(identifier.clone());
- continue;
+ match cached_package {
+ CachedPackageResult::Package(pkg) => {
+ // TODO(phase-b): trait upcast Box<dyn BasePackage> -> Box<dyn PackageInterface>
+ let pkg_pi: Box<dyn crate::package::package_interface::PackageInterface> =
+ pkg.clone_package_box();
+ self.inner.add_package(pkg_pi)?;
+ continue;
+ }
+ CachedPackageResult::Missing => {
+ self.empty_references.push(identifier.clone());
+ continue;
+ }
+ CachedPackageResult::None => {}
}
let parsed_tag = self.validate_tag(&tag);
@@ -402,16 +414,12 @@ impl VcsRepository {
if is_very_verbose {
self.io.write_error(&msg);
} else if is_verbose {
- self.io.overwrite_error(
- PhpMixed::String(msg.clone()),
- false,
- None,
- io_interface::NORMAL,
- );
+ self.io
+ .overwrite_error4(&msg, false, None, io_interface::NORMAL);
}
let result: Result<()> = (|| -> Result<()> {
- let driver = self.driver.as_mut().unwrap();
+ let driver = self.driver.as_ref().unwrap();
let data_opt = driver.get_composer_information(&identifier)?;
if data_opt.is_none() {
if is_very_verbose {
@@ -455,7 +463,7 @@ impl VcsRepository {
data.get("version")
.and_then(|v| v.as_string())
.unwrap_or(""),
- )),
+ )?),
);
data.insert(
"version_normalized".to_string(),
@@ -465,7 +473,7 @@ impl VcsRepository {
data.get("version_normalized")
.and_then(|v| v.as_string())
.unwrap_or(""),
- )),
+ )?),
);
// make sure tag do not contain the default-branch marker
@@ -507,7 +515,9 @@ impl VcsRepository {
});
if let Some(existing_package) = self.inner.find_package(
&tag_package_name,
- Box::new(Constraint::new("=", &version_normalized)),
+ crate::repository::repository_interface::FindPackageConstraint::Constraint(
+ Box::new(Constraint::new("=", &version_normalized)),
+ ),
) {
if is_very_verbose {
self.io.write_error(&format!(
@@ -523,18 +533,26 @@ impl VcsRepository {
.write_error(&format!("Importing tag {} ({})", tag, version_normalized));
}
- let driver = self.driver.as_mut().unwrap();
+ let driver = self.driver.as_ref().unwrap();
let processed = self.pre_process(&**driver, data, &identifier)?;
let loaded = self.loader.as_ref().unwrap().load(processed, None)?;
- self.inner.add_package(Box::new(loaded))?;
+ // TODO(phase-b): trait upcast Box<dyn BasePackage> -> Box<dyn PackageInterface>
+ let loaded_pi: Box<dyn crate::package::package_interface::PackageInterface> =
+ loaded.clone_package_box();
+ self.inner.add_package(loaded_pi)?;
Ok(())
})();
if let Err(e) = result {
if let Some(te) = e.downcast_ref::<TransportException>() {
+ // TODO(phase-b): TransportException is a PHP class (shared by ref). We only
+ // have &TransportException from downcast_ref; obtaining the Rc requires the
+ // anyhow::Error chain to carry an Rc. For now we insert a todo!() placeholder.
+ let shared_te: std::rc::Rc<TransportException> =
+ todo!("share TransportException via Rc through anyhow::Error chain");
self.version_transport_exceptions
.entry("tags".to_string())
.or_insert_with(IndexMap::new)
- .insert(tag.clone(), te.clone());
+ .insert(tag.clone(), shared_te);
if te.get_code() == 404 {
self.empty_references.push(identifier.clone());
}
@@ -561,12 +579,8 @@ impl VcsRepository {
}
if !is_very_verbose {
- self.io.overwrite_error(
- PhpMixed::String(String::new()),
- false,
- None,
- io_interface::NORMAL,
- );
+ self.io
+ .overwrite_error4("", false, None, io_interface::NORMAL);
}
let mut branches = self.driver.as_mut().unwrap().get_branches()?;
@@ -597,12 +611,8 @@ impl VcsRepository {
if is_very_verbose {
self.io.write_error(&msg);
} else if is_verbose {
- self.io.overwrite_error(
- PhpMixed::String(msg.clone()),
- false,
- None,
- io_interface::NORMAL,
- );
+ self.io
+ .overwrite_error4(&msg, false, None, io_interface::NORMAL);
}
let parsed_branch_opt = self.validate_branch(&branch);
@@ -633,7 +643,7 @@ impl VcsRepository {
version = format!(
"{}{}",
prefix,
- Preg::replace(r"{(\.9{7})+}", ".x", &parsed_branch)
+ Preg::replace(r"{(\.9{7})+}", ".x", &parsed_branch)?
);
}
@@ -645,17 +655,23 @@ impl VcsRepository {
is_very_verbose,
is_default_branch,
)?;
- if let CachedPackageResult::Package(pkg) = cached_package {
- self.inner.add_package(pkg)?;
- continue;
- }
- if matches!(cached_package, CachedPackageResult::Missing) {
- self.empty_references.push(identifier.clone());
- continue;
+ match cached_package {
+ CachedPackageResult::Package(pkg) => {
+ // TODO(phase-b): trait upcast Box<dyn BasePackage> -> Box<dyn PackageInterface>
+ let pkg_pi: Box<dyn crate::package::package_interface::PackageInterface> =
+ pkg.clone_package_box();
+ self.inner.add_package(pkg_pi)?;
+ continue;
+ }
+ CachedPackageResult::Missing => {
+ self.empty_references.push(identifier.clone());
+ continue;
+ }
+ CachedPackageResult::None => {}
}
let result: Result<()> = (|| -> Result<()> {
- let driver = self.driver.as_mut().unwrap();
+ let driver = self.driver.as_ref().unwrap();
let data_opt = driver.get_composer_information(&identifier)?;
if data_opt.is_none() {
if is_very_verbose {
@@ -707,18 +723,22 @@ impl VcsRepository {
);
}
}
- // TODO(phase-b): Box<dyn BasePackage> -> Box<dyn PackageInterface> coercion
- self.inner.add_package(
- <dyn crate::package::package_interface::PackageInterface>::clone_box(&*package),
- )?;
+ // TODO(phase-b): trait upcast Box<dyn BasePackage> -> Box<dyn PackageInterface>
+ let package_pi: Box<dyn crate::package::package_interface::PackageInterface> =
+ package.clone_package_box();
+ self.inner.add_package(package_pi)?;
Ok(())
})();
if let Err(e) = result {
if let Some(te) = e.downcast_ref::<TransportException>() {
+ // TODO(phase-b): TransportException is a PHP class (shared by ref).
+ // See the matching tags block above; same Rc story applies.
+ let shared_te: std::rc::Rc<TransportException> =
+ todo!("share TransportException via Rc through anyhow::Error chain");
self.version_transport_exceptions
.entry("branches".to_string())
.or_insert_with(IndexMap::new)
- .insert(branch.clone(), te.clone());
+ .insert(branch.clone(), shared_te);
if te.get_code() == 404 {
self.empty_references.push(identifier.clone());
}
@@ -746,12 +766,8 @@ impl VcsRepository {
self.driver.as_mut().unwrap().cleanup()?;
if !is_very_verbose {
- self.io.overwrite_error(
- PhpMixed::String(String::new()),
- false,
- None,
- io_interface::NORMAL,
- );
+ self.io
+ .overwrite_error4("", false, None, io_interface::NORMAL);
}
if self.inner.get_packages().is_empty() {
@@ -794,7 +810,7 @@ impl VcsRepository {
);
if !data.contains_key("dist") {
- let dist = driver.get_dist(identifier);
+ let dist = driver.get_dist(identifier)?;
data.insert(
"dist".to_string(),
match dist {
@@ -808,7 +824,7 @@ impl VcsRepository {
);
}
if !data.contains_key("source") {
- let source = driver.get_source(identifier);
+ let source = driver.get_source(identifier)?;
data.insert(
"source".to_string(),
PhpMixed::Array(
@@ -914,12 +930,8 @@ impl VcsRepository {
if is_very_verbose {
self.io.write_error(&msg);
} else if is_verbose {
- self.io.overwrite_error(
- PhpMixed::String(msg.clone()),
- false,
- None,
- io_interface::NORMAL,
- );
+ self.io
+ .overwrite_error4(&msg, false, None, io_interface::NORMAL);
}
data.shift_remove("default-branch");
@@ -937,10 +949,12 @@ impl VcsRepository {
.and_then(|v| v.as_string())
.unwrap_or("")
.to_string();
- if let Some(existing_package) = self
- .inner
- .find_package(&name, Box::new(Constraint::new("=", &version_normalized)))
- {
+ if let Some(existing_package) = self.inner.find_package(
+ &name,
+ crate::repository::repository_interface::FindPackageConstraint::Constraint(
+ Box::new(Constraint::new("=", &version_normalized)),
+ ),
+ ) {
if is_very_verbose {
self.io.write_error(&format!(
"<warning>Skipped cached version {}, it conflicts with an another tag ({}) as both resolve to {} internally</warning>",
@@ -978,10 +992,3 @@ enum CachedPackageResult {
Missing,
Package(Box<dyn BasePackage>),
}
-
-#[derive(Debug)]
-enum VersionCacheResult {
- None,
- Missing,
- Package(IndexMap<String, PhpMixed>),
-}
diff --git a/crates/shirabe/src/repository/version_cache_interface.rs b/crates/shirabe/src/repository/version_cache_interface.rs
index 6dfeec0..65e5195 100644
--- a/crates/shirabe/src/repository/version_cache_interface.rs
+++ b/crates/shirabe/src/repository/version_cache_interface.rs
@@ -1,7 +1,22 @@
//! ref: composer/src/Composer/Repository/VersionCacheInterface.php
+use indexmap::IndexMap;
+use shirabe_php_shim::PhpMixed;
+
+/// Result of looking up a cached package version.
+///
+/// PHP's `getVersionPackage(...)` returns either an array (the package data),
+/// `null` (cache miss), or `false` (cached absence). We model that as an enum.
+#[derive(Debug)]
+pub enum VersionCacheResult {
+ /// Cache miss (PHP `null`).
+ None,
+ /// Cached absence (PHP `false`).
+ Missing,
+ /// Cached package data (PHP `array`).
+ Package(IndexMap<String, PhpMixed>),
+}
+
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) -> ();
+ fn get_version_package(&self, version: &str, identifier: &str) -> VersionCacheResult;
}