diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-06-08 01:54:56 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-06-08 01:54:56 +0900 |
| commit | 318ea948f5932dfa7942081a269d62fd7161a9bf (patch) | |
| tree | 0fff2fe818f87a20dea89a51901f9e16071f2f53 /crates/shirabe/src/repository | |
| parent | f232d7f9d2936ef84bd904cacd21c12cb7012b34 (diff) | |
| download | php-shirabe-318ea948f5932dfa7942081a269d62fd7161a9bf.tar.gz php-shirabe-318ea948f5932dfa7942081a269d62fd7161a9bf.tar.zst php-shirabe-318ea948f5932dfa7942081a269d62fd7161a9bf.zip | |
feat(phase-c): resolve reflection/downcast phase-b TODOs
Resolve category F phase-b TODOs (class-string, instanceof, get_class,
method_exists, __FILE__, Reflection API, downcast).
- VcsRepository: dispatch drivers through a VcsDriverKind enum
(instantiate/supports/php_class_name) and add constructors to the
concrete VCS drivers
- repository downcasts via RepositoryInterfaceHandle::downcast_rc and
as_any (init/show commands, vcs ValidatingArrayLoader)
- BaseCommand::is_self_update_command override replaces an instanceof
- Factory::create narrows PartialComposer to ComposerHandle via as_full
- InstalledVersions gains set_self_dir/set_installed_is_local_dir,
replacing Reflection-based static property mutation
- ClassLoader::as_array_iter ports the PHP (array) cast
- drop the unnecessary __FILE__ phar branch in self-update
application get_class(command) reclassified TODO(plugin); buffer_io
StreamableInputInterface downcast and the ValidatingArrayLoader trait
redesign left as tracked TODOs.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe/src/repository')
| -rw-r--r-- | crates/shirabe/src/repository/filesystem_repository.rs | 10 | ||||
| -rw-r--r-- | crates/shirabe/src/repository/handle.rs | 18 | ||||
| -rw-r--r-- | crates/shirabe/src/repository/installed_repository.rs | 4 | ||||
| -rw-r--r-- | crates/shirabe/src/repository/vcs/forgejo_driver.rs | 17 | ||||
| -rw-r--r-- | crates/shirabe/src/repository/vcs/fossil_driver.rs | 17 | ||||
| -rw-r--r-- | crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs | 26 | ||||
| -rw-r--r-- | crates/shirabe/src/repository/vcs/github_driver.rs | 24 | ||||
| -rw-r--r-- | crates/shirabe/src/repository/vcs/gitlab_driver.rs | 23 | ||||
| -rw-r--r-- | crates/shirabe/src/repository/vcs/hg_driver.rs | 16 | ||||
| -rw-r--r-- | crates/shirabe/src/repository/vcs/mod.rs | 131 | ||||
| -rw-r--r-- | crates/shirabe/src/repository/vcs/perforce_driver.rs | 15 | ||||
| -rw-r--r-- | crates/shirabe/src/repository/vcs/svn_driver.rs | 22 | ||||
| -rw-r--r-- | crates/shirabe/src/repository/vcs_repository.rs | 186 |
13 files changed, 406 insertions, 103 deletions
diff --git a/crates/shirabe/src/repository/filesystem_repository.rs b/crates/shirabe/src/repository/filesystem_repository.rs index 4e752d9..bb78886 100644 --- a/crates/shirabe/src/repository/filesystem_repository.rs +++ b/crates/shirabe/src/repository/filesystem_repository.rs @@ -341,14 +341,8 @@ impl FilesystemRepository { // make sure the selfDir matches the expected data at runtime if the class was loaded from the vendor dir, as it may have been // loaded from the Composer sources, causing packages to appear twice in that case if the installed.php is loaded in addition to the // in memory loaded data from above - // TODO(phase-b): Reflection API on static properties — confirm porting approach with user - let _attempt: Result<()> = (|| -> Result<()> { - todo!( - "ReflectionProperty(Composer\\InstalledVersions::class, 'selfDir')->setValue(null, strtr($repoDir, '\\\\', '/'))" - ); - // (the second reflection block sets installedIsLocalDir = true) - })(); - // PHP: catches \ReflectionException and rethrows if not "Property does not exist" + InstalledVersions::set_self_dir(repo_dir.replace('\\', "/")); + InstalledVersions::set_installed_is_local_dir(true); } } diff --git a/crates/shirabe/src/repository/handle.rs b/crates/shirabe/src/repository/handle.rs index 0d02b83..aa79d5e 100644 --- a/crates/shirabe/src/repository/handle.rs +++ b/crates/shirabe/src/repository/handle.rs @@ -66,6 +66,24 @@ impl RepositoryInterfaceHandle { self.0.borrow().as_any().is::<T>() } + /// Downcasts the shared handle to a concrete repository type, preserving shared ownership. + pub fn downcast_rc<T: RepositoryInterface + 'static>(&self) -> Option<Rc<RefCell<T>>> { + if self.0.borrow().as_any().is::<T>() { + let rc = self.0.clone(); + let ptr = Rc::into_raw(rc) as *const RefCell<T>; + // SAFETY: is::<T>() proved the value is `T`, and handles are always allocated as + // `Rc::new(RefCell::new(concrete))`, so the layout matches `RcBox<RefCell<T>>`. + Some(unsafe { Rc::from_raw(ptr) }) + } else { + None + } + } + + pub fn as_platform_repository(&self) -> Option<PlatformRepositoryHandle> { + self.downcast_rc::<PlatformRepository>() + .map(PlatformRepositoryHandle::from_rc) + } + pub fn count(&self) -> i64 { self.0.borrow().count() } diff --git a/crates/shirabe/src/repository/installed_repository.rs b/crates/shirabe/src/repository/installed_repository.rs index 2bd6fc6..8b611ae 100644 --- a/crates/shirabe/src/repository/installed_repository.rs +++ b/crates/shirabe/src/repository/installed_repository.rs @@ -48,6 +48,10 @@ impl InstalledRepository { this } + pub fn get_repositories(&self) -> &Vec<RepositoryInterfaceHandle> { + self.inner.get_repositories() + } + pub fn find_packages_with_replacers_and_providers( &self, name: &str, diff --git a/crates/shirabe/src/repository/vcs/forgejo_driver.rs b/crates/shirabe/src/repository/vcs/forgejo_driver.rs index c93ee70..3c1366d 100644 --- a/crates/shirabe/src/repository/vcs/forgejo_driver.rs +++ b/crates/shirabe/src/repository/vcs/forgejo_driver.rs @@ -34,6 +34,23 @@ pub struct ForgejoDriver { } impl ForgejoDriver { + pub fn new( + repo_config: IndexMap<String, shirabe_php_shim::PhpMixed>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, + config: std::rc::Rc<std::cell::RefCell<Config>>, + http_downloader: std::rc::Rc<std::cell::RefCell<crate::util::HttpDownloader>>, + process: std::rc::Rc<std::cell::RefCell<crate::util::ProcessExecutor>>, + ) -> Self { + Self { + inner: VcsDriverBase::new(repo_config, io, config, http_downloader, process), + forgejo_url: None, + repository_data: None, + git_driver: None, + tags: None, + branches: None, + } + } + pub fn initialize(&mut self) -> Result<()> { let forgejo_url = ForgejoUrl::create(&self.inner.url)?; self.inner.origin_url = forgejo_url.origin_url.clone(); diff --git a/crates/shirabe/src/repository/vcs/fossil_driver.rs b/crates/shirabe/src/repository/vcs/fossil_driver.rs index 5b7ccc1..0ee20da 100644 --- a/crates/shirabe/src/repository/vcs/fossil_driver.rs +++ b/crates/shirabe/src/repository/vcs/fossil_driver.rs @@ -26,6 +26,23 @@ pub struct FossilDriver { } impl FossilDriver { + pub fn new( + repo_config: IndexMap<String, shirabe_php_shim::PhpMixed>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, + config: std::rc::Rc<std::cell::RefCell<Config>>, + http_downloader: std::rc::Rc<std::cell::RefCell<crate::util::HttpDownloader>>, + process: std::rc::Rc<std::cell::RefCell<crate::util::ProcessExecutor>>, + ) -> Self { + Self { + inner: VcsDriverBase::new(repo_config, io, config, http_downloader, process), + tags: None, + branches: None, + root_identifier: None, + repo_file: None, + checkout_dir: String::new(), + } + } + pub fn initialize(&mut self) -> anyhow::Result<()> { // Make sure fossil is installed and reachable. self.check_fossil()?; diff --git a/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs b/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs index 2a8d374..3f0be13 100644 --- a/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs +++ b/crates/shirabe/src/repository/vcs/git_bitbucket_driver.rs @@ -58,6 +58,32 @@ pub struct GitBitbucketDriver { } impl GitBitbucketDriver { + pub fn new( + repo_config: IndexMap<String, shirabe_php_shim::PhpMixed>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, + config: std::rc::Rc<std::cell::RefCell<Config>>, + http_downloader: std::rc::Rc<std::cell::RefCell<crate::util::HttpDownloader>>, + process: std::rc::Rc<std::cell::RefCell<crate::util::ProcessExecutor>>, + ) -> Self { + Self { + inner: VcsDriverBase::new(repo_config, io, config, http_downloader, process), + owner: String::new(), + repository: String::new(), + has_issues: false, + root_identifier: None, + tags: None, + branches: None, + branches_url: String::new(), + tags_url: String::new(), + home_url: String::new(), + website: String::new(), + clone_https_url: String::new(), + repo_data: IndexMap::new(), + fallback_driver: None, + vcs_type: None, + } + } + /// @inheritDoc pub fn initialize(&mut self) -> Result<()> { let mut m: indexmap::IndexMap<CaptureKey, String> = indexmap::IndexMap::new(); diff --git a/crates/shirabe/src/repository/vcs/github_driver.rs b/crates/shirabe/src/repository/vcs/github_driver.rs index b3d5e05..6fc50ee 100644 --- a/crates/shirabe/src/repository/vcs/github_driver.rs +++ b/crates/shirabe/src/repository/vcs/github_driver.rs @@ -47,6 +47,30 @@ pub struct GitHubDriver { } impl GitHubDriver { + pub fn new( + repo_config: IndexMap<String, shirabe_php_shim::PhpMixed>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, + config: std::rc::Rc<std::cell::RefCell<Config>>, + http_downloader: std::rc::Rc<std::cell::RefCell<crate::util::HttpDownloader>>, + process: std::rc::Rc<std::cell::RefCell<crate::util::ProcessExecutor>>, + ) -> Self { + Self { + inner: VcsDriverBase::new(repo_config, io, config, http_downloader, process), + owner: String::new(), + repository: String::new(), + tags: None, + branches: None, + root_identifier: String::new(), + repo_data: None, + has_issues: false, + is_private: false, + is_archived: false, + funding_info: None, + allow_git_fallback: true, + git_driver: None, + } + } + pub fn initialize(&mut self) -> Result<()> { let mut match_: IndexMap<CaptureKey, String> = IndexMap::new(); if !Preg::is_match_strict_groups3( diff --git a/crates/shirabe/src/repository/vcs/gitlab_driver.rs b/crates/shirabe/src/repository/vcs/gitlab_driver.rs index 84a569f..84961f5 100644 --- a/crates/shirabe/src/repository/vcs/gitlab_driver.rs +++ b/crates/shirabe/src/repository/vcs/gitlab_driver.rs @@ -56,6 +56,29 @@ pub struct GitLabDriver { impl GitLabDriver { pub const URL_REGEX: &'static str = r##"#^(?:(?P<scheme>https?)://(?P<domain>.+?)(?::(?P<port>[0-9]+))?/|git@(?P<domain2>[^:]+):)(?P<parts>.+)/(?P<repo>[^/]+?)(?:\.git|/)?$#"##; + pub fn new( + repo_config: IndexMap<String, shirabe_php_shim::PhpMixed>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, + config: std::rc::Rc<std::cell::RefCell<Config>>, + http_downloader: std::rc::Rc<std::cell::RefCell<crate::util::HttpDownloader>>, + process: std::rc::Rc<std::cell::RefCell<crate::util::ProcessExecutor>>, + ) -> Self { + Self { + inner: VcsDriverBase::new(repo_config, io, config, http_downloader, process), + scheme: String::new(), + namespace: String::new(), + repository: String::new(), + project: None, + commits: IndexMap::new(), + tags: None, + branches: None, + git_driver: None, + protocol: String::new(), + is_private: true, + has_nonstandard_origin: false, + } + } + /// Extracts information from the repository url. /// /// SSH urls use https by default. Set "secure-http": false on the repository config to use http instead. diff --git a/crates/shirabe/src/repository/vcs/hg_driver.rs b/crates/shirabe/src/repository/vcs/hg_driver.rs index 1686f5d..c35a574 100644 --- a/crates/shirabe/src/repository/vcs/hg_driver.rs +++ b/crates/shirabe/src/repository/vcs/hg_driver.rs @@ -25,6 +25,22 @@ pub struct HgDriver { } impl HgDriver { + pub fn new( + repo_config: IndexMap<String, shirabe_php_shim::PhpMixed>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, + config: std::rc::Rc<std::cell::RefCell<Config>>, + http_downloader: std::rc::Rc<std::cell::RefCell<crate::util::HttpDownloader>>, + process: std::rc::Rc<std::cell::RefCell<crate::util::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<()> { if Filesystem::is_local_path(&self.inner.url) { self.repo_dir = self.inner.url.clone(); diff --git a/crates/shirabe/src/repository/vcs/mod.rs b/crates/shirabe/src/repository/vcs/mod.rs index 715d2e5..5c60e0a 100644 --- a/crates/shirabe/src/repository/vcs/mod.rs +++ b/crates/shirabe/src/repository/vcs/mod.rs @@ -21,3 +21,134 @@ pub use perforce_driver::*; pub use svn_driver::*; pub use vcs_driver::*; pub use vcs_driver_interface::*; + +use crate::config::Config; +use crate::io::IOInterface; +use crate::util::{HttpDownloader, ProcessExecutor}; +use indexmap::IndexMap; +use shirabe_php_shim::PhpMixed; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum VcsDriverKind { + GitHub, + GitLab, + GitBitbucket, + Forgejo, + Git, + Hg, + Perforce, + Fossil, + Svn, +} + +impl VcsDriverKind { + pub fn instantiate( + self, + repo_config: IndexMap<String, PhpMixed>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, + config: std::rc::Rc<std::cell::RefCell<Config>>, + http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>, + process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>, + ) -> Box<dyn VcsDriverInterface> { + match self { + VcsDriverKind::GitHub => Box::new(GitHubDriver::new( + repo_config, + io, + config, + http_downloader, + process, + )), + VcsDriverKind::GitLab => Box::new(GitLabDriver::new( + repo_config, + io, + config, + http_downloader, + process, + )), + VcsDriverKind::GitBitbucket => Box::new(GitBitbucketDriver::new( + repo_config, + io, + config, + http_downloader, + process, + )), + VcsDriverKind::Forgejo => Box::new(ForgejoDriver::new( + repo_config, + io, + config, + http_downloader, + process, + )), + VcsDriverKind::Git => Box::new(GitDriver::new( + repo_config, + io, + config, + http_downloader, + process, + )), + VcsDriverKind::Hg => Box::new(HgDriver::new( + repo_config, + io, + config, + http_downloader, + process, + )), + VcsDriverKind::Perforce => Box::new(PerforceDriver::new( + repo_config, + io, + config, + http_downloader, + process, + )), + VcsDriverKind::Fossil => Box::new(FossilDriver::new( + repo_config, + io, + config, + http_downloader, + process, + )), + VcsDriverKind::Svn => Box::new(SvnDriver::new( + repo_config, + io, + config, + http_downloader, + process, + )), + } + } + + pub fn supports( + self, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, + config: std::rc::Rc<std::cell::RefCell<Config>>, + url: &str, + deep: bool, + ) -> anyhow::Result<bool> { + match self { + VcsDriverKind::GitHub => GitHubDriver::supports(io, config, url, deep), + VcsDriverKind::GitLab => GitLabDriver::supports(io, config, url, deep), + VcsDriverKind::GitBitbucket => GitBitbucketDriver::supports(io, config, url, deep), + VcsDriverKind::Forgejo => ForgejoDriver::supports(io, config, url, deep), + VcsDriverKind::Git => GitDriver::supports(io, config, url, deep), + VcsDriverKind::Hg => HgDriver::supports(io, config, url, deep), + VcsDriverKind::Perforce => PerforceDriver::supports(io, config, url, deep), + VcsDriverKind::Fossil => FossilDriver::supports(io, config, url, deep), + VcsDriverKind::Svn => SvnDriver::supports(io, config, url, deep), + } + } + + /// PHP fully-qualified `class-string`, used as the fallback driver name in `getRepoName()`. + pub fn php_class_name(self) -> &'static str { + match self { + VcsDriverKind::GitHub => "Composer\\Repository\\Vcs\\GitHubDriver", + VcsDriverKind::GitLab => "Composer\\Repository\\Vcs\\GitLabDriver", + VcsDriverKind::GitBitbucket => "Composer\\Repository\\Vcs\\GitBitbucketDriver", + VcsDriverKind::Forgejo => "Composer\\Repository\\Vcs\\ForgejoDriver", + VcsDriverKind::Git => "Composer\\Repository\\Vcs\\GitDriver", + VcsDriverKind::Hg => "Composer\\Repository\\Vcs\\HgDriver", + VcsDriverKind::Perforce => "Composer\\Repository\\Vcs\\PerforceDriver", + VcsDriverKind::Fossil => "Composer\\Repository\\Vcs\\FossilDriver", + VcsDriverKind::Svn => "Composer\\Repository\\Vcs\\SvnDriver", + } + } +} diff --git a/crates/shirabe/src/repository/vcs/perforce_driver.rs b/crates/shirabe/src/repository/vcs/perforce_driver.rs index d653869..0fe0f6f 100644 --- a/crates/shirabe/src/repository/vcs/perforce_driver.rs +++ b/crates/shirabe/src/repository/vcs/perforce_driver.rs @@ -21,6 +21,21 @@ pub struct PerforceDriver { } impl PerforceDriver { + pub fn new( + repo_config: IndexMap<String, shirabe_php_shim::PhpMixed>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, + config: std::rc::Rc<std::cell::RefCell<Config>>, + http_downloader: std::rc::Rc<std::cell::RefCell<crate::util::HttpDownloader>>, + process: std::rc::Rc<std::cell::RefCell<crate::util::ProcessExecutor>>, + ) -> Self { + Self { + inner: VcsDriverBase::new(repo_config, io, config, http_downloader, process), + depot: String::new(), + branch: String::new(), + perforce: None, + } + } + pub fn initialize(&mut self) -> anyhow::Result<()> { self.depot = self .inner diff --git a/crates/shirabe/src/repository/vcs/svn_driver.rs b/crates/shirabe/src/repository/vcs/svn_driver.rs index 9b2ea47..377d698 100644 --- a/crates/shirabe/src/repository/vcs/svn_driver.rs +++ b/crates/shirabe/src/repository/vcs/svn_driver.rs @@ -50,6 +50,28 @@ pub struct SvnDriver { } impl SvnDriver { + pub fn new( + repo_config: IndexMap<String, shirabe_php_shim::PhpMixed>, + io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, + config: std::rc::Rc<std::cell::RefCell<Config>>, + http_downloader: std::rc::Rc<std::cell::RefCell<crate::util::HttpDownloader>>, + process: std::rc::Rc<std::cell::RefCell<crate::util::ProcessExecutor>>, + ) -> Self { + Self { + inner: VcsDriverBase::new(repo_config, io, config, http_downloader, process), + base_url: String::new(), + tags: None, + branches: None, + root_identifier: None, + trunk_path: Some("trunk".to_string()), + branches_path: "branches".to_string(), + tags_path: "tags".to_string(), + package_path: String::new(), + cache_credentials: true, + util: None, + } + } + pub fn initialize(&mut self) -> Result<()> { let normalized = Self::normalize_url(&self.inner.url); self.inner.url = normalized.trim_end_matches('/').to_string(); diff --git a/crates/shirabe/src/repository/vcs_repository.rs b/crates/shirabe/src/repository/vcs_repository.rs index 4c2492a..7817172 100644 --- a/crates/shirabe/src/repository/vcs_repository.rs +++ b/crates/shirabe/src/repository/vcs_repository.rs @@ -4,10 +4,7 @@ use crate::io::io_interface; use anyhow::Result; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::Preg; -use shirabe_php_shim::{ - InvalidArgumentException, PhpMixed, array_search_mixed, count, get_class, in_array, - str_replace, strpos, -}; +use shirabe_php_shim::{InvalidArgumentException, PhpMixed, in_array, str_replace, strpos}; use shirabe_semver::constraint::AnyConstraint; use shirabe_semver::constraint::SimpleConstraint; @@ -26,12 +23,14 @@ use crate::repository::ConfigurableRepositoryInterface; use crate::repository::InvalidRepositoryException; use crate::repository::RepositoryInterface; use crate::repository::vcs::VcsDriverInterface; +use crate::repository::vcs::VcsDriverKind; use crate::repository::{VersionCacheInterface, VersionCacheResult}; use crate::util::HttpDownloader; use crate::util::Platform; use crate::util::ProcessExecutor; use crate::util::Url; +// TODO(phase-c): the driver registration should be refactored later. #[derive(Debug)] pub struct VcsRepository { pub(crate) inner: ArrayRepository, @@ -62,9 +61,12 @@ pub struct VcsRepository { /// @var bool pub(crate) branch_error_occurred: bool, /// @var array<string, class-string<VcsDriverInterface>> - drivers: IndexMap<String, String>, + drivers: IndexMap<String, VcsDriverKind>, /// @var ?VcsDriverInterface driver: Option<Box<dyn VcsDriverInterface>>, + /// Kind of the resolved `driver`, used by `get_repo_name` to recover the driver type + /// (PHP `array_search(get_class($driver), $this->drivers)`). + driver_kind: Option<VcsDriverKind>, /// @var ?VersionCacheInterface version_cache: Option<Box<dyn VersionCacheInterface>>, /// @var list<string> @@ -91,53 +93,23 @@ impl VcsRepository { http_downloader: std::rc::Rc<std::cell::RefCell<HttpDownloader>>, dispatcher: Option<std::rc::Rc<std::cell::RefCell<EventDispatcher>>>, process: Option<std::rc::Rc<std::cell::RefCell<ProcessExecutor>>>, - drivers: Option<IndexMap<String, String>>, + drivers: Option<IndexMap<String, VcsDriverKind>>, version_cache: Option<Box<dyn VersionCacheInterface>>, ) -> Result<Self> { let inner = ArrayRepository::new(vec![])?; let drivers = drivers.unwrap_or_else(|| { - let mut m: IndexMap<String, String> = IndexMap::new(); - m.insert( - "github".to_string(), - "Composer\\Repository\\Vcs\\GitHubDriver".to_string(), - ); - m.insert( - "gitlab".to_string(), - "Composer\\Repository\\Vcs\\GitLabDriver".to_string(), - ); - m.insert( - "bitbucket".to_string(), - "Composer\\Repository\\Vcs\\GitBitbucketDriver".to_string(), - ); - m.insert( - "git-bitbucket".to_string(), - "Composer\\Repository\\Vcs\\GitBitbucketDriver".to_string(), - ); - m.insert( - "forgejo".to_string(), - "Composer\\Repository\\Vcs\\ForgejoDriver".to_string(), - ); - m.insert( - "git".to_string(), - "Composer\\Repository\\Vcs\\GitDriver".to_string(), - ); - m.insert( - "hg".to_string(), - "Composer\\Repository\\Vcs\\HgDriver".to_string(), - ); - m.insert( - "perforce".to_string(), - "Composer\\Repository\\Vcs\\PerforceDriver".to_string(), - ); - m.insert( - "fossil".to_string(), - "Composer\\Repository\\Vcs\\FossilDriver".to_string(), - ); + let mut m: IndexMap<String, VcsDriverKind> = IndexMap::new(); + m.insert("github".to_string(), VcsDriverKind::GitHub); + m.insert("gitlab".to_string(), VcsDriverKind::GitLab); + m.insert("bitbucket".to_string(), VcsDriverKind::GitBitbucket); + m.insert("git-bitbucket".to_string(), VcsDriverKind::GitBitbucket); + m.insert("forgejo".to_string(), VcsDriverKind::Forgejo); + m.insert("git".to_string(), VcsDriverKind::Git); + m.insert("hg".to_string(), VcsDriverKind::Hg); + m.insert("perforce".to_string(), VcsDriverKind::Perforce); + m.insert("fossil".to_string(), VcsDriverKind::Fossil); // svn must be last because identifying a subversion server for sure is practically impossible - m.insert( - "svn".to_string(), - "Composer\\Repository\\Vcs\\SvnDriver".to_string(), - ); + m.insert("svn".to_string(), VcsDriverKind::Svn); m }); @@ -178,6 +150,7 @@ impl VcsRepository { branch_error_occurred: false, drivers, driver: None, + driver_kind: None, version_cache, empty_references: vec![], version_transport_exceptions: IndexMap::new(), @@ -186,22 +159,18 @@ impl VcsRepository { } pub fn get_repo_name(&mut self) -> String { - // Ensure the driver is initialized; we do not need a handle here. + // Ensure the driver is resolved so `driver_kind` is populated. 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(drivers_snapshot), - false, - ) - .map(|v| v.as_string().unwrap_or("").to_string()) - .filter(|s| !s.is_empty()) - .unwrap_or(driver_class); + // PHP: array_search(get_class($driver), $this->drivers), falling back to the class name. + let driver_type = match self.driver_kind { + Some(kind) => self + .drivers + .iter() + .find(|(_, v)| **v == kind) + .map(|(name, _)| name.clone()) + .unwrap_or_else(|| kind.php_class_name().to_string()), + None => String::new(), + }; format!( "vcs repo ({} {})", @@ -223,39 +192,56 @@ impl VcsRepository { return self.driver.as_mut(); } - if let Some(_class) = self.drivers.get(&self.r#type).cloned() { - // TODO(phase-b): dynamic class-string instantiation `new $class(...)` - let driver: Option<Box<dyn VcsDriverInterface>> = None; - if let Some(mut d) = driver { - let _ = d.initialize(); - self.driver = Some(d); - } + if let Some(kind) = self.drivers.get(&self.r#type).copied() { + let mut driver = kind.instantiate( + self.repo_config.clone(), + self.io.clone(), + self.config.clone(), + self.http_downloader.clone(), + self.process_executor.clone(), + ); + let _ = driver.initialize(); + self.driver = Some(driver); + self.driver_kind = Some(kind); return self.driver.as_mut(); } - for (_, _driver_class) in self.drivers.iter() { - // TODO(phase-b): static-method dispatch on class-string: `$driver::supports(...)` - let supports = false; - if supports { - // TODO(phase-b): dynamic class-string instantiation `new $driver(...)` - let d: Option<Box<dyn VcsDriverInterface>> = None; - if let Some(mut d) = d { - let _ = d.initialize(); - self.driver = Some(d); - } + let kinds: Vec<VcsDriverKind> = self.drivers.values().copied().collect(); + + for kind in &kinds { + if kind + .supports(self.io.clone(), self.config.clone(), &self.url, false) + .unwrap_or(false) + { + let mut driver = kind.instantiate( + self.repo_config.clone(), + self.io.clone(), + self.config.clone(), + self.http_downloader.clone(), + self.process_executor.clone(), + ); + let _ = driver.initialize(); + self.driver = Some(driver); + self.driver_kind = Some(*kind); return self.driver.as_mut(); } } - for (_, _driver_class) in self.drivers.iter() { - // TODO(phase-b): static-method dispatch on class-string: `$driver::supports(..., true)` - let supports = false; - if supports { - let d: Option<Box<dyn VcsDriverInterface>> = None; - if let Some(mut d) = d { - let _ = d.initialize(); - self.driver = Some(d); - } + for kind in &kinds { + if kind + .supports(self.io.clone(), self.config.clone(), &self.url, true) + .unwrap_or(false) + { + let mut driver = kind.instantiate( + self.repo_config.clone(), + self.io.clone(), + self.config.clone(), + self.http_downloader.clone(), + self.process_executor.clone(), + ); + let _ = driver.initialize(); + self.driver = Some(driver); + self.driver_kind = Some(*kind); return self.driver.as_mut(); } } @@ -706,14 +692,24 @@ impl VcsRepository { .as_ref() .unwrap() .load(package_data.clone(), None)?; - // TODO(phase-b): `$this->loader instanceof ValidatingArrayLoader` downcast - let loader_as_validating: Option<&ValidatingArrayLoader> = None; + // PHP: `$this->loader instanceof ValidatingArrayLoader`. + // TODO(phase-c): ValidatingArrayLoader does not implement LoaderInterface yet (its + // `load` needs `&mut self`, requiring a LoaderInterface redesign), so it can never be + // stored in `self.loader` and this downcast is always None. Production never calls + // setLoader so the default ArrayLoader matches upstream, but the InvalidPackageException + // path stays dead until the trait is reworked. + let loader_as_validating = self + .loader + .as_ref() + .and_then(|l| l.as_any().downcast_ref::<ValidatingArrayLoader>()); if let Some(validating) = loader_as_validating { - if count(&PhpMixed::Null) > 0 { - let _ = validating; - return Err( - InvalidPackageException::new(vec![], vec![], package_data).into() - ); + if !validating.get_warnings().is_empty() { + return Err(InvalidPackageException::new( + validating.get_errors().to_vec(), + validating.get_warnings().to_vec(), + package_data, + ) + .into()); } } self.inner.add_package(package)?; |
