aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/mozart-core/src/downloader/download_manager.rs
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-05-10 23:58:26 +0900
committernsfisis <nsfisis@gmail.com>2026-05-10 23:58:26 +0900
commit8871b923fa3df1935c263db155cb8bc3d59705cd (patch)
tree4c080d383c30a0d92229f9b411f1d94976a6e707 /crates/mozart-core/src/downloader/download_manager.rs
parent59bab6efee41a196b0d9d392167c536abbe068ba (diff)
downloadphp-mozart-8871b923fa3df1935c263db155cb8bc3d59705cd.tar.gz
php-mozart-8871b923fa3df1935c263db155cb8bc3d59705cd.tar.zst
php-mozart-8871b923fa3df1935c263db155cb8bc3d59705cd.zip
refactor(downloader): turn DownloadManager into downloader registry
Reshape DownloadManager from a hard-coded VCS match into a registry of DownloaderInterface instances keyed by source type, mirroring Composer's DownloadManager — with prefer-source/dist preferences, an IO handle, and a files cache. ArchiveManager now resolves dist sources through a shared DownloadManager instead of calling download_dist directly, and Composer::require / try_load take an IO so it flows through the factory wiring.
Diffstat (limited to 'crates/mozart-core/src/downloader/download_manager.rs')
-rw-r--r--crates/mozart-core/src/downloader/download_manager.rs135
1 files changed, 85 insertions, 50 deletions
diff --git a/crates/mozart-core/src/downloader/download_manager.rs b/crates/mozart-core/src/downloader/download_manager.rs
index c83bc64..68ae20e 100644
--- a/crates/mozart-core/src/downloader/download_manager.rs
+++ b/crates/mozart-core/src/downloader/download_manager.rs
@@ -1,40 +1,49 @@
-//! `DownloadManager` — pick the right [`VcsDownloader`] for a given
-//! [`LocalPackage`]. Mirrors `Composer\Downloader\DownloadManager`.
-
use crate::composer::{InstallationSource, LocalPackage};
-use crate::downloader::{GitDownloader, HgDownloader, SvnDownloader, VcsDownloader};
-use crate::vcs::process::ProcessExecutor;
-use crate::vcs::util::git::GitUtil;
-use crate::vcs::util::hg::HgUtil;
-use crate::vcs::util::svn::SvnUtil;
-use std::path::PathBuf;
+use crate::console::IoInterface;
+use crate::downloader::{DownloaderInterface, VcsDownloader};
+use crate::repository::cache::Cache;
+use crate::repository::downloader::{DownloadProgress, download_dist};
+use crate::util::Filesystem;
-/// 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`).
+/// ref: \Composer\Downloader\DownloadManager
pub struct DownloadManager {
- git_cache_dir: PathBuf,
+ #[allow(unused)]
+ io: std::sync::Arc<std::sync::Mutex<Box<dyn IoInterface>>>,
+ prefer_dist: bool,
+ prefer_source: bool,
+ #[allow(unused)]
+ package_preferences: Vec<(String, InstallationSource)>,
+ #[allow(unused)]
+ filesystem: Filesystem,
+ downloaders: indexmap::IndexMap<String, Box<dyn DownloaderInterface>>,
+ files_cache: Cache, // TODO: remove
}
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 new(
+ io: std::sync::Arc<std::sync::Mutex<Box<dyn IoInterface>>>,
+ prefer_source: bool,
+ filesystem: Filesystem,
+ files_cache: Cache,
+ ) -> Self {
+ Self {
+ io,
+ prefer_dist: false,
+ prefer_source,
+ package_preferences: Vec::new(),
+ filesystem,
+ downloaders: indexmap::IndexMap::new(),
+ files_cache,
+ }
}
- pub fn get_downloader_for_package(
- &self,
- package: &LocalPackage,
- ) -> Option<Box<dyn VcsDownloader>> {
+ pub fn set_downloader(&mut self, r#type: String, downloader: Box<dyn DownloaderInterface>) {
+ assert!(r#type.chars().all(|c| c.is_ascii_lowercase()));
+
+ self.downloaders.insert(r#type, downloader);
+ }
+
+ pub fn get_downloader_for_package(&self, package: &LocalPackage) -> Option<&dyn VcsDownloader> {
if package.package_type() == Some("metapackage") {
return None;
}
@@ -42,32 +51,58 @@ impl DownloadManager {
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,
- }
+ self.downloaders
+ .get(kind)
+ .and_then(|d| d.as_vcs_downloader())
}
}
}
+
+ /// Makes downloader prefer source installation over the dist.
+ pub fn set_prefer_source(&mut self, prefer_source: bool) {
+ self.prefer_source = prefer_source;
+ }
+
+ /// Makes downloader prefer dist installation over the source.
+ pub fn set_prefer_dist(&mut self, prefer_dist: bool) {
+ self.prefer_dist = prefer_dist;
+ }
+
+ pub async fn download_legacy(
+ &self,
+ url: &str,
+ expected_shasum: Option<&str>,
+ progress: Option<&mut DownloadProgress>,
+ ) -> anyhow::Result<Vec<u8>> {
+ download_dist(url, expected_shasum, progress, &self.files_cache).await
+ }
}
#[cfg(test)]
mod tests {
use super::*;
use crate::composer::PackageReference;
+ use crate::console::Console;
+ use crate::downloader::GitDownloader;
+ use crate::vcs::process::ProcessExecutor;
use serde_json::Value;
+ use std::path::PathBuf;
+ use std::sync::{Arc, Mutex};
+
+ fn make_dm() -> DownloadManager {
+ let io: Arc<Mutex<Box<dyn IoInterface>>> = Arc::new(Mutex::new(Box::new(Console::new(
+ 0, true, false, true, true,
+ ))
+ as Box<dyn IoInterface>));
+ let cache_dir = PathBuf::from("/tmp/mz-test-cache");
+ let cache = Cache::new(cache_dir.clone(), false);
+ let mut dm = DownloadManager::new(io, false, Filesystem::new(), cache);
+ dm.set_downloader(
+ "git".to_owned(),
+ Box::new(GitDownloader::new(ProcessExecutor::new(), cache_dir)),
+ );
+ dm
+ }
fn pkg(
installation_source: Option<InstallationSource>,
@@ -93,7 +128,7 @@ mod tests {
#[test]
fn metapackage_returns_none() {
- let dm = DownloadManager::new(PathBuf::from("/tmp/mz-test-cache"));
+ let dm = make_dm();
let mut p = pkg(Some(InstallationSource::Source), Some("git"));
// override type
p = LocalPackage::new(
@@ -111,28 +146,28 @@ mod tests {
#[test]
fn dist_install_returns_none() {
- let dm = DownloadManager::new(PathBuf::from("/tmp/mz-test-cache"));
+ let dm = make_dm();
let p = pkg(Some(InstallationSource::Dist), Some("git"));
assert!(dm.get_downloader_for_package(&p).is_none());
}
#[test]
fn source_install_with_git_returns_some() {
- let dm = DownloadManager::new(PathBuf::from("/tmp/mz-test-cache"));
+ let dm = make_dm();
let p = pkg(Some(InstallationSource::Source), Some("git"));
assert!(dm.get_downloader_for_package(&p).is_some());
}
#[test]
fn unknown_source_kind_returns_none() {
- let dm = DownloadManager::new(PathBuf::from("/tmp/mz-test-cache"));
+ let dm = make_dm();
let p = pkg(Some(InstallationSource::Source), Some("perforce"));
assert!(dm.get_downloader_for_package(&p).is_none());
}
#[test]
fn missing_installation_source_returns_none() {
- let dm = DownloadManager::new(PathBuf::from("/tmp/mz-test-cache"));
+ let dm = make_dm();
let p = pkg(None, Some("git"));
assert!(dm.get_downloader_for_package(&p).is_none());
}