diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-05-08 19:52:18 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-05-08 19:52:18 +0900 |
| commit | 5cb8fc4e306970764e84bb850da2c56f844c3b12 (patch) | |
| tree | 0d66f3129a26138fcfee9402616b24929c40a017 /crates/mozart-registry/src | |
| parent | d83b9ef48775aeb31ba1909b29d5470e6d0ddaaa (diff) | |
| download | php-mozart-5cb8fc4e306970764e84bb850da2c56f844c3b12.tar.gz php-mozart-5cb8fc4e306970764e84bb850da2c56f844c3b12.tar.zst php-mozart-5cb8fc4e306970764e84bb850da2c56f844c3b12.zip | |
fix(status): align with Composer's StatusCommand pipeline
Replace the dist-hash tree-diff implementation with Composer's VCS-level
status flow: three buckets (errors / unpushed_changes / vcs_version_changes)
populated via ChangeReportInterface / DvcsDownloaderInterface /
VcsCapableDownloaderInterface, and a bitfield exit code (1|2|4) instead
of always 1.
Supporting work:
- mozart-semver: add normalize_branch (VersionParser::normalizeBranch).
- mozart-vcs: extend VcsDownloader trait with unpushed_changes /
vcs_reference; port GitDownloader::getUnpushedChanges (HEAD-ref
discovery + git diff --name-status remote...branch + two-pass fetch);
fix git status invocation to use --untracked-files=no (Composer parity);
add hasMetadataRepository preconditions to git/hg/svn local_changes;
port VersionGuesser (git/hg/svn dispatch — Fossil omitted, feature
branch detection runs sequentially instead of via async promises).
- mozart-core: extend LocalPackage with pretty_version, package_type,
installation_source, source, dist, extra; add InstallationSource and
PackageReference. factory.rs reads them from installed.json.
- mozart-registry: new download_manager mirroring
DownloadManager::getDownloaderForPackage.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/mozart-registry/src')
| -rw-r--r-- | crates/mozart-registry/src/download_manager.rs | 140 | ||||
| -rw-r--r-- | crates/mozart-registry/src/lib.rs | 1 |
2 files changed, 141 insertions, 0 deletions
diff --git a/crates/mozart-registry/src/download_manager.rs b/crates/mozart-registry/src/download_manager.rs new file mode 100644 index 0000000..3e05517 --- /dev/null +++ b/crates/mozart-registry/src/download_manager.rs @@ -0,0 +1,140 @@ +//! `DownloadManager` — pick the right [`VcsDownloader`] for a given +//! [`LocalPackage`]. Mirrors `Composer\Downloader\DownloadManager`. + +use std::path::PathBuf; + +use mozart_core::composer::{InstallationSource, LocalPackage}; +use mozart_vcs::downloader::VcsDownloader; +use mozart_vcs::downloader::git::GitDownloader; +use mozart_vcs::downloader::hg::HgDownloader; +use mozart_vcs::downloader::svn::SvnDownloader; +use mozart_vcs::process::ProcessExecutor; +use mozart_vcs::util::git::GitUtil; +use mozart_vcs::util::hg::HgUtil; +use mozart_vcs::util::svn::SvnUtil; + +/// Selects a `VcsDownloader` for a package based on its installation source +/// and source type. Mirrors `DownloadManager::getDownloaderForPackage`: +/// +/// - `metapackage` → `None`. +/// - `installation-source: dist` → `None` (Composer would return a +/// `FileDownloader`-family object that does not implement +/// `ChangeReportInterface` / `DvcsDownloaderInterface`, so the status +/// command's `instanceof` checks all become no-ops; returning `None` +/// directly is the equivalent in our trait-object world). +/// - `installation-source: source` → the matching VCS downloader by +/// `source.type` (`git` / `hg` / `svn`). +pub struct DownloadManager { + git_cache_dir: PathBuf, +} + +impl DownloadManager { + /// `git_cache_dir`: where `GitUtil` should keep mirror clones (e.g. + /// `<vendor>/.cache/git`). + pub fn new(git_cache_dir: PathBuf) -> Self { + Self { git_cache_dir } + } + + pub fn for_package(&self, package: &LocalPackage) -> Option<Box<dyn VcsDownloader>> { + if package.package_type() == Some("metapackage") { + return None; + } + match package.installation_source()? { + InstallationSource::Dist => None, + InstallationSource::Source => { + let kind = package.source()?.kind.as_str(); + match kind { + "git" => { + let git_util = + GitUtil::new(ProcessExecutor::new(), self.git_cache_dir.clone()); + Some(Box::new(GitDownloader::new(git_util))) + } + "hg" => { + let hg_util = HgUtil::new(ProcessExecutor::new()); + Some(Box::new(HgDownloader::new(hg_util))) + } + "svn" => { + let svn_util = SvnUtil::new(ProcessExecutor::new()); + Some(Box::new(SvnDownloader::new(svn_util))) + } + _ => None, + } + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use mozart_core::composer::PackageReference; + use serde_json::Value; + + fn pkg( + installation_source: Option<InstallationSource>, + source_kind: Option<&str>, + ) -> LocalPackage { + let source = source_kind.map(|kind| PackageReference { + kind: kind.to_string(), + url: "https://example/repo".into(), + reference: Some("abc123".into()), + shasum: None, + }); + LocalPackage::new( + "vendor/pkg".into(), + "1.0.0".into(), + None, + Some("library".into()), + installation_source, + source, + None, + Value::Null, + ) + } + + #[test] + fn metapackage_returns_none() { + let dm = DownloadManager::new(PathBuf::from("/tmp/mz-test-cache")); + let mut p = pkg(Some(InstallationSource::Source), Some("git")); + // override type + p = LocalPackage::new( + "vendor/pkg".into(), + "1.0.0".into(), + None, + Some("metapackage".into()), + p.installation_source(), + p.source().cloned(), + None, + Value::Null, + ); + assert!(dm.for_package(&p).is_none()); + } + + #[test] + fn dist_install_returns_none() { + let dm = DownloadManager::new(PathBuf::from("/tmp/mz-test-cache")); + let p = pkg(Some(InstallationSource::Dist), Some("git")); + assert!(dm.for_package(&p).is_none()); + } + + #[test] + fn source_install_with_git_returns_some() { + let dm = DownloadManager::new(PathBuf::from("/tmp/mz-test-cache")); + let p = pkg(Some(InstallationSource::Source), Some("git")); + assert!(dm.for_package(&p).is_some()); + } + + #[test] + fn unknown_source_kind_returns_none() { + let dm = DownloadManager::new(PathBuf::from("/tmp/mz-test-cache")); + let p = pkg(Some(InstallationSource::Source), Some("perforce")); + assert!(dm.for_package(&p).is_none()); + } + + #[test] + fn missing_installation_source_returns_none() { + let dm = DownloadManager::new(PathBuf::from("/tmp/mz-test-cache")); + let p = pkg(None, Some("git")); + assert!(dm.for_package(&p).is_none()); + } +} diff --git a/crates/mozart-registry/src/lib.rs b/crates/mozart-registry/src/lib.rs index 36a12c6..9d72c36 100644 --- a/crates/mozart-registry/src/lib.rs +++ b/crates/mozart-registry/src/lib.rs @@ -1,6 +1,7 @@ pub mod browse_repos; pub mod cache; pub mod composer_repo; +pub mod download_manager; pub mod downloader; pub mod inline_package; pub mod installed; |
