aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src/repository
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-08-02 08:42:55 +0900
committernsfisis <nsfisis@gmail.com>2026-08-02 08:42:55 +0900
commitb5d6d91054b9ceef95aef0819bb5c4ef7db1abdd (patch)
tree0bd12e599f48fcc63375363363416ca48766c698 /crates/shirabe/src/repository
parent3e367f78eec3521106979461fda717926717515a (diff)
downloadphp-shirabe-b5d6d91054b9ceef95aef0819bb5c4ef7db1abdd.tar.gz
php-shirabe-b5d6d91054b9ceef95aef0819bb5c4ef7db1abdd.tar.zst
php-shirabe-b5d6d91054b9ceef95aef0819bb5c4ef7db1abdd.zip
fix(repository): run lazy initialization in count/has_package
PHP's ArrayRepository::count()/hasPackage() call $this->initialize(), which late-binds to the concrete repository class and lazily loads its packages. The Rust pass-throughs skipped that: they ran ArrayRepository's stub initialize instead, returning 0/false and marking the repository initialized with an empty package list, which made ensure_initialized() skip the real initialization forever after. Take &mut self in RepositoryInterface::count/has_package so the lazy repositories (Filesystem, Platform, Composer) can guard with their real initialize, and return Result from has_package since that initialization can fail (PHP propagates the exception). InstallerInterface::is_installed and InstallationManager::is_package_installed/mark_alias_installed propagate the same way, which also resolves the TODO(phase-d) markers on Package/Path/Artifact/Vcs repositories about initialization errors being swallowed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe/src/repository')
-rw-r--r--crates/shirabe/src/repository/array_repository.rs27
-rw-r--r--crates/shirabe/src/repository/artifact_repository.rs9
-rw-r--r--crates/shirabe/src/repository/composer_repository.rs22
-rw-r--r--crates/shirabe/src/repository/composite_repository.rs10
-rw-r--r--crates/shirabe/src/repository/filesystem_repository.rs8
-rw-r--r--crates/shirabe/src/repository/filter_repository.rs4
-rw-r--r--crates/shirabe/src/repository/handle.rs6
-rw-r--r--crates/shirabe/src/repository/installed_array_repository.rs9
-rw-r--r--crates/shirabe/src/repository/installed_filesystem_repository.rs4
-rw-r--r--crates/shirabe/src/repository/installed_repository.rs4
-rw-r--r--crates/shirabe/src/repository/lock_array_repository.rs4
-rw-r--r--crates/shirabe/src/repository/package_repository.rs9
-rw-r--r--crates/shirabe/src/repository/path_repository.rs11
-rw-r--r--crates/shirabe/src/repository/platform_repository.rs8
-rw-r--r--crates/shirabe/src/repository/repository_interface.rs8
-rw-r--r--crates/shirabe/src/repository/root_package_repository.rs4
-rw-r--r--crates/shirabe/src/repository/vcs_repository.rs9
-rw-r--r--crates/shirabe/src/repository/writable_array_repository.rs9
18 files changed, 87 insertions, 78 deletions
diff --git a/crates/shirabe/src/repository/array_repository.rs b/crates/shirabe/src/repository/array_repository.rs
index 09c37766..30598b62 100644
--- a/crates/shirabe/src/repository/array_repository.rs
+++ b/crates/shirabe/src/repository/array_repository.rs
@@ -205,6 +205,16 @@ impl ArrayRepository {
*self.packages.borrow_mut() = Some(vec![]);
}
+ /// Shared body of `RepositoryInterface::count`, kept on `&self` for `get_repo_name` (PHP's
+ /// `getRepoName` also triggers lazy initialization through `count()`).
+ pub(crate) fn base_count(&self) -> usize {
+ if self.packages.borrow().is_none() {
+ self.initialize();
+ }
+
+ self.packages.borrow().as_ref().unwrap().len()
+ }
+
/// Resets the packages cache so the next access re-runs `initialize`.
pub(crate) fn reset_packages(&self) {
*self.packages.borrow_mut() = None;
@@ -217,16 +227,12 @@ impl ArrayRepository {
impl RepositoryInterface for ArrayRepository {
/// Returns the number of packages in this repository
- fn count(&self) -> anyhow::Result<usize> {
- if self.packages.borrow().is_none() {
- self.initialize();
- }
-
- Ok(self.packages.borrow().as_ref().unwrap().len())
+ fn count(&mut self) -> anyhow::Result<usize> {
+ Ok(self.base_count())
}
fn get_repo_name(&self) -> String {
- let count = self.count().expect("ArrayRepository::count is infallible");
+ let count = self.base_count();
format!(
"array repo (defining {} package{})",
count,
@@ -409,7 +415,7 @@ impl RepositoryInterface for ArrayRepository {
Ok(matches.into_values().collect())
}
- fn has_package(&self, package: PackageInterfaceHandle) -> bool {
+ fn has_package(&mut self, package: PackageInterfaceHandle) -> anyhow::Result<bool> {
if self.package_map.borrow().is_none() {
let mut map: IndexMap<String, BasePackageHandle> = IndexMap::new();
for repo_package in self.get_packages_internal() {
@@ -418,11 +424,12 @@ impl RepositoryInterface for ArrayRepository {
*self.package_map.borrow_mut() = Some(map);
}
- self.package_map
+ Ok(self
+ .package_map
.borrow()
.as_ref()
.unwrap()
- .contains_key(&package.get_unique_name())
+ .contains_key(&package.get_unique_name()))
}
fn get_providers(
diff --git a/crates/shirabe/src/repository/artifact_repository.rs b/crates/shirabe/src/repository/artifact_repository.rs
index 7d9c02b6..f18f7f04 100644
--- a/crates/shirabe/src/repository/artifact_repository.rs
+++ b/crates/shirabe/src/repository/artifact_repository.rs
@@ -247,16 +247,13 @@ impl RepositoryInterface for ArtifactRepository {
// The structural methods are inherited from ArrayRepository in PHP, where the lazy directory
// scan is driven by the overridden initialize(). Here each one first ensures that scan has
// happened (see ensure_initialized), then delegates to the inner ArrayRepository.
- fn count(&self) -> anyhow::Result<usize> {
+ fn count(&mut self) -> anyhow::Result<usize> {
self.ensure_initialized()?;
self.inner.count()
}
- fn has_package(&self, package: PackageInterfaceHandle) -> bool {
- // TODO(phase-d): hasPackage returns bool and cannot surface an initialization error; a
- // failed scan leaves the inner repository with whatever packages were added before the
- // failure.
- let _ = self.ensure_initialized();
+ fn has_package(&mut self, package: PackageInterfaceHandle) -> anyhow::Result<bool> {
+ self.ensure_initialized()?;
self.inner.has_package(package)
}
diff --git a/crates/shirabe/src/repository/composer_repository.rs b/crates/shirabe/src/repository/composer_repository.rs
index 558d7e00..ba68d22c 100644
--- a/crates/shirabe/src/repository/composer_repository.rs
+++ b/crates/shirabe/src/repository/composer_repository.rs
@@ -79,15 +79,6 @@ pub struct ProviderListingEntry {
#[derive(Debug)]
pub struct ComposerRepository {
- // TODO(phase-c): PHP's ArrayRepository methods that aren't overridden here (findPackage,
- // findPackages, count, hasPackage) call $this->getPackages(), which virtual-dispatches back
- // to ComposerRepository::getPackages() (see its "embedded inheritance does not dispatch back
- // to the wrapper" comment below, and the identical guard in load_packages()). Composition
- // doesn't get that dispatch for free: self.inner.find_package()/find_packages()/count()/
- // has_package() below call ArrayRepository::initialize() (a no-op stub) instead, and will
- // silently see an empty package list if called before get_packages()/load_packages() has
- // run once on this instance. Not yet known to be hit by any test; audit call sites and add
- // the same `if !self.inner.is_initialized() { self.initialize()?; }` guard where needed.
inner: ArrayRepository,
/// Weak reference to the outermost repository handle wrapping this `ComposerRepository`,
/// injected via `set_self_handle`. Used to wire package -> repository back-references.
@@ -3409,11 +3400,20 @@ fn clone_root_data(rd: &RootData) -> RootData {
}
impl RepositoryInterface for ComposerRepository {
- fn count(&self) -> anyhow::Result<usize> {
+ // PHP's ArrayRepository::count()/hasPackage() call $this->initialize(), which
+ // virtual-dispatches to ComposerRepository::initialize(); the guard restores that
+ // (same guard as in get_packages()).
+ fn count(&mut self) -> anyhow::Result<usize> {
+ if !self.inner.is_initialized() {
+ self.initialize()?;
+ }
self.inner.count()
}
- fn has_package(&self, package: PackageInterfaceHandle) -> bool {
+ fn has_package(&mut self, package: PackageInterfaceHandle) -> anyhow::Result<bool> {
+ if !self.inner.is_initialized() {
+ self.initialize()?;
+ }
self.inner.has_package(package)
}
diff --git a/crates/shirabe/src/repository/composite_repository.rs b/crates/shirabe/src/repository/composite_repository.rs
index 9e5c9757..a9c55a0f 100644
--- a/crates/shirabe/src/repository/composite_repository.rs
+++ b/crates/shirabe/src/repository/composite_repository.rs
@@ -60,7 +60,7 @@ impl CompositeRepository {
}
impl RepositoryInterface for CompositeRepository {
- fn count(&self) -> anyhow::Result<usize> {
+ fn count(&mut self) -> anyhow::Result<usize> {
let mut total = 0;
for repository in &self.repositories {
total += repository.count()?;
@@ -78,13 +78,13 @@ impl RepositoryInterface for CompositeRepository {
format!("composite repo ({})", names.join(", "))
}
- fn has_package(&self, package: PackageInterfaceHandle) -> bool {
+ fn has_package(&mut self, package: PackageInterfaceHandle) -> anyhow::Result<bool> {
for repository in &self.repositories {
- if repository.has_package(package.clone()) {
- return true;
+ if repository.has_package(package.clone())? {
+ return Ok(true);
}
}
- false
+ Ok(false)
}
fn find_package(
diff --git a/crates/shirabe/src/repository/filesystem_repository.rs b/crates/shirabe/src/repository/filesystem_repository.rs
index d4b9b72e..9186a836 100644
--- a/crates/shirabe/src/repository/filesystem_repository.rs
+++ b/crates/shirabe/src/repository/filesystem_repository.rs
@@ -716,11 +716,15 @@ impl FilesystemRepository {
}
impl RepositoryInterface for FilesystemRepository {
- fn count(&self) -> anyhow::Result<usize> {
+ // PHP's ArrayRepository::count()/hasPackage() call $this->initialize(), which
+ // virtual-dispatches to FilesystemRepository::initialize(); the guard restores that.
+ fn count(&mut self) -> anyhow::Result<usize> {
+ self.ensure_initialized()?;
self.inner.count()
}
- fn has_package(&self, package: PackageInterfaceHandle) -> bool {
+ fn has_package(&mut self, package: PackageInterfaceHandle) -> anyhow::Result<bool> {
+ self.ensure_initialized()?;
self.inner.has_package(package)
}
diff --git a/crates/shirabe/src/repository/filter_repository.rs b/crates/shirabe/src/repository/filter_repository.rs
index bdf17c54..32a32ba1 100644
--- a/crates/shirabe/src/repository/filter_repository.rs
+++ b/crates/shirabe/src/repository/filter_repository.rs
@@ -148,7 +148,7 @@ impl FilterRepository {
}
impl RepositoryInterface for FilterRepository {
- fn count(&self) -> anyhow::Result<usize> {
+ fn count(&mut self) -> anyhow::Result<usize> {
if self.repo.count()? > 0 {
Ok(self
.repo
@@ -161,7 +161,7 @@ impl RepositoryInterface for FilterRepository {
}
}
- fn has_package(&self, package: PackageInterfaceHandle) -> bool {
+ fn has_package(&mut self, package: PackageInterfaceHandle) -> anyhow::Result<bool> {
self.repo.has_package(package)
}
diff --git a/crates/shirabe/src/repository/handle.rs b/crates/shirabe/src/repository/handle.rs
index f093f66b..497e2b91 100644
--- a/crates/shirabe/src/repository/handle.rs
+++ b/crates/shirabe/src/repository/handle.rs
@@ -84,7 +84,7 @@ impl RepositoryInterfaceHandle {
}
pub fn count(&self) -> anyhow::Result<usize> {
- self.0.borrow().count()
+ self.0.borrow_mut().count()
}
pub fn get_repo_name(&self) -> String {
@@ -95,8 +95,8 @@ impl RepositoryInterfaceHandle {
self.0.borrow_mut().get_packages()
}
- pub fn has_package(&self, package: PackageInterfaceHandle) -> bool {
- self.0.borrow().has_package(package)
+ pub fn has_package(&self, package: PackageInterfaceHandle) -> anyhow::Result<bool> {
+ self.0.borrow_mut().has_package(package)
}
pub fn find_package(
diff --git a/crates/shirabe/src/repository/installed_array_repository.rs b/crates/shirabe/src/repository/installed_array_repository.rs
index dd0f7d38..b458ff61 100644
--- a/crates/shirabe/src/repository/installed_array_repository.rs
+++ b/crates/shirabe/src/repository/installed_array_repository.rs
@@ -39,10 +39,7 @@ impl InstalledRepositoryInterface for InstalledArrayRepository {
}
fn is_fresh(&self) -> bool {
- self.inner
- .count()
- .expect("WritableArrayRepository::count is infallible")
- == 0
+ self.inner.base_count() == 0
}
}
@@ -82,11 +79,11 @@ impl WritableRepositoryInterface for InstalledArrayRepository {
}
impl RepositoryInterface for InstalledArrayRepository {
- fn count(&self) -> anyhow::Result<usize> {
+ fn count(&mut self) -> anyhow::Result<usize> {
self.inner.count()
}
- fn has_package(&self, package: PackageInterfaceHandle) -> bool {
+ fn has_package(&mut self, package: PackageInterfaceHandle) -> anyhow::Result<bool> {
self.inner.has_package(package)
}
fn find_package(
diff --git a/crates/shirabe/src/repository/installed_filesystem_repository.rs b/crates/shirabe/src/repository/installed_filesystem_repository.rs
index 7fab762a..85fb514b 100644
--- a/crates/shirabe/src/repository/installed_filesystem_repository.rs
+++ b/crates/shirabe/src/repository/installed_filesystem_repository.rs
@@ -113,11 +113,11 @@ impl WritableRepositoryInterface for InstalledFilesystemRepository {
}
impl RepositoryInterface for InstalledFilesystemRepository {
- fn count(&self) -> anyhow::Result<usize> {
+ fn count(&mut self) -> anyhow::Result<usize> {
self.inner.count()
}
- fn has_package(&self, package: PackageInterfaceHandle) -> bool {
+ fn has_package(&mut self, package: PackageInterfaceHandle) -> anyhow::Result<bool> {
self.inner.has_package(package)
}
fn find_package(
diff --git a/crates/shirabe/src/repository/installed_repository.rs b/crates/shirabe/src/repository/installed_repository.rs
index 388670d4..08b1f36b 100644
--- a/crates/shirabe/src/repository/installed_repository.rs
+++ b/crates/shirabe/src/repository/installed_repository.rs
@@ -376,7 +376,7 @@ impl InstalledRepository {
}
impl RepositoryInterface for InstalledRepository {
- fn count(&self) -> anyhow::Result<usize> {
+ fn count(&mut self) -> anyhow::Result<usize> {
self.inner.count()
}
@@ -390,7 +390,7 @@ impl RepositoryInterface for InstalledRepository {
format!("installed repo ({})", names.join(", "))
}
- fn has_package(&self, package: PackageInterfaceHandle) -> bool {
+ fn has_package(&mut self, package: PackageInterfaceHandle) -> anyhow::Result<bool> {
self.inner.has_package(package)
}
diff --git a/crates/shirabe/src/repository/lock_array_repository.rs b/crates/shirabe/src/repository/lock_array_repository.rs
index 390fae38..8b909fbc 100644
--- a/crates/shirabe/src/repository/lock_array_repository.rs
+++ b/crates/shirabe/src/repository/lock_array_repository.rs
@@ -31,11 +31,11 @@ impl LockArrayRepository {
}
impl RepositoryInterface for LockArrayRepository {
- fn count(&self) -> anyhow::Result<usize> {
+ fn count(&mut self) -> anyhow::Result<usize> {
self.inner.count()
}
- fn has_package(&self, package: PackageInterfaceHandle) -> bool {
+ fn has_package(&mut self, package: PackageInterfaceHandle) -> anyhow::Result<bool> {
self.inner.has_package(package)
}
diff --git a/crates/shirabe/src/repository/package_repository.rs b/crates/shirabe/src/repository/package_repository.rs
index 3397f071..06b28451 100644
--- a/crates/shirabe/src/repository/package_repository.rs
+++ b/crates/shirabe/src/repository/package_repository.rs
@@ -108,16 +108,13 @@ impl RepositoryInterface for PackageRepository {
// The structural methods are inherited from ArrayRepository in PHP, where the lazy package load
// is driven by the overridden initialize(). Here each one first ensures that load has happened
// (see ensure_initialized), then delegates to the inner ArrayRepository.
- fn count(&self) -> anyhow::Result<usize> {
+ fn count(&mut self) -> anyhow::Result<usize> {
self.ensure_initialized()?;
self.inner.count()
}
- fn has_package(&self, package: PackageInterfaceHandle) -> bool {
- // TODO(phase-d): hasPackage returns bool and cannot surface an initialization error; a
- // failed load leaves the inner repository with whatever packages were added before the
- // failure.
- let _ = self.ensure_initialized();
+ fn has_package(&mut self, package: PackageInterfaceHandle) -> anyhow::Result<bool> {
+ self.ensure_initialized()?;
self.inner.has_package(package)
}
diff --git a/crates/shirabe/src/repository/path_repository.rs b/crates/shirabe/src/repository/path_repository.rs
index d7f24fa7..879b769c 100644
--- a/crates/shirabe/src/repository/path_repository.rs
+++ b/crates/shirabe/src/repository/path_repository.rs
@@ -141,7 +141,7 @@ impl PathRepository {
) -> anyhow::Result<bool> {
self.initialize()?;
use crate::repository::RepositoryInterface;
- Ok(self.inner.has_package(package))
+ self.inner.has_package(package)
}
// In PHP the inherited ArrayRepository methods lazily call the overridden initialize() to glob
@@ -397,16 +397,13 @@ impl RepositoryInterface for PathRepository {
// The structural methods are inherited from ArrayRepository in PHP, where the lazy package load
// is driven by the overridden initialize(). Here each one first ensures that load has happened
// (see ensure_initialized), then delegates to the inner ArrayRepository.
- fn count(&self) -> anyhow::Result<usize> {
+ fn count(&mut self) -> anyhow::Result<usize> {
self.ensure_initialized()?;
self.inner.count()
}
- fn has_package(&self, package: PackageInterfaceHandle) -> bool {
- // TODO(phase-d): hasPackage returns bool and cannot surface an initialization error; a
- // failed load leaves the inner repository with whatever packages were added before the
- // failure.
- let _ = self.ensure_initialized();
+ fn has_package(&mut self, package: PackageInterfaceHandle) -> anyhow::Result<bool> {
+ self.ensure_initialized()?;
self.inner.has_package(package)
}
diff --git a/crates/shirabe/src/repository/platform_repository.rs b/crates/shirabe/src/repository/platform_repository.rs
index e4d93d3b..30164237 100644
--- a/crates/shirabe/src/repository/platform_repository.rs
+++ b/crates/shirabe/src/repository/platform_repository.rs
@@ -1866,11 +1866,15 @@ impl PlatformRepository {
}
impl crate::repository::RepositoryInterface for PlatformRepository {
- fn count(&self) -> anyhow::Result<usize> {
+ // PHP's ArrayRepository::count()/hasPackage() call $this->initialize(), which
+ // virtual-dispatches to PlatformRepository::initialize(); the guard restores that.
+ fn count(&mut self) -> anyhow::Result<usize> {
+ self.ensure_initialized()?;
self.inner.count()
}
- fn has_package(&self, package: PackageInterfaceHandle) -> bool {
+ fn has_package(&mut self, package: PackageInterfaceHandle) -> anyhow::Result<bool> {
+ self.ensure_initialized()?;
self.inner.has_package(package)
}
diff --git a/crates/shirabe/src/repository/repository_interface.rs b/crates/shirabe/src/repository/repository_interface.rs
index 6dbc01da..ab25242c 100644
--- a/crates/shirabe/src/repository/repository_interface.rs
+++ b/crates/shirabe/src/repository/repository_interface.rs
@@ -52,9 +52,13 @@ pub const SEARCH_NAME: i64 = 1;
pub const SEARCH_VENDOR: i64 = 2;
pub trait RepositoryInterface: std::fmt::Debug {
- fn count(&self) -> anyhow::Result<usize>;
+ // count/has_package take &mut self (and has_package returns Result) because PHP's
+ // ArrayRepository::count()/hasPackage() late-bind $this->initialize() to the concrete
+ // repository class, which lazily loads packages and can throw; lazy repositories need the
+ // same guard here (see FilesystemRepository/PlatformRepository/ComposerRepository).
+ fn count(&mut self) -> anyhow::Result<usize>;
- fn has_package(&self, package: PackageInterfaceHandle) -> bool;
+ fn has_package(&mut self, package: PackageInterfaceHandle) -> anyhow::Result<bool>;
fn find_package(
&mut self,
diff --git a/crates/shirabe/src/repository/root_package_repository.rs b/crates/shirabe/src/repository/root_package_repository.rs
index 4357ed69..7630f6c5 100644
--- a/crates/shirabe/src/repository/root_package_repository.rs
+++ b/crates/shirabe/src/repository/root_package_repository.rs
@@ -26,11 +26,11 @@ impl RootPackageRepository {
}
impl RepositoryInterface for RootPackageRepository {
- fn count(&self) -> anyhow::Result<usize> {
+ fn count(&mut self) -> anyhow::Result<usize> {
self.inner.count()
}
- fn has_package(&self, package: PackageInterfaceHandle) -> bool {
+ fn has_package(&mut self, package: PackageInterfaceHandle) -> anyhow::Result<bool> {
self.inner.has_package(package)
}
diff --git a/crates/shirabe/src/repository/vcs_repository.rs b/crates/shirabe/src/repository/vcs_repository.rs
index c04b1b67..b7ce1a6c 100644
--- a/crates/shirabe/src/repository/vcs_repository.rs
+++ b/crates/shirabe/src/repository/vcs_repository.rs
@@ -1053,16 +1053,13 @@ impl RepositoryInterface for VcsRepository {
// The structural methods are inherited from ArrayRepository in PHP, where the lazy package load
// is driven by the overridden initialize(). Here each one first ensures that load has happened
// (see ensure_initialized), then delegates to the inner ArrayRepository.
- fn count(&self) -> anyhow::Result<usize> {
+ fn count(&mut self) -> anyhow::Result<usize> {
self.ensure_initialized()?;
self.inner.count()
}
- fn has_package(&self, package: PackageInterfaceHandle) -> bool {
- // TODO(phase-d): hasPackage returns bool and cannot surface an initialization error; a
- // failed load leaves the inner repository with whatever packages were added before the
- // failure.
- let _ = self.ensure_initialized();
+ fn has_package(&mut self, package: PackageInterfaceHandle) -> anyhow::Result<bool> {
+ self.ensure_initialized()?;
self.inner.has_package(package)
}
diff --git a/crates/shirabe/src/repository/writable_array_repository.rs b/crates/shirabe/src/repository/writable_array_repository.rs
index e415a1e1..f5db2bd9 100644
--- a/crates/shirabe/src/repository/writable_array_repository.rs
+++ b/crates/shirabe/src/repository/writable_array_repository.rs
@@ -31,6 +31,11 @@ impl WritableArrayRepository {
self.dev_mode
}
+ /// See `ArrayRepository::base_count`; kept on `&self` for `is_fresh` callers.
+ pub(crate) fn base_count(&self) -> usize {
+ self.inner.base_count()
+ }
+
pub fn set_dev_package_names(&mut self, dev_package_names: Vec<String>) {
self.dev_package_names = dev_package_names;
}
@@ -124,11 +129,11 @@ impl WritableArrayRepository {
}
impl RepositoryInterface for WritableArrayRepository {
- fn count(&self) -> anyhow::Result<usize> {
+ fn count(&mut self) -> anyhow::Result<usize> {
self.inner.count()
}
- fn has_package(&self, package: PackageInterfaceHandle) -> bool {
+ fn has_package(&mut self, package: PackageInterfaceHandle) -> anyhow::Result<bool> {
self.inner.has_package(package)
}