aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src/repository
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-05-28 22:43:11 +0900
committernsfisis <nsfisis@gmail.com>2026-05-28 22:43:43 +0900
commiteea4efe87e455742ec17881ee93d8095925e8516 (patch)
tree6d242f4fdd0bf32f0494a6fbbd62bce9ed6e1dc7 /crates/shirabe/src/repository
parentcc5d73c05a0abca2eebcc8a6afa0b1543ee49850 (diff)
downloadphp-shirabe-eea4efe87e455742ec17881ee93d8095925e8516.tar.gz
php-shirabe-eea4efe87e455742ec17881ee93d8095925e8516.tar.zst
php-shirabe-eea4efe87e455742ec17881ee93d8095925e8516.zip
refactor(repository): introduce Rc<RefCell<_>> handles for repositories
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/array_repository.rs43
-rw-r--r--crates/shirabe/src/repository/composer_repository.rs39
-rw-r--r--crates/shirabe/src/repository/composite_repository.rs26
-rw-r--r--crates/shirabe/src/repository/filesystem_repository.rs4
-rw-r--r--crates/shirabe/src/repository/filter_repository.rs25
-rw-r--r--crates/shirabe/src/repository/handle.rs174
-rw-r--r--crates/shirabe/src/repository/installed_array_repository.rs11
-rw-r--r--crates/shirabe/src/repository/installed_filesystem_repository.rs11
-rw-r--r--crates/shirabe/src/repository/installed_repository.rs23
-rw-r--r--crates/shirabe/src/repository/lock_array_repository.rs7
-rw-r--r--crates/shirabe/src/repository/mod.rs2
-rw-r--r--crates/shirabe/src/repository/platform_repository.rs4
-rw-r--r--crates/shirabe/src/repository/repository_factory.rs20
-rw-r--r--crates/shirabe/src/repository/repository_interface.rs15
-rw-r--r--crates/shirabe/src/repository/repository_manager.rs32
-rw-r--r--crates/shirabe/src/repository/repository_set.rs45
-rw-r--r--crates/shirabe/src/repository/repository_utils.rs32
-rw-r--r--crates/shirabe/src/repository/root_package_repository.rs5
-rw-r--r--crates/shirabe/src/repository/writable_array_repository.rs5
19 files changed, 423 insertions, 100 deletions
diff --git a/crates/shirabe/src/repository/array_repository.rs b/crates/shirabe/src/repository/array_repository.rs
index 6467c14..3a05513 100644
--- a/crates/shirabe/src/repository/array_repository.rs
+++ b/crates/shirabe/src/repository/array_repository.rs
@@ -2,6 +2,7 @@
use std::any::Any;
use std::cell::RefCell;
+use std::rc::Weak;
use anyhow::Result;
use indexmap::IndexMap;
@@ -17,7 +18,7 @@ use crate::package::version::StabilityFilter;
use crate::package::version::VersionParser;
use crate::repository::{
AbandonedInfo, FindPackageConstraint, LoadPackagesResult, ProviderInfo, RepositoryInterface,
- SearchResult,
+ RepositoryInterfaceHandle, RepositoryInterfaceWeakHandle, SearchResult,
};
/// A repository implementation that simply stores packages in an array
@@ -29,6 +30,10 @@ pub struct ArrayRepository {
/// @var ?array<BasePackage> indexed by package unique name and used to cache hasPackage calls
pub(crate) package_map: RefCell<Option<IndexMap<String, BasePackageHandle>>>,
+
+ /// Weak reference to the outermost repository handle wrapping this `ArrayRepository`,
+ /// injected via `set_self_handle`. Used to wire package -> repository back-references.
+ self_weak: RefCell<Option<RepositoryInterfaceWeakHandle>>,
}
impl ArrayRepository {
@@ -37,6 +42,7 @@ impl ArrayRepository {
let this = Self {
packages: RefCell::new(None),
package_map: RefCell::new(None),
+ self_weak: RefCell::new(None),
};
for package in packages {
this.add_package(package)?;
@@ -49,8 +55,12 @@ impl ArrayRepository {
if self.packages.borrow().is_none() {
self.initialize();
}
- // TODO(phase-b): pass a reference to self, not a clone
- package.set_repository(todo!("self as Box<dyn RepositoryInterface>"))?;
+ // PHP: $package->setRepository($this). The back-reference is wired only once this
+ // repository (or its outermost wrapper) has been put behind a RepositoryInterfaceHandle;
+ // packages added during construction are wired later in set_self_handle.
+ if let Some(handle) = self.self_weak.borrow().as_ref().and_then(Weak::upgrade) {
+ package.set_repository(RepositoryInterfaceHandle::from_rc(handle))?;
+ }
let aliased_package: Option<PackageHandle> =
package.as_alias().map(|alias| alias.get_alias_of());
@@ -59,13 +69,13 @@ impl ArrayRepository {
.borrow_mut()
.as_mut()
.unwrap()
- .push(package.into());
+ .push(package.clone());
if let Some(aliased_package) = aliased_package {
// PHP: if ($aliasedPackage->getRepository() === null) $this->addPackage($aliasedPackage);
- // TODO(phase-c): the handle does not expose get_repository (a `RefCell`-borrowed
- // back-reference); this needs repository back-references on handles.
- let _ = aliased_package;
+ if aliased_package.get_repository().is_none() {
+ self.add_package(aliased_package.into())?;
+ }
}
// invalidate package map cache
@@ -438,4 +448,23 @@ impl RepositoryInterface for ArrayRepository {
fn as_any(&self) -> &dyn std::any::Any {
self
}
+
+ fn set_self_handle(&self, weak: RepositoryInterfaceWeakHandle) {
+ *self.self_weak.borrow_mut() = Some(weak.clone());
+ // Wire back-references for packages added before this repository was wrapped in a handle
+ // (e.g. those passed to ArrayRepository::new). Reads the field directly to avoid
+ // triggering lazy initialization in wrapper repositories.
+ if let Some(handle) = weak.upgrade() {
+ let handle = RepositoryInterfaceHandle::from_rc(handle);
+ let pkgs: Vec<BasePackageHandle> = self
+ .packages
+ .borrow()
+ .as_ref()
+ .map(|v| v.clone())
+ .unwrap_or_default();
+ for pkg in pkgs {
+ let _ = pkg.set_repository(handle.clone());
+ }
+ }
+ }
}
diff --git a/crates/shirabe/src/repository/composer_repository.rs b/crates/shirabe/src/repository/composer_repository.rs
index ddf8e31..9931a73 100644
--- a/crates/shirabe/src/repository/composer_repository.rs
+++ b/crates/shirabe/src/repository/composer_repository.rs
@@ -38,6 +38,8 @@ use crate::repository::ConfigurableRepositoryInterface;
use crate::repository::LoadPackagesResult;
use crate::repository::PlatformRepository;
use crate::repository::RepositoryInterface;
+use crate::repository::RepositoryInterfaceHandle;
+use crate::repository::RepositoryInterfaceWeakHandle;
use crate::repository::RepositorySecurityException;
use crate::repository::{PartialOrSecurityAdvisory, SecurityAdvisoryResult};
use crate::repository::{SEARCH_FULLTEXT, SEARCH_VENDOR};
@@ -78,6 +80,9 @@ pub struct ProviderListingEntry {
#[derive(Debug)]
pub struct ComposerRepository {
inner: ArrayRepository,
+ /// Weak reference to the outermost repository handle wrapping this `ComposerRepository`,
+ /// injected via `set_self_handle`. Used to wire package -> repository back-references.
+ self_weak: std::cell::RefCell<Option<RepositoryInterfaceWeakHandle>>,
/// @phpstan-var array{url: string, options?: mixed[], type?: 'composer', allow_ssl_downgrade?: bool}
repo_config: IndexMap<String, PhpMixed>,
options: IndexMap<String, PhpMixed>,
@@ -274,6 +279,7 @@ impl ComposerRepository {
let mut this = Self {
inner: inner?,
+ self_weak: std::cell::RefCell::new(None),
repo_config,
options,
url,
@@ -1745,13 +1751,20 @@ impl ComposerRepository {
versions_to_load.values().cloned().collect();
let loaded_packages = self.create_packages_flat(versions_to_load_vec, packages_source)?;
let uids: Vec<String> = versions_to_load.keys().cloned().collect();
+ let self_handle = self.self_handle();
for (index, package) in loaded_packages.into_iter().enumerate() {
- // TODO(phase-c): wire the repository back-reference onto the shared package handle.
+ if let Some(h) = self_handle.as_ref() {
+ package.set_repository(h.clone())?;
+ }
let uid = &uids[index];
if let Some(alias) = package.as_alias() {
let aliased = alias.get_alias_of();
+ if let Some(h) = self_handle.as_ref() {
+ let aliased_pkg: PackageInterfaceHandle = aliased.clone().into();
+ aliased_pkg.set_repository(h.clone())?;
+ }
result.insert(uid.clone(), aliased.into());
result.insert(format!("{}-alias", uid), package);
} else {
@@ -1784,6 +1797,21 @@ impl ComposerRepository {
self.inner.add_package(package.into());
}
+ /// Forwards the outermost handle's weak to the inner `ArrayRepository` so that packages added
+ /// through `add_package` get the back-reference to this `ComposerRepository` wrapper.
+ pub fn set_self_handle(&self, weak: RepositoryInterfaceWeakHandle) {
+ *self.self_weak.borrow_mut() = Some(weak.clone());
+ self.inner.set_self_handle(weak);
+ }
+
+ fn self_handle(&self) -> Option<RepositoryInterfaceHandle> {
+ self.self_weak
+ .borrow()
+ .as_ref()
+ .and_then(std::rc::Weak::upgrade)
+ .map(RepositoryInterfaceHandle::from_rc)
+ }
+
/// @param packageNames array of package name => ConstraintInterface|null - if a constraint is provided, only packages matching it will be loaded
fn load_async_packages(
&mut self,
@@ -1976,11 +2004,18 @@ impl ComposerRepository {
let loaded_packages: Vec<BasePackageHandle> =
ComposerRepository::create_packages_static(versions_to_load, packages_source)?;
+ let self_handle = self.self_handle();
for package in loaded_packages.into_iter() {
- // TODO(phase-c): wire the repository back-reference onto the shared package handle.
+ if let Some(h) = self_handle.as_ref() {
+ package.set_repository(h.clone())?;
+ }
let hash_c = package.ptr_id().to_string();
if let Some(alias) = package.as_alias() {
let aliased = alias.get_alias_of();
+ if let Some(h) = self_handle.as_ref() {
+ let aliased_pkg: PackageInterfaceHandle = aliased.clone().into();
+ aliased_pkg.set_repository(h.clone())?;
+ }
let aliased_hash = aliased.ptr_id().to_string();
if !packages.contains_key(&aliased_hash) {
packages.insert(aliased_hash, aliased.into());
diff --git a/crates/shirabe/src/repository/composite_repository.rs b/crates/shirabe/src/repository/composite_repository.rs
index 12f1837..6c167f2 100644
--- a/crates/shirabe/src/repository/composite_repository.rs
+++ b/crates/shirabe/src/repository/composite_repository.rs
@@ -8,16 +8,17 @@ use shirabe_semver::constraint::AnyConstraint;
use crate::package::BasePackageHandle;
use crate::package::PackageInterfaceHandle;
use crate::repository::{
- FindPackageConstraint, LoadPackagesResult, ProviderInfo, RepositoryInterface, SearchResult,
+ FindPackageConstraint, LoadPackagesResult, ProviderInfo, RepositoryInterface,
+ RepositoryInterfaceHandle, SearchResult,
};
#[derive(Debug)]
pub struct CompositeRepository {
- repositories: Vec<Box<dyn RepositoryInterface>>,
+ repositories: Vec<RepositoryInterfaceHandle>,
}
impl CompositeRepository {
- pub fn new(repositories: Vec<Box<dyn RepositoryInterface>>) -> Self {
+ pub fn new(repositories: Vec<RepositoryInterfaceHandle>) -> Self {
let mut this = Self {
repositories: vec![],
};
@@ -27,7 +28,7 @@ impl CompositeRepository {
this
}
- pub fn get_repositories(&self) -> &Vec<Box<dyn RepositoryInterface>> {
+ pub fn get_repositories(&self) -> &Vec<RepositoryInterfaceHandle> {
&self.repositories
}
@@ -39,10 +40,17 @@ impl CompositeRepository {
}
}
- pub fn add_repository(&mut self, repository: Box<dyn RepositoryInterface>) {
- if let Some(composite) = repository.as_any().downcast_ref::<CompositeRepository>() {
- for repo in composite.get_repositories() {
- self.repositories.push(repo.clone_box());
+ pub fn add_repository(&mut self, repository: RepositoryInterfaceHandle) {
+ let nested: Option<Vec<RepositoryInterfaceHandle>> = {
+ let repo_ref = repository.borrow();
+ repo_ref
+ .as_any()
+ .downcast_ref::<CompositeRepository>()
+ .map(|composite| composite.get_repositories().clone())
+ };
+ if let Some(nested) = nested {
+ for repo in nested {
+ self.repositories.push(repo);
}
} else {
self.repositories.push(repository);
@@ -163,6 +171,6 @@ impl RepositoryInterface for CompositeRepository {
}
fn as_any(&self) -> &dyn std::any::Any {
- todo!()
+ self
}
}
diff --git a/crates/shirabe/src/repository/filesystem_repository.rs b/crates/shirabe/src/repository/filesystem_repository.rs
index 8232ecf..a721b4f 100644
--- a/crates/shirabe/src/repository/filesystem_repository.rs
+++ b/crates/shirabe/src/repository/filesystem_repository.rs
@@ -78,6 +78,10 @@ impl FilesystemRepository {
self.dev_mode
}
+ pub fn set_self_handle(&self, weak: crate::repository::RepositoryInterfaceWeakHandle) {
+ self.inner.set_self_handle(weak);
+ }
+
pub fn get_repo_name(&self) -> String {
format!("file ({})", self.file.get_path())
}
diff --git a/crates/shirabe/src/repository/filter_repository.rs b/crates/shirabe/src/repository/filter_repository.rs
index 2a339f5..ecf1f0f 100644
--- a/crates/shirabe/src/repository/filter_repository.rs
+++ b/crates/shirabe/src/repository/filter_repository.rs
@@ -5,7 +5,8 @@ use crate::package::PackageInterfaceHandle;
use crate::package::base_package::{self};
use crate::repository::{AdvisoryProviderInterface, SecurityAdvisoryResult};
use crate::repository::{
- FindPackageConstraint, LoadPackagesResult, ProviderInfo, RepositoryInterface, SearchResult,
+ FindPackageConstraint, LoadPackagesResult, ProviderInfo, RepositoryInterface,
+ RepositoryInterfaceHandle, SearchResult,
};
use anyhow::Result;
use indexmap::IndexMap;
@@ -18,12 +19,12 @@ pub struct FilterRepository {
only: Option<String>,
exclude: Option<String>,
canonical: bool,
- repo: Box<dyn RepositoryInterface>,
+ repo: RepositoryInterfaceHandle,
}
impl FilterRepository {
pub fn new(
- repo: Box<dyn RepositoryInterface>,
+ repo: RepositoryInterfaceHandle,
options: IndexMap<String, PhpMixed>,
) -> Result<Self> {
let mut only: Option<String> = None;
@@ -126,8 +127,8 @@ impl FilterRepository {
})
}
- pub fn get_repository(&self) -> &dyn RepositoryInterface {
- self.repo.as_ref()
+ pub fn get_repository(&self) -> RepositoryInterfaceHandle {
+ self.repo.clone()
}
fn is_allowed(&self, name: &str) -> bool {
@@ -254,7 +255,13 @@ impl RepositoryInterface for FilterRepository {
}
fn as_advisory_provider(&self) -> Option<&dyn AdvisoryProviderInterface> {
- self.repo.as_advisory_provider()
+ // FilterRepository is itself an advisory provider (it filters), but only meaningfully so
+ // when the wrapped repository provides advisories.
+ if self.repo.borrow().as_advisory_provider().is_some() {
+ Some(self)
+ } else {
+ None
+ }
}
fn as_any(&self) -> &dyn std::any::Any {
@@ -264,7 +271,8 @@ impl RepositoryInterface for FilterRepository {
impl AdvisoryProviderInterface for FilterRepository {
fn has_security_advisories(&self) -> bool {
- if let Some(advisory_repo) = self.repo.as_advisory_provider() {
+ let repo = self.repo.borrow();
+ if let Some(advisory_repo) = repo.as_advisory_provider() {
advisory_repo.has_security_advisories()
} else {
false
@@ -276,7 +284,8 @@ impl AdvisoryProviderInterface for FilterRepository {
mut package_constraint_map: IndexMap<String, AnyConstraint>,
allow_partial_advisories: bool,
) -> anyhow::Result<SecurityAdvisoryResult> {
- if let Some(advisory_repo) = self.repo.as_advisory_provider() {
+ let repo = self.repo.borrow();
+ if let Some(advisory_repo) = repo.as_advisory_provider() {
package_constraint_map.retain(|name, _| self.is_allowed(name));
advisory_repo.get_security_advisories(package_constraint_map, allow_partial_advisories)
} else {
diff --git a/crates/shirabe/src/repository/handle.rs b/crates/shirabe/src/repository/handle.rs
new file mode 100644
index 0000000..f7a4104
--- /dev/null
+++ b/crates/shirabe/src/repository/handle.rs
@@ -0,0 +1,174 @@
+//! Shared handle over `RepositoryInterface`.
+
+use std::cell::{Ref, RefCell, RefMut};
+use std::rc::{Rc, Weak};
+
+use indexmap::IndexMap;
+use shirabe_php_shim::Countable;
+use shirabe_semver::constraint::AnyConstraint;
+
+use crate::package::BasePackageHandle;
+use crate::package::PackageInterfaceHandle;
+use crate::repository::{
+ FindPackageConstraint, InstalledRepositoryInterface, LoadPackagesResult, ProviderInfo,
+ RepositoryInterface, SearchResult, WritableRepositoryInterface,
+};
+
+/// Shared reference to a repository. Corresponds to PHP `RepositoryInterface`.
+#[derive(Debug, Clone)]
+pub struct RepositoryInterfaceHandle(Rc<RefCell<dyn RepositoryInterface>>);
+
+/// Weak back-reference held by packages to the repository that owns them.
+pub type RepositoryInterfaceWeakHandle = Weak<RefCell<dyn RepositoryInterface>>;
+
+impl RepositoryInterfaceHandle {
+ /// Wraps a concrete repository in a shared handle and injects its own weak reference so that
+ /// `add_package` can wire package -> repository back-references (PHP `setRepository($this)`).
+ pub fn new<T: RepositoryInterface + 'static>(repository: T) -> Self {
+ let rc: Rc<RefCell<dyn RepositoryInterface>> = Rc::new(RefCell::new(repository));
+ rc.borrow().set_self_handle(Rc::downgrade(&rc));
+ Self(rc)
+ }
+
+ pub fn from_rc(rc: Rc<RefCell<dyn RepositoryInterface>>) -> Self {
+ Self(rc)
+ }
+
+ pub fn as_rc(&self) -> &Rc<RefCell<dyn RepositoryInterface>> {
+ &self.0
+ }
+
+ pub fn downgrade(&self) -> RepositoryInterfaceWeakHandle {
+ Rc::downgrade(&self.0)
+ }
+
+ pub fn borrow(&self) -> Ref<'_, dyn RepositoryInterface> {
+ self.0.borrow()
+ }
+
+ pub fn borrow_mut(&self) -> RefMut<'_, dyn RepositoryInterface> {
+ self.0.borrow_mut()
+ }
+
+ /// PHP `===` (reference identity).
+ pub fn ptr_eq(&self, other: &Self) -> bool {
+ Rc::ptr_eq(&self.0, &other.0)
+ }
+
+ /// Stable identity usable as a map key (PHP `spl_object_hash`).
+ pub fn ptr_id(&self) -> usize {
+ Rc::as_ptr(&self.0) as *const () as usize
+ }
+
+ /// PHP `instanceof T` for a concrete repository type. Keeps the `RefCell` borrow internal.
+ pub fn is<T: RepositoryInterface + 'static>(&self) -> bool {
+ self.0.borrow().as_any().is::<T>()
+ }
+
+ pub fn count(&self) -> i64 {
+ self.0.borrow().count()
+ }
+
+ pub fn get_repo_name(&self) -> String {
+ self.0.borrow().get_repo_name()
+ }
+
+ pub fn get_packages(&self) -> Vec<BasePackageHandle> {
+ self.0.borrow().get_packages()
+ }
+
+ pub fn has_package(&self, package: PackageInterfaceHandle) -> bool {
+ self.0.borrow().has_package(package)
+ }
+
+ pub fn find_package(
+ &self,
+ name: &str,
+ constraint: FindPackageConstraint,
+ ) -> Option<BasePackageHandle> {
+ self.0.borrow().find_package(name, constraint)
+ }
+
+ pub fn find_packages(
+ &self,
+ name: &str,
+ constraint: Option<FindPackageConstraint>,
+ ) -> Vec<BasePackageHandle> {
+ self.0.borrow().find_packages(name, constraint)
+ }
+
+ pub fn load_packages(
+ &self,
+ package_name_map: IndexMap<String, Option<AnyConstraint>>,
+ acceptable_stabilities: IndexMap<String, i64>,
+ stability_flags: IndexMap<String, i64>,
+ already_loaded: IndexMap<String, IndexMap<String, PackageInterfaceHandle>>,
+ ) -> LoadPackagesResult {
+ self.0.borrow().load_packages(
+ package_name_map,
+ acceptable_stabilities,
+ stability_flags,
+ already_loaded,
+ )
+ }
+
+ pub fn search(&self, query: String, mode: i64, r#type: Option<String>) -> Vec<SearchResult> {
+ self.0.borrow().search(query, mode, r#type)
+ }
+
+ pub fn get_providers(&self, package_name: String) -> IndexMap<String, ProviderInfo> {
+ self.0.borrow().get_providers(package_name)
+ }
+
+ // --- InstalledRepositoryInterface helpers (valid only when the wrapped repository is one) ---
+
+ pub fn is_fresh(&self) -> bool {
+ self.0
+ .borrow()
+ .as_installed_repository_interface()
+ .map_or(false, |r| r.is_fresh())
+ }
+
+ pub fn get_dev_mode(&self) -> Option<bool> {
+ self.0
+ .borrow()
+ .as_installed_repository_interface()
+ .and_then(|r| r.get_dev_mode())
+ }
+
+ pub fn get_canonical_packages(&self) -> Vec<PackageInterfaceHandle> {
+ self.0
+ .borrow()
+ .as_installed_repository_interface()
+ .map(|r| r.get_canonical_packages())
+ .unwrap_or_default()
+ }
+
+ pub fn get_dev_package_names(&self) -> Vec<String> {
+ self.0
+ .borrow()
+ .as_installed_repository_interface()
+ .map(|r| r.get_dev_package_names().clone())
+ .unwrap_or_default()
+ }
+
+ pub fn set_dev_package_names(&self, dev_package_names: Vec<String>) {
+ if let Some(r) = self.0.borrow_mut().as_installed_repository_interface_mut() {
+ r.set_dev_package_names(dev_package_names);
+ }
+ }
+}
+
+impl PartialEq for RepositoryInterfaceHandle {
+ fn eq(&self, other: &Self) -> bool {
+ Rc::ptr_eq(&self.0, &other.0)
+ }
+}
+
+impl Eq for RepositoryInterfaceHandle {}
+
+impl std::hash::Hash for RepositoryInterfaceHandle {
+ fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+ self.ptr_id().hash(state);
+ }
+}
diff --git a/crates/shirabe/src/repository/installed_array_repository.rs b/crates/shirabe/src/repository/installed_array_repository.rs
index ca2d8c7..1520497 100644
--- a/crates/shirabe/src/repository/installed_array_repository.rs
+++ b/crates/shirabe/src/repository/installed_array_repository.rs
@@ -133,7 +133,18 @@ impl RepositoryInterface for InstalledArrayRepository {
fn as_advisory_provider(&self) -> Option<&dyn AdvisoryProviderInterface> {
None
}
+ fn as_installed_repository_interface(&self) -> Option<&dyn InstalledRepositoryInterface> {
+ Some(self)
+ }
+ fn as_installed_repository_interface_mut(
+ &mut self,
+ ) -> Option<&mut dyn InstalledRepositoryInterface> {
+ Some(self)
+ }
fn as_any(&self) -> &dyn std::any::Any {
todo!()
}
+ fn set_self_handle(&self, weak: crate::repository::RepositoryInterfaceWeakHandle) {
+ self.inner.set_self_handle(weak);
+ }
}
diff --git a/crates/shirabe/src/repository/installed_filesystem_repository.rs b/crates/shirabe/src/repository/installed_filesystem_repository.rs
index 352d86f..73a9f1a 100644
--- a/crates/shirabe/src/repository/installed_filesystem_repository.rs
+++ b/crates/shirabe/src/repository/installed_filesystem_repository.rs
@@ -143,7 +143,18 @@ impl RepositoryInterface for InstalledFilesystemRepository {
fn as_advisory_provider(&self) -> Option<&dyn AdvisoryProviderInterface> {
None
}
+ fn as_installed_repository_interface(&self) -> Option<&dyn InstalledRepositoryInterface> {
+ Some(self)
+ }
+ fn as_installed_repository_interface_mut(
+ &mut self,
+ ) -> Option<&mut dyn InstalledRepositoryInterface> {
+ Some(self)
+ }
fn as_any(&self) -> &dyn std::any::Any {
todo!()
}
+ fn set_self_handle(&self, weak: crate::repository::RepositoryInterfaceWeakHandle) {
+ self.inner.set_self_handle(weak);
+ }
}
diff --git a/crates/shirabe/src/repository/installed_repository.rs b/crates/shirabe/src/repository/installed_repository.rs
index 486d4bd..b857bc2 100644
--- a/crates/shirabe/src/repository/installed_repository.rs
+++ b/crates/shirabe/src/repository/installed_repository.rs
@@ -16,7 +16,8 @@ use crate::repository::LockArrayRepository;
use crate::repository::PlatformRepository;
use crate::repository::RootPackageRepository;
use crate::repository::{
- FindPackageConstraint, LoadPackagesResult, ProviderInfo, RepositoryInterface, SearchResult,
+ FindPackageConstraint, LoadPackagesResult, ProviderInfo, RepositoryInterface,
+ RepositoryInterfaceHandle, SearchResult,
};
pub enum NeedleInput {
@@ -36,7 +37,7 @@ pub struct InstalledRepository {
}
impl InstalledRepository {
- pub fn new(repositories: Vec<Box<dyn RepositoryInterface>>) -> Self {
+ pub fn new(repositories: Vec<RepositoryInterfaceHandle>) -> Self {
let mut this = Self {
inner: CompositeRepository::new(vec![]),
};
@@ -381,25 +382,21 @@ impl InstalledRepository {
results
}
- pub fn add_repository(
- &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::<RootPackageRepository>()
- || repository.as_any().is::<PlatformRepository>()
+ pub fn add_repository(&mut self, repository: RepositoryInterfaceHandle) -> anyhow::Result<()> {
+ if repository.is::<LockArrayRepository>()
+ || repository.is::<RootPackageRepository>()
+ || repository.is::<PlatformRepository>()
{
self.inner.add_repository(repository);
return Ok(());
}
+ let type_name = std::any::type_name_of_val(&*repository.borrow()).to_string();
+ let repo_name = repository.get_repo_name();
Err(anyhow::anyhow!(LogicException {
message: format!(
"An InstalledRepository can not contain a repository of type {} ({})",
- std::any::type_name_of_val(&*repository),
- repository.get_repo_name(),
+ type_name, repo_name,
),
code: 0,
}))
diff --git a/crates/shirabe/src/repository/lock_array_repository.rs b/crates/shirabe/src/repository/lock_array_repository.rs
index cbc8564..f9b64a7 100644
--- a/crates/shirabe/src/repository/lock_array_repository.rs
+++ b/crates/shirabe/src/repository/lock_array_repository.rs
@@ -5,7 +5,8 @@ use crate::package::PackageInterfaceHandle;
use crate::repository::ArrayRepository;
use crate::repository::CanonicalPackagesTrait;
use crate::repository::{
- FindPackageConstraint, LoadPackagesResult, ProviderInfo, RepositoryInterface, SearchResult,
+ FindPackageConstraint, LoadPackagesResult, ProviderInfo, RepositoryInterface,
+ RepositoryInterfaceWeakHandle, SearchResult,
};
use indexmap::IndexMap;
use shirabe_php_shim::Countable;
@@ -89,4 +90,8 @@ impl RepositoryInterface for LockArrayRepository {
fn as_any(&self) -> &dyn std::any::Any {
self
}
+
+ fn set_self_handle(&self, weak: RepositoryInterfaceWeakHandle) {
+ self.inner.set_self_handle(weak);
+ }
}
diff --git a/crates/shirabe/src/repository/mod.rs b/crates/shirabe/src/repository/mod.rs
index 6f037d0..d678764 100644
--- a/crates/shirabe/src/repository/mod.rs
+++ b/crates/shirabe/src/repository/mod.rs
@@ -7,6 +7,7 @@ pub mod composite_repository;
pub mod configurable_repository_interface;
pub mod filesystem_repository;
pub mod filter_repository;
+pub mod handle;
pub mod installed_array_repository;
pub mod installed_filesystem_repository;
pub mod installed_repository;
@@ -39,6 +40,7 @@ pub use composite_repository::*;
pub use configurable_repository_interface::*;
pub use filesystem_repository::*;
pub use filter_repository::*;
+pub use handle::*;
pub use installed_array_repository::*;
pub use installed_filesystem_repository::*;
pub use installed_repository::*;
diff --git a/crates/shirabe/src/repository/platform_repository.rs b/crates/shirabe/src/repository/platform_repository.rs
index 1b95ff7..b8c4f52 100644
--- a/crates/shirabe/src/repository/platform_repository.rs
+++ b/crates/shirabe/src/repository/platform_repository.rs
@@ -2012,4 +2012,8 @@ impl crate::repository::RepositoryInterface for PlatformRepository {
fn as_any(&self) -> &dyn std::any::Any {
self
}
+
+ fn set_self_handle(&self, weak: crate::repository::RepositoryInterfaceWeakHandle) {
+ self.inner.set_self_handle(weak);
+ }
}
diff --git a/crates/shirabe/src/repository/repository_factory.rs b/crates/shirabe/src/repository/repository_factory.rs
index 908704c..1159da1 100644
--- a/crates/shirabe/src/repository/repository_factory.rs
+++ b/crates/shirabe/src/repository/repository_factory.rs
@@ -12,7 +12,7 @@ use crate::factory::Factory;
use crate::io::IOInterface;
use crate::json::JsonFile;
use crate::repository::FilesystemRepository;
-use crate::repository::RepositoryInterface;
+use crate::repository::RepositoryInterfaceHandle;
use crate::repository::RepositoryManager;
use crate::util::HttpDownloader;
use crate::util::ProcessExecutor;
@@ -102,7 +102,7 @@ impl RepositoryFactory {
repository: &str,
allow_filesystem: bool,
rm: Option<&mut RepositoryManager>,
- ) -> anyhow::Result<Box<dyn RepositoryInterface>> {
+ ) -> anyhow::Result<RepositoryInterfaceHandle> {
let repo_config =
Self::config_from_string(io.clone(), config, repository, allow_filesystem)?;
Self::create_repo(io, config, repo_config, rm)
@@ -113,7 +113,7 @@ impl RepositoryFactory {
config: &std::rc::Rc<std::cell::RefCell<Config>>,
repo_config: IndexMap<String, PhpMixed>,
rm: Option<&mut RepositoryManager>,
- ) -> anyhow::Result<Box<dyn RepositoryInterface>> {
+ ) -> anyhow::Result<RepositoryInterfaceHandle> {
let mut owned_rm;
let rm = if let Some(rm) = rm {
rm
@@ -145,7 +145,7 @@ impl RepositoryFactory {
io: Option<std::rc::Rc<std::cell::RefCell<dyn IOInterface>>>,
config: Option<std::rc::Rc<std::cell::RefCell<Config>>>,
rm: Option<&mut RepositoryManager>,
- ) -> anyhow::Result<IndexMap<String, Box<dyn RepositoryInterface>>> {
+ ) -> anyhow::Result<IndexMap<String, RepositoryInterfaceHandle>> {
let config = match config {
Some(c) => c,
None => std::rc::Rc::new(std::cell::RefCell::new(Factory::create_config(None, None)?)),
@@ -233,7 +233,7 @@ impl RepositoryFactory {
pub fn default_repos_with_default_manager(
io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>,
- ) -> anyhow::Result<IndexMap<String, Box<dyn RepositoryInterface>>> {
+ ) -> anyhow::Result<IndexMap<String, RepositoryInterfaceHandle>> {
let config = std::rc::Rc::new(std::cell::RefCell::new(Factory::create_config(
Some(io.clone()),
None,
@@ -247,8 +247,8 @@ impl RepositoryFactory {
fn create_repos(
rm: &mut RepositoryManager,
repo_configs: Vec<PhpMixed>,
- ) -> anyhow::Result<IndexMap<String, Box<dyn RepositoryInterface>>> {
- let mut repo_map: IndexMap<String, Box<dyn RepositoryInterface>> = IndexMap::new();
+ ) -> anyhow::Result<IndexMap<String, RepositoryInterfaceHandle>> {
+ let mut repo_map: IndexMap<String, RepositoryInterfaceHandle> = IndexMap::new();
for (index, repo) in repo_configs.into_iter().enumerate() {
match &repo {
@@ -290,7 +290,7 @@ impl RepositoryFactory {
.to_string();
// TODO(phase-b): FilesystemRepository does not yet implement
// RepositoryInterface; once it does, construct it from JsonFile here.
- let created: Box<dyn RepositoryInterface> =
+ let created: RepositoryInterfaceHandle =
todo!("FilesystemRepository as dyn RepositoryInterface");
repo_map.insert(name, created);
} else {
@@ -323,7 +323,7 @@ impl RepositoryFactory {
pub fn generate_repository_name(
index: &PhpMixed,
repo: &IndexMap<String, PhpMixed>,
- existing_repos: &IndexMap<String, Box<dyn RepositoryInterface>>,
+ existing_repos: &IndexMap<String, RepositoryInterfaceHandle>,
) -> String {
let mut name = match index {
PhpMixed::Int(_) => {
@@ -344,7 +344,7 @@ impl RepositoryFactory {
fn generate_repository_name_indexed(
index: usize,
repo: &IndexMap<String, PhpMixed>,
- existing_repos: &IndexMap<String, Box<dyn RepositoryInterface>>,
+ existing_repos: &IndexMap<String, RepositoryInterfaceHandle>,
) -> String {
let mut name = if let Some(url) = repo.get("url").and_then(|v| v.as_string()) {
Preg::replace("{^https?://}i", "", url).unwrap_or_else(|_| url.to_string())
diff --git a/crates/shirabe/src/repository/repository_interface.rs b/crates/shirabe/src/repository/repository_interface.rs
index 316f509..21914f6 100644
--- a/crates/shirabe/src/repository/repository_interface.rs
+++ b/crates/shirabe/src/repository/repository_interface.rs
@@ -93,8 +93,23 @@ pub trait RepositoryInterface: Countable + std::fmt::Debug {
None
}
+ fn as_installed_repository_interface_mut(
+ &mut self,
+ ) -> Option<&mut dyn crate::repository::InstalledRepositoryInterface> {
+ None
+ }
+
fn as_any(&self) -> &dyn std::any::Any;
+ /// Injects this repository's own weak handle so that `add_package` can wire package ->
+ /// repository back-references (PHP `setRepository($this)`). Called once when the repository is
+ /// wrapped in a [`RepositoryInterfaceHandle`](crate::repository::RepositoryInterfaceHandle).
+ /// Wrapper repositories forward the same weak (the outermost handle) to their inner
+ /// `ArrayRepository`.
+ fn set_self_handle(&self, weak: crate::repository::RepositoryInterfaceWeakHandle) {
+ let _ = weak;
+ }
+
fn clone_box(&self) -> Box<dyn RepositoryInterface> {
todo!()
}
diff --git a/crates/shirabe/src/repository/repository_manager.rs b/crates/shirabe/src/repository/repository_manager.rs
index 72c3643..dd69731 100644
--- a/crates/shirabe/src/repository/repository_manager.rs
+++ b/crates/shirabe/src/repository/repository_manager.rs
@@ -10,15 +10,14 @@ use crate::io::IOInterface;
use crate::io::IOInterfaceImmutable;
use crate::package::PackageInterfaceHandle;
use crate::repository::FilterRepository;
-use crate::repository::InstalledRepositoryInterface;
-use crate::repository::RepositoryInterface;
+use crate::repository::RepositoryInterfaceHandle;
use crate::util::HttpDownloader;
use crate::util::ProcessExecutor;
#[derive(Debug)]
pub struct RepositoryManager {
- local_repository: Option<Box<dyn InstalledRepositoryInterface>>,
- repositories: Vec<Box<dyn RepositoryInterface>>,
+ local_repository: Option<RepositoryInterfaceHandle>,
+ repositories: Vec<RepositoryInterfaceHandle>,
repository_classes: IndexMap<String, String>,
io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>,
config: std::rc::Rc<std::cell::RefCell<Config>>,
@@ -62,7 +61,7 @@ impl RepositoryManager {
name,
crate::repository::FindPackageConstraint::Constraint(constraint.clone()),
) {
- return Some(package.clone().into());
+ return Some(package.into());
}
}
None
@@ -81,17 +80,17 @@ impl RepositoryManager {
constraint.clone(),
)),
) {
- packages.push(p.clone().into());
+ packages.push(p);
}
}
packages
}
- pub fn add_repository(&mut self, repository: Box<dyn RepositoryInterface>) {
+ pub fn add_repository(&mut self, repository: RepositoryInterfaceHandle) {
self.repositories.push(repository);
}
- pub fn prepend_repository(&mut self, repository: Box<dyn RepositoryInterface>) {
+ pub fn prepend_repository(&mut self, repository: RepositoryInterfaceHandle) {
self.repositories.insert(0, repository);
}
@@ -100,7 +99,7 @@ impl RepositoryManager {
r#type: &str,
config: IndexMap<String, PhpMixed>,
name: Option<&str>,
- ) -> anyhow::Result<Box<dyn RepositoryInterface>> {
+ ) -> anyhow::Result<RepositoryInterfaceHandle> {
if !self.repository_classes.contains_key(r#type) {
return Err(InvalidArgumentException {
message: format!("Repository type is not registered: {}", r#type),
@@ -140,7 +139,10 @@ 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(RepositoryInterfaceHandle::new(FilterRepository::new(
+ repository,
+ filter_config,
+ )?));
}
Ok(repository)
@@ -150,7 +152,7 @@ impl RepositoryManager {
&self,
_class: &str,
_config: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<Box<dyn RepositoryInterface>> {
+ ) -> anyhow::Result<RepositoryInterfaceHandle> {
todo!("Phase B: dynamic class instantiation by class name")
}
@@ -159,15 +161,15 @@ impl RepositoryManager {
.insert(r#type.to_string(), class.to_string());
}
- pub fn get_repositories(&self) -> &Vec<Box<dyn RepositoryInterface>> {
+ pub fn get_repositories(&self) -> &Vec<RepositoryInterfaceHandle> {
&self.repositories
}
- pub fn set_local_repository(&mut self, repository: Box<dyn InstalledRepositoryInterface>) {
+ pub fn set_local_repository(&mut self, repository: RepositoryInterfaceHandle) {
self.local_repository = Some(repository);
}
- pub fn get_local_repository(&self) -> &dyn InstalledRepositoryInterface {
- self.local_repository.as_ref().unwrap().as_ref()
+ pub fn get_local_repository(&self) -> RepositoryInterfaceHandle {
+ self.local_repository.clone().unwrap()
}
}
diff --git a/crates/shirabe/src/repository/repository_set.rs b/crates/shirabe/src/repository/repository_set.rs
index 3a81eda..f4a4057 100644
--- a/crates/shirabe/src/repository/repository_set.rs
+++ b/crates/shirabe/src/repository/repository_set.rs
@@ -35,7 +35,7 @@ use crate::repository::InstalledRepositoryInterface;
use crate::repository::LockArrayRepository;
use crate::repository::PlatformRepository;
use crate::repository::{AdvisoryProviderInterface, PartialOrSecurityAdvisory};
-use crate::repository::{FindPackageConstraint, RepositoryInterface};
+use crate::repository::{FindPackageConstraint, RepositoryInterface, RepositoryInterfaceHandle};
#[derive(Debug, Clone)]
pub struct RootAliasEntry {
@@ -66,7 +66,7 @@ pub struct RepositorySet {
pub(crate) root_references: IndexMap<String, String>,
/// @var RepositoryInterface[]
- pub(crate) repositories: Vec<Box<dyn RepositoryInterface>>,
+ pub(crate) repositories: Vec<RepositoryInterfaceHandle>,
/// @var int[] array of stability => BasePackage::STABILITY_* value
/// @phpstan-var array<key-of<BasePackage::STABILITIES>, BasePackage::STABILITY_*>
@@ -170,7 +170,7 @@ impl RepositorySet {
/// repository the search for that package ends, and following repos will not be consulted.
///
/// @param RepositoryInterface $repo A package repository
- pub fn add_repository(&mut self, repo: Box<dyn RepositoryInterface>) -> Result<()> {
+ pub fn add_repository(&mut self, repo: RepositoryInterfaceHandle) -> Result<()> {
if self.locked {
return Err(RuntimeException {
message: "Pool has already been created from this repository set, it cannot be modified anymore.".to_string(),
@@ -179,17 +179,15 @@ impl RepositorySet {
.into());
}
- let repos: Vec<Box<dyn RepositoryInterface>> =
- if let Some(composite) = repo.as_any().downcast_ref::<CompositeRepository>() {
- // TODO(phase-b): clone composite.get_repositories() — Box<dyn RepositoryInterface> cloning
- composite
- .get_repositories()
- .iter()
- .map(|r| r.clone_box())
- .collect()
+ let repos: Vec<RepositoryInterfaceHandle> = {
+ let repo_ref = repo.borrow();
+ if let Some(composite) = repo_ref.as_any().downcast_ref::<CompositeRepository>() {
+ composite.get_repositories().clone()
} else {
+ drop(repo_ref);
vec![repo]
- };
+ }
+ };
for repo in repos {
self.repositories.push(repo);
@@ -379,7 +377,8 @@ impl RepositorySet {
for repository in &self.repositories {
// TODO(phase-b): use anyhow::Result<Result<T, E>> to model PHP try/catch
let attempt: Result<()> = (|| -> Result<()> {
- let Some(advisory_repo) = repository.as_advisory_provider() else {
+ let repo_ref = repository.borrow();
+ let Some(advisory_repo) = repo_ref.as_advisory_provider() else {
return Ok(());
};
if !advisory_repo.has_security_advisories() {
@@ -480,11 +479,11 @@ impl RepositorySet {
pool_builder.set_allowed_types(allowed_types);
for repo in &self.repositories {
- let is_installed = repo.as_installed_repository_interface().is_some()
- || repo
- .as_any()
- .downcast_ref::<InstalledRepository>()
- .is_some();
+ let is_installed = {
+ let repo_ref = repo.borrow();
+ repo_ref.as_installed_repository_interface().is_some()
+ || repo_ref.as_any().is::<InstalledRepository>()
+ };
if is_installed && !self.allow_installed_repositories {
return Err(LogicException {
message: "The pool can not accept packages from an installed repository"
@@ -507,11 +506,11 @@ impl RepositorySet {
/// Create a pool for dependency resolution from the packages in this repository set.
pub fn create_pool_with_all_packages(&mut self) -> Result<Pool> {
for repo in &self.repositories {
- let is_installed = repo.as_installed_repository_interface().is_some()
- || repo
- .as_any()
- .downcast_ref::<InstalledRepository>()
- .is_some();
+ let is_installed = {
+ let repo_ref = repo.borrow();
+ repo_ref.as_installed_repository_interface().is_some()
+ || repo_ref.as_any().is::<InstalledRepository>()
+ };
if is_installed && !self.allow_installed_repositories {
return Err(LogicException {
message: "The pool can not accept packages from an installed repository"
diff --git a/crates/shirabe/src/repository/repository_utils.rs b/crates/shirabe/src/repository/repository_utils.rs
index 667cea2..4522039 100644
--- a/crates/shirabe/src/repository/repository_utils.rs
+++ b/crates/shirabe/src/repository/repository_utils.rs
@@ -3,7 +3,7 @@
use crate::package::Link;
use crate::repository::CompositeRepository;
use crate::repository::FilterRepository;
-use crate::repository::RepositoryInterface;
+use crate::repository::RepositoryInterfaceHandle;
use indexmap::IndexMap;
use std::any::Any;
@@ -39,23 +39,31 @@ impl RepositoryUtils {
}
pub fn flatten_repositories(
- repo: Box<dyn RepositoryInterface>,
+ repo: RepositoryInterfaceHandle,
unwrap_filter_repos: bool,
- ) -> Vec<Box<dyn RepositoryInterface>> {
- let repo: Box<dyn RepositoryInterface> = if unwrap_filter_repos {
- if let Some(filter_repo) = repo.as_any().downcast_ref::<FilterRepository>() {
- filter_repo.get_repository().clone_box()
- } else {
- repo
- }
+ ) -> Vec<RepositoryInterfaceHandle> {
+ let repo: RepositoryInterfaceHandle = if unwrap_filter_repos {
+ let unwrapped = {
+ let r = repo.borrow();
+ r.as_any()
+ .downcast_ref::<FilterRepository>()
+ .map(|filter_repo| filter_repo.get_repository())
+ };
+ unwrapped.unwrap_or(repo)
} else {
repo
};
- if let Some(composite_repo) = repo.as_any().downcast_ref::<CompositeRepository>() {
+ let nested = {
+ let r = repo.borrow();
+ r.as_any()
+ .downcast_ref::<CompositeRepository>()
+ .map(|composite_repo| composite_repo.get_repositories().clone())
+ };
+ if let Some(nested) = nested {
let mut repos = Vec::new();
- for r in composite_repo.get_repositories() {
- for r2 in Self::flatten_repositories(r.clone_box(), unwrap_filter_repos) {
+ for r in nested {
+ for r2 in Self::flatten_repositories(r, unwrap_filter_repos) {
repos.push(r2);
}
}
diff --git a/crates/shirabe/src/repository/root_package_repository.rs b/crates/shirabe/src/repository/root_package_repository.rs
index cb01a7c..22ca437 100644
--- a/crates/shirabe/src/repository/root_package_repository.rs
+++ b/crates/shirabe/src/repository/root_package_repository.rs
@@ -4,6 +4,7 @@ use crate::package::BasePackageHandle;
use crate::package::PackageInterfaceHandle;
use crate::package::RootPackageInterfaceHandle;
use crate::repository::ArrayRepository;
+use crate::repository::RepositoryInterfaceWeakHandle;
use crate::repository::{ProviderInfo, RepositoryInterface, SearchResult};
use indexmap::IndexMap;
@@ -85,4 +86,8 @@ impl RepositoryInterface for RootPackageRepository {
fn as_any(&self) -> &dyn std::any::Any {
self
}
+
+ fn set_self_handle(&self, weak: RepositoryInterfaceWeakHandle) {
+ self.inner.set_self_handle(weak);
+ }
}
diff --git a/crates/shirabe/src/repository/writable_array_repository.rs b/crates/shirabe/src/repository/writable_array_repository.rs
index 282f3c0..ac9df8f 100644
--- a/crates/shirabe/src/repository/writable_array_repository.rs
+++ b/crates/shirabe/src/repository/writable_array_repository.rs
@@ -3,6 +3,7 @@
use crate::installer::InstallationManager;
use crate::repository::ArrayRepository;
use crate::repository::RepositoryInterface;
+use crate::repository::RepositoryInterfaceWeakHandle;
use anyhow::Result;
use shirabe_php_shim::Countable;
@@ -52,6 +53,10 @@ impl WritableArrayRepository {
self.inner.add_package(package)
}
+ pub fn set_self_handle(&self, weak: RepositoryInterfaceWeakHandle) {
+ self.inner.set_self_handle(weak);
+ }
+
pub fn remove_package(
&mut self,
package: crate::package::PackageInterfaceHandle,