aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-05-15 23:50:58 +0900
committernsfisis <nsfisis@gmail.com>2026-05-16 10:00:40 +0900
commiteee660c46a6b919ff5db6d447c3a500140306991 (patch)
tree2daf2b09776d2108fc3b13d31322065e4e978bac /crates/shirabe
parent12bbfc80c4e42ebd82e26c04c49d7d355ea086d6 (diff)
downloadphp-shirabe-eee660c46a6b919ff5db6d447c3a500140306991.tar.gz
php-shirabe-eee660c46a6b919ff5db6d447c3a500140306991.tar.zst
php-shirabe-eee660c46a6b919ff5db6d447c3a500140306991.zip
feat(port): port RepositoryManager.php
Diffstat (limited to 'crates/shirabe')
-rw-r--r--crates/shirabe/src/repository/repository_manager.rs119
1 files changed, 119 insertions, 0 deletions
diff --git a/crates/shirabe/src/repository/repository_manager.rs b/crates/shirabe/src/repository/repository_manager.rs
index 8afb92e..fd60e7b 100644
--- a/crates/shirabe/src/repository/repository_manager.rs
+++ b/crates/shirabe/src/repository/repository_manager.rs
@@ -1 +1,120 @@
//! ref: composer/src/Composer/Repository/RepositoryManager.php
+
+use indexmap::IndexMap;
+use shirabe_php_shim::{json_encode, InvalidArgumentException, PhpMixed};
+use shirabe_semver::constraint::constraint_interface::ConstraintInterface;
+
+use crate::config::Config;
+use crate::event_dispatcher::event_dispatcher::EventDispatcher;
+use crate::io::io_interface::IOInterface;
+use crate::package::package_interface::PackageInterface;
+use crate::repository::filter_repository::FilterRepository;
+use crate::repository::installed_repository_interface::InstalledRepositoryInterface;
+use crate::repository::repository_interface::RepositoryInterface;
+use crate::util::http_downloader::HttpDownloader;
+use crate::util::process_executor::ProcessExecutor;
+
+pub struct RepositoryManager {
+ local_repository: Option<Box<dyn InstalledRepositoryInterface>>,
+ repositories: Vec<Box<dyn RepositoryInterface>>,
+ repository_classes: IndexMap<String, String>,
+ io: Box<dyn IOInterface>,
+ config: Config,
+ http_downloader: HttpDownloader,
+ event_dispatcher: Option<EventDispatcher>,
+ process: ProcessExecutor,
+}
+
+impl RepositoryManager {
+ pub fn new(io: &dyn IOInterface, config: &Config, http_downloader: HttpDownloader, event_dispatcher: Option<EventDispatcher>, process: Option<ProcessExecutor>) -> Self {
+ let process = process.unwrap_or_else(|| ProcessExecutor::new(io));
+ Self {
+ local_repository: None,
+ repositories: vec![],
+ repository_classes: IndexMap::new(),
+ io: io.clone_box(),
+ config: config.clone(),
+ http_downloader,
+ event_dispatcher,
+ process,
+ }
+ }
+
+ pub fn find_package(&self, name: &str, constraint: &dyn ConstraintInterface) -> Option<Box<dyn PackageInterface>> {
+ for repository in &self.repositories {
+ if let Some(package) = repository.find_package(name, constraint) {
+ return Some(package);
+ }
+ }
+ None
+ }
+
+ pub fn find_packages(&self, name: &str, constraint: &dyn ConstraintInterface) -> 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));
+ }
+ packages
+ }
+
+ pub fn add_repository(&mut self, repository: Box<dyn RepositoryInterface>) {
+ self.repositories.push(repository);
+ }
+
+ pub fn prepend_repository(&mut self, repository: Box<dyn RepositoryInterface>) {
+ self.repositories.insert(0, repository);
+ }
+
+ pub fn create_repository(&self, r#type: &str, config: IndexMap<String, PhpMixed>, name: Option<&str>) -> anyhow::Result<Box<dyn RepositoryInterface>> {
+ if !self.repository_classes.contains_key(r#type) {
+ return Err(InvalidArgumentException {
+ message: format!("Repository type is not registered: {}", r#type),
+ code: 0,
+ }.into());
+ }
+
+ if config.get("packagist").and_then(|v| v.as_bool()) == Some(false) {
+ let config_json = json_encode(&PhpMixed::Array(config.iter().map(|(k, v)| (k.clone(), Box::new(v.clone()))).collect())).unwrap_or_default();
+ self.io.write_error(&format!("<warning>Repository \"{}\" ({}) has a packagist key which should be in its own repository definition</warning>", name.unwrap_or(""), config_json));
+ }
+
+ let class = self.repository_classes[r#type].clone();
+
+ let has_filter = config.contains_key("only") || config.contains_key("exclude") || config.contains_key("canonical");
+ let filter_config = if has_filter { Some(config.clone()) } else { None };
+
+ let mut cleaned_config = config;
+ cleaned_config.remove("only");
+ cleaned_config.remove("exclude");
+ cleaned_config.remove("canonical");
+
+ // Phase B: implement dynamic class instantiation by class name
+ 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)));
+ }
+
+ Ok(repository)
+ }
+
+ fn create_repository_by_class(&self, _class: &str, _config: IndexMap<String, PhpMixed>) -> anyhow::Result<Box<dyn RepositoryInterface>> {
+ todo!("Phase B: dynamic class instantiation by class name")
+ }
+
+ pub fn set_repository_class(&mut self, r#type: &str, class: &str) {
+ self.repository_classes.insert(r#type.to_string(), class.to_string());
+ }
+
+ pub fn get_repositories(&self) -> &Vec<Box<dyn RepositoryInterface>> {
+ &self.repositories
+ }
+
+ pub fn set_local_repository(&mut self, repository: Box<dyn InstalledRepositoryInterface>) {
+ self.local_repository = Some(repository);
+ }
+
+ pub fn get_local_repository(&self) -> &dyn InstalledRepositoryInterface {
+ self.local_repository.as_ref().unwrap().as_ref()
+ }
+}