diff options
Diffstat (limited to 'crates')
31 files changed, 353 insertions, 203 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()); } diff --git a/crates/mozart-core/src/downloader/downloader_interface.rs b/crates/mozart-core/src/downloader/downloader_interface.rs index 9c1b585..6184a0d 100644 --- a/crates/mozart-core/src/downloader/downloader_interface.rs +++ b/crates/mozart-core/src/downloader/downloader_interface.rs @@ -1 +1,5 @@ -pub trait DownloaderInterface {} +use crate::downloader::VcsDownloader; + +pub trait DownloaderInterface: Send + Sync { + fn as_vcs_downloader(&self) -> Option<&dyn VcsDownloader>; +} diff --git a/crates/mozart-core/src/downloader/git_downloader.rs b/crates/mozart-core/src/downloader/git_downloader.rs index d4d8c44..6e1351c 100644 --- a/crates/mozart-core/src/downloader/git_downloader.rs +++ b/crates/mozart-core/src/downloader/git_downloader.rs @@ -1,12 +1,11 @@ +use crate::downloader::{DownloaderInterface, VcsDownloader}; +use crate::vcs::process::ProcessExecutor; +use crate::vcs::util::git::GitUtil; use anyhow::Result; use regex::Regex; use std::path::Path; use std::sync::LazyLock; -use crate::downloader::VcsDownloader; -use crate::vcs::process::ProcessExecutor; -use crate::vcs::util::git::GitUtil; - /// Match `<hex> HEAD` lines in `git show-ref --head -d` output. static HEAD_REF_RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"(?im)^([a-f0-9]+) HEAD$").unwrap()); @@ -19,8 +18,10 @@ pub struct GitDownloader { } impl GitDownloader { - pub fn new(git_util: GitUtil) -> Self { - Self { git_util } + pub fn new(process: ProcessExecutor, cache_dir: std::path::PathBuf) -> Self { + Self { + git_util: GitUtil::new(process, cache_dir), + } } } @@ -235,6 +236,12 @@ impl VcsDownloader for GitDownloader { } } +impl DownloaderInterface for GitDownloader { + fn as_vcs_downloader(&self) -> Option<&dyn VcsDownloader> { + Some(self) + } +} + fn collect_show_ref(process: &ProcessExecutor, target: &Path) -> Result<Option<String>> { let output = process.execute(&["git", "show-ref", "--head", "-d"], Some(target))?; if output.status != 0 { diff --git a/crates/mozart-core/src/downloader/hg_downloader.rs b/crates/mozart-core/src/downloader/hg_downloader.rs index 9fb918e..dfe3546 100644 --- a/crates/mozart-core/src/downloader/hg_downloader.rs +++ b/crates/mozart-core/src/downloader/hg_downloader.rs @@ -1,16 +1,19 @@ +use crate::downloader::{DownloaderInterface, VcsDownloader}; +use crate::vcs::process::ProcessExecutor; +use crate::vcs::util::hg::HgUtil; use anyhow::Result; use std::path::Path; -use crate::{downloader::VcsDownloader, vcs::util::hg::HgUtil}; - /// Mercurial downloader using clone/pull/update. pub struct HgDownloader { hg_util: HgUtil, } impl HgDownloader { - pub fn new(hg_util: HgUtil) -> Self { - Self { hg_util } + pub fn new(process: ProcessExecutor) -> Self { + Self { + hg_util: HgUtil::new(process), + } } } @@ -82,3 +85,9 @@ impl VcsDownloader for HgDownloader { false } } + +impl DownloaderInterface for HgDownloader { + fn as_vcs_downloader(&self) -> Option<&dyn VcsDownloader> { + Some(self) + } +} diff --git a/crates/mozart-core/src/downloader/svn_downloader.rs b/crates/mozart-core/src/downloader/svn_downloader.rs index c78f9b7..690a090 100644 --- a/crates/mozart-core/src/downloader/svn_downloader.rs +++ b/crates/mozart-core/src/downloader/svn_downloader.rs @@ -1,11 +1,11 @@ +use crate::downloader::{DownloaderInterface, VcsDownloader}; +use crate::vcs::process::ProcessExecutor; +use crate::vcs::util::svn::SvnUtil; use anyhow::Result; use regex::Regex; use std::path::Path; use std::sync::LazyLock; -use crate::downloader::VcsDownloader; -use crate::vcs::util::svn::SvnUtil; - /// Match any non-`X` status line (mirror of Composer's /// `{^ *[^X ] +}m`). Ignores externals (`X` prefix). static SVN_STATUS_RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"(?m)^ *[^X ] +").unwrap()); @@ -16,8 +16,10 @@ pub struct SvnDownloader { } impl SvnDownloader { - pub fn new(svn_util: SvnUtil) -> Self { - Self { svn_util } + pub fn new(process: ProcessExecutor) -> Self { + Self { + svn_util: SvnUtil::new(process), + } } } @@ -83,3 +85,9 @@ impl VcsDownloader for SvnDownloader { false } } + +impl DownloaderInterface for SvnDownloader { + fn as_vcs_downloader(&self) -> Option<&dyn VcsDownloader> { + Some(self) + } +} diff --git a/crates/mozart-core/src/lib.rs b/crates/mozart-core/src/lib.rs index adf24a3..efb5528 100644 --- a/crates/mozart-core/src/lib.rs +++ b/crates/mozart-core/src/lib.rs @@ -21,6 +21,7 @@ pub mod repository; pub mod repository_utils; pub mod script_events; pub mod suggest; +pub mod util; pub mod validation; pub mod vcs; pub mod version_bumper; diff --git a/crates/mozart-core/src/package/archiver.rs b/crates/mozart-core/src/package/archiver.rs index 142edbd..a01a173 100644 --- a/crates/mozart-core/src/package/archiver.rs +++ b/crates/mozart-core/src/package/archiver.rs @@ -5,8 +5,8 @@ use std::fs; use std::io::Write as _; use std::path::{Path, PathBuf}; -pub mod manager; -pub use manager::{ArchiveManager, ArchivePackage}; +mod archive_manager; +pub use archive_manager::*; /// A compiled exclude pattern derived from a gitignore-style rule. pub struct ExcludePattern { diff --git a/crates/mozart-core/src/package/archiver/manager.rs b/crates/mozart-core/src/package/archiver/archive_manager.rs index bd5083e..b4f8e27 100644 --- a/crates/mozart-core/src/package/archiver/manager.rs +++ b/crates/mozart-core/src/package/archiver/archive_manager.rs @@ -1,3 +1,5 @@ +use crate::downloader::DownloadManager; + use super::{ ArchiveFormat, collect_archivable_files, create_archive, generate_archive_filename, parse_composer_excludes, parse_gitattributes, parse_gitignore_pattern, self_exclusion_patterns, @@ -108,20 +110,22 @@ fn read_archive_config(composer_json_path: &Path) -> anyhow::Result<(Option<Stri Ok((name, excludes)) } -/// Manages the creation of package archives. -/// -/// Mirrors Composer's `Composer\Package\Archiver\ArchiveManager`. -pub struct ArchiveManager; +trait ArchiverInterface: Send + Sync {} -impl Default for ArchiveManager { - fn default() -> Self { - Self::new() - } +/// ref: \Composer\Package\Archiver\ArchiveManager +pub struct ArchiveManager { + download_manager: std::sync::Arc<tokio::sync::Mutex<DownloadManager>>, + _archivers: Vec<Box<dyn ArchiverInterface>>, + _overwrite_files: bool, } impl ArchiveManager { - pub fn new() -> Self { - ArchiveManager + pub fn new(download_manager: std::sync::Arc<tokio::sync::Mutex<DownloadManager>>) -> Self { + Self { + download_manager, + _archivers: Vec::new(), + _overwrite_files: true, + } } /// Build the parts that make up a package archive's filename. @@ -170,7 +174,6 @@ impl ArchiveManager { target_dir: &Path, file_name: Option<&str>, ignore_filters: bool, - files_cache: &crate::repository::cache::Cache, ) -> anyhow::Result<PathBuf> { let archive_format = ArchiveFormat::parse(format).ok_or_else(|| { anyhow::anyhow!( @@ -179,7 +182,7 @@ impl ArchiveManager { ) })?; - let source = acquire_source(package, files_cache).await?; + let source = acquire_source(package, &self.download_manager).await?; let filename_base = if let Some(file_name) = file_name { file_name.to_string() @@ -228,7 +231,7 @@ impl ArchiveManager { /// composer.json. async fn acquire_source( package: &ArchivePackage, - files_cache: &crate::repository::cache::Cache, + download_manager: &std::sync::Arc<tokio::sync::Mutex<DownloadManager>>, ) -> anyhow::Result<AcquiredSource> { match package { ArchivePackage::Root { source_dir, .. } => { @@ -262,13 +265,11 @@ async fn acquire_source( let temp_dir = temp_base.join(&unique); std::fs::create_dir_all(&temp_dir)?; - let bytes = crate::repository::downloader::download_dist( - dist_url, - dist_shasum.as_deref(), - None, - files_cache, - ) - .await?; + let bytes = download_manager + .lock() + .await + .download_legacy(dist_url, dist_shasum.as_deref(), None) + .await?; match dist_type.as_str() { "zip" => crate::repository::downloader::extract_zip(&bytes, &temp_dir)?, diff --git a/crates/mozart-core/src/repository/downloader.rs b/crates/mozart-core/src/repository/downloader.rs index 56b3652..f2e33a7 100644 --- a/crates/mozart-core/src/repository/downloader.rs +++ b/crates/mozart-core/src/repository/downloader.rs @@ -80,7 +80,7 @@ impl DownloadProgress { /// Downloaded bytes are cached by URL in `files_cache`; cache hits skip the network request /// entirely. #[tracing::instrument(skip(expected_shasum, progress, files_cache))] -pub async fn download_dist( +pub(crate) async fn download_dist( url: &str, expected_shasum: Option<&str>, progress: Option<&mut DownloadProgress>, diff --git a/crates/mozart-core/src/repository/installer_executor/filesystem.rs b/crates/mozart-core/src/repository/installer_executor/filesystem.rs index 2b34e02..05143a0 100644 --- a/crates/mozart-core/src/repository/installer_executor/filesystem.rs +++ b/crates/mozart-core/src/repository/installer_executor/filesystem.rs @@ -146,22 +146,18 @@ fn install_from_source( match source_type { "git" => { let process = crate::vcs::process::ProcessExecutor::new(); - let git_util = - crate::vcs::util::git::GitUtil::new(process, vendor_dir.join(".cache").join("git")); - let downloader = GitDownloader::new(git_util); + let downloader = GitDownloader::new(process, vendor_dir.join(".cache").join("git")); downloader.download(url, reference, &target)?; downloader.install(url, reference, &target)?; } "svn" => { let process = crate::vcs::process::ProcessExecutor::new(); - let svn_util = crate::vcs::util::svn::SvnUtil::new(process); - let downloader = SvnDownloader::new(svn_util); + let downloader = SvnDownloader::new(process); downloader.install(url, reference, &target)?; } "hg" => { let process = crate::vcs::process::ProcessExecutor::new(); - let hg_util = crate::vcs::util::hg::HgUtil::new(process); - let downloader = HgDownloader::new(hg_util); + let downloader = HgDownloader::new(process); downloader.install(url, reference, &target)?; } _ => { diff --git a/crates/mozart-core/src/util.rs b/crates/mozart-core/src/util.rs new file mode 100644 index 0000000..ced0e0e --- /dev/null +++ b/crates/mozart-core/src/util.rs @@ -0,0 +1,3 @@ +mod filesystem; + +pub use filesystem::*; diff --git a/crates/mozart-core/src/util/filesystem.rs b/crates/mozart-core/src/util/filesystem.rs new file mode 100644 index 0000000..efcb0fb --- /dev/null +++ b/crates/mozart-core/src/util/filesystem.rs @@ -0,0 +1,8 @@ +#[derive(Default)] +pub struct Filesystem; + +impl Filesystem { + pub fn new() -> Self { + Self {} + } +} diff --git a/crates/mozart-core/tests/git_driver_test.rs b/crates/mozart-core/tests/git_driver_test.rs index 2b2db57..8d5ba1a 100644 --- a/crates/mozart-core/tests/git_driver_test.rs +++ b/crates/mozart-core/tests/git_driver_test.rs @@ -2,7 +2,6 @@ use mozart_core::downloader::{GitDownloader, VcsDownloader}; use mozart_core::repository::vcs::{DriverConfig, DriverType, create_driver, detect_driver}; use mozart_core::vcs::process::ProcessExecutor; use mozart_core::vcs::repository::VcsRepository; -use mozart_core::vcs::util::git::GitUtil; use std::path::Path; use std::process::Command; use tempfile::TempDir; @@ -148,9 +147,7 @@ fn test_git_downloader() { let install_dir = TempDir::new().unwrap(); create_test_repo(repo_dir.path()); - let process = ProcessExecutor::new(); - let git_util = GitUtil::new(process, cache_dir.path().join("git")); - let downloader = GitDownloader::new(git_util); + let downloader = GitDownloader::new(ProcessExecutor::new(), cache_dir.path().join("git")); let url = repo_dir.path().to_str().unwrap(); let target = install_dir.path().join("test-package"); @@ -203,9 +200,7 @@ fn test_git_downloader_unpushed_changes() { let install_dir = TempDir::new().unwrap(); create_test_repo(repo_dir.path()); - let process = ProcessExecutor::new(); - let git_util = GitUtil::new(process, cache_dir.path().join("git")); - let downloader = GitDownloader::new(git_util); + let downloader = GitDownloader::new(ProcessExecutor::new(), cache_dir.path().join("git")); let url = repo_dir.path().to_str().unwrap(); let target = install_dir.path().join("test-package"); diff --git a/crates/mozart/src/commands/archive.rs b/crates/mozart/src/commands/archive.rs index e1c0fa3..ed7de0e 100644 --- a/crates/mozart/src/commands/archive.rs +++ b/crates/mozart/src/commands/archive.rs @@ -1,9 +1,11 @@ use crate::composer::Composer; +use crate::factory::{create_archive_manager, create_download_manager}; use clap::Args; +use mozart_core::config::Config; use mozart_core::console::IoInterface; use mozart_core::console_writeln; use mozart_core::factory::create_config; -use mozart_core::package::archiver::{ArchiveManager, ArchivePackage}; +use mozart_core::package::archiver::ArchivePackage; use std::borrow::Cow; use std::path::{Path, PathBuf}; @@ -39,7 +41,7 @@ pub async fn execute( ) -> anyhow::Result<()> { let working_dir = cli.working_dir()?; - let composer = Composer::try_load(&working_dir)?; + let composer = Composer::try_load(io.clone(), &working_dir)?; let config = if let Some(composer) = &composer { Cow::Borrowed(composer.config()) } else { @@ -50,7 +52,8 @@ pub async fn execute( let dir = args.dir.as_deref().unwrap_or(&config.archive_dir); archive( - &io, + io, + &config, args.package.as_deref(), args.version.as_deref(), format, @@ -59,13 +62,15 @@ pub async fn execute( args.ignore_filters, &working_dir, cli.no_cache, + composer.as_ref(), ) .await } #[allow(clippy::too_many_arguments)] async fn archive( - io: &std::sync::Arc<std::sync::Mutex<Box<dyn IoInterface>>>, + io: std::sync::Arc<std::sync::Mutex<Box<dyn IoInterface>>>, + config: &Config, package_name: Option<&str>, version: Option<&str>, format: &str, @@ -74,15 +79,24 @@ async fn archive( ignore_filters: bool, working_dir: &Path, no_cache: bool, + composer: Option<&Composer>, ) -> anyhow::Result<()> { let cache_config = mozart_core::repository::cache::build_cache_config(no_cache); let repo_cache = mozart_core::repository::cache::Cache::repo(&cache_config); - let files_cache = mozart_core::repository::cache::Cache::files(&cache_config); - let archive_manager = ArchiveManager::new(); + let archive_manager = if let Some(composer) = composer { + Cow::Borrowed(composer.archive_manager()) + } else { + let download_manager = std::sync::Arc::new(tokio::sync::Mutex::new( + create_download_manager(io.clone(), config), + )); + Cow::Owned(std::sync::Arc::new(tokio::sync::Mutex::new( + create_archive_manager(download_manager), + ))) + }; let package = if let Some(package_name) = package_name { - select_package(io, package_name, version, &repo_cache).await? + select_package(&io, package_name, version, &repo_cache).await? } else { load_root_package(working_dir)? }; @@ -97,14 +111,9 @@ async fn archive( .unwrap() .info(&format!("Creating the archive into \"{}\".", dest)); let package_path = archive_manager - .archive( - &package, - format, - &dest_dir, - file_name, - ignore_filters, - &files_cache, - ) + .lock() + .await + .archive(&package, format, &dest_dir, file_name, ignore_filters) .await?; let absolute = package_path.display().to_string(); diff --git a/crates/mozart/src/commands/audit.rs b/crates/mozart/src/commands/audit.rs index 9672d57..0b84ccf 100644 --- a/crates/mozart/src/commands/audit.rs +++ b/crates/mozart/src/commands/audit.rs @@ -44,7 +44,7 @@ pub async fn execute( let working_dir = cli.working_dir()?; // Load Composer state (reads composer.json + config) - let composer = Composer::require(&working_dir)?; + let composer = Composer::require(io.clone(), &working_dir)?; // Parse audit config from composer.json's config.audit section let audit_config = AuditConfig::from_config(composer.config(), true, AuditFormat::Table)?; diff --git a/crates/mozart/src/commands/browse.rs b/crates/mozart/src/commands/browse.rs index ada3b8b..e3eae22 100644 --- a/crates/mozart/src/commands/browse.rs +++ b/crates/mozart/src/commands/browse.rs @@ -32,7 +32,7 @@ pub async fn execute( let working_dir = cli.working_dir()?; let cache = Cache::repo(&build_cache_config(cli.no_cache)); - let composer = Composer::try_load(&working_dir)?; + let composer = Composer::try_load(io.clone(), &working_dir)?; let repos = build_repos(composer.as_ref(), cache); let packages: Vec<String> = if args.packages.is_empty() { diff --git a/crates/mozart/src/commands/bump.rs b/crates/mozart/src/commands/bump.rs index dc7a5e7..351be01 100644 --- a/crates/mozart/src/commands/bump.rs +++ b/crates/mozart/src/commands/bump.rs @@ -35,7 +35,7 @@ pub async fn execute( io: std::sync::Arc<std::sync::Mutex<Box<dyn IoInterface>>>, ) -> anyhow::Result<()> { let working_dir = cli.working_dir()?; - let composer = Composer::require(&working_dir)?; + let composer = Composer::require(io.clone(), &working_dir)?; let exit = do_bump( io, diff --git a/crates/mozart/src/commands/clear_cache.rs b/crates/mozart/src/commands/clear_cache.rs index dbca5c8..c0f2957 100644 --- a/crates/mozart/src/commands/clear_cache.rs +++ b/crates/mozart/src/commands/clear_cache.rs @@ -18,7 +18,7 @@ pub async fn execute( cli: &super::Cli, io: std::sync::Arc<std::sync::Mutex<Box<dyn IoInterface>>>, ) -> anyhow::Result<()> { - let composer = Composer::try_load(cli.working_dir()?)?; + let composer = Composer::try_load(io.clone(), cli.working_dir()?)?; let config = if let Some(composer) = &composer { Cow::Borrowed(composer.config()) } else { diff --git a/crates/mozart/src/commands/create_project.rs b/crates/mozart/src/commands/create_project.rs index 276bd3a..08e146e 100644 --- a/crates/mozart/src/commands/create_project.rs +++ b/crates/mozart/src/commands/create_project.rs @@ -2,6 +2,7 @@ use clap::Args; use indexmap::IndexMap; use mozart_core::console::IoInterface; use mozart_core::console_format; +use mozart_core::factory::create_config; use mozart_core::package::{self, Stability}; use mozart_core::repository::downloader; use mozart_core::repository::lockfile; @@ -11,6 +12,8 @@ use mozart_core::repository::version; use mozart_core::validation; use std::path::{Path, PathBuf}; +use crate::factory::create_download_manager; + #[derive(Args)] pub struct CreateProjectArgs { /// Package name to install @@ -337,7 +340,7 @@ pub async fn execute( let secure_http = !args.no_secure_http; install_project( - &io, + io, cli, args, args.package.as_deref(), @@ -362,7 +365,7 @@ pub async fn execute( #[allow(clippy::too_many_arguments)] async fn install_project( - io: &std::sync::Arc<std::sync::Mutex<Box<dyn IoInterface>>>, + io: std::sync::Arc<std::sync::Mutex<Box<dyn IoInterface>>>, cli: &super::Cli, args: &CreateProjectArgs, package_name: Option<&str>, @@ -395,7 +398,7 @@ async fn install_project( let root_result = if let Some(name) = package_name { Some( install_root_package( - io, + io.clone(), cli, args, name, @@ -647,7 +650,7 @@ async fn install_project( #[allow(clippy::too_many_arguments)] async fn install_root_package( - io: &std::sync::Arc<std::sync::Mutex<Box<dyn IoInterface>>>, + io: std::sync::Arc<std::sync::Mutex<Box<dyn IoInterface>>>, cli: &super::Cli, _args: &CreateProjectArgs, package_name: &str, @@ -733,7 +736,6 @@ async fn install_root_package( // --- Find the best candidate matching constraint + stability --- let cache_config = mozart_core::repository::cache::build_cache_config(cli.no_cache); let repo_cache = mozart_core::repository::cache::Cache::repo(&cache_config); - let files_cache = mozart_core::repository::cache::Cache::files(&cache_config); let versions = packagist::fetch_package_versions(&name, &repo_cache).await?; @@ -786,13 +788,11 @@ async fn install_root_package( let mut progress = downloader::DownloadProgress::new(!no_progress, format!("{name} ({concrete_version})")); - let bytes = downloader::download_dist( - &dist.url, - dist.shasum.as_deref(), - Some(&mut progress), - &files_cache, - ) - .await?; + let config = create_config()?; + let download_manager = create_download_manager(io.clone(), &config); + let bytes = download_manager + .download_legacy(&dist.url, dist.shasum.as_deref(), Some(&mut progress)) + .await?; progress.finish(); diff --git a/crates/mozart/src/commands/diagnose.rs b/crates/mozart/src/commands/diagnose.rs index d139467..a1d655f 100644 --- a/crates/mozart/src/commands/diagnose.rs +++ b/crates/mozart/src/commands/diagnose.rs @@ -362,7 +362,7 @@ pub async fn execute( let mut exit_code: i32 = 0; - let composer = Composer::try_load(&working_dir)?; + let composer = Composer::try_load(io.clone(), &working_dir)?; let config: Cow<'_, Config> = if let Some(c) = &composer { Cow::Borrowed(c.config()) } else { diff --git a/crates/mozart/src/commands/dump_autoload.rs b/crates/mozart/src/commands/dump_autoload.rs index b66b7fc..211b46e 100644 --- a/crates/mozart/src/commands/dump_autoload.rs +++ b/crates/mozart/src/commands/dump_autoload.rs @@ -57,7 +57,7 @@ pub async fn execute( cli: &super::Cli, io: std::sync::Arc<std::sync::Mutex<Box<dyn IoInterface>>>, ) -> anyhow::Result<()> { - let composer = Composer::require(cli.working_dir()?)?; + let composer = Composer::require(io.clone(), cli.working_dir()?)?; let installation_manager = composer.installation_manager(); let local_repo = composer.repository_manager().local_repository(); diff --git a/crates/mozart/src/commands/exec.rs b/crates/mozart/src/commands/exec.rs index e1a9e2b..f2a9c55 100644 --- a/crates/mozart/src/commands/exec.rs +++ b/crates/mozart/src/commands/exec.rs @@ -25,7 +25,7 @@ pub async fn execute( ) -> anyhow::Result<()> { let working_dir = cli.working_dir()?; - let composer = Composer::require(&working_dir)?; + let composer = Composer::require(io.clone(), &working_dir)?; let bin_dir = resolve_bin_dir(&working_dir, &composer); if args.list || args.binary.is_none() { @@ -150,7 +150,15 @@ fn get_binaries(composer: &Composer, bin_dir: &Path) -> Vec<(String, bool)> { #[cfg(test)] mod tests { use super::*; + use mozart_core::console::Console; use std::fs; + use std::sync::{Arc, Mutex}; + + fn io() -> Arc<Mutex<Box<dyn IoInterface>>> { + Arc::new(Mutex::new( + Box::new(Console::new(0, true, false, true, true)) as Box<dyn IoInterface>, + )) + } #[test] fn test_resolve_bin_dir_default() { @@ -158,7 +166,7 @@ mod tests { let composer_json = dir.path().join("composer.json"); fs::write(&composer_json, r#"{"name": "test/pkg", "require": {}}"#).unwrap(); - let composer = Composer::require(dir.path()).unwrap(); + let composer = Composer::require(io(), dir.path()).unwrap(); let result = resolve_bin_dir(dir.path(), &composer); assert_eq!(result, dir.path().join("vendor/bin")); } @@ -173,7 +181,7 @@ mod tests { ) .unwrap(); - let composer = Composer::require(dir.path()).unwrap(); + let composer = Composer::require(io(), dir.path()).unwrap(); let result = resolve_bin_dir(dir.path(), &composer); assert_eq!(result, dir.path().join("libs/bin")); } @@ -188,7 +196,7 @@ mod tests { ) .unwrap(); - let composer = Composer::require(dir.path()).unwrap(); + let composer = Composer::require(io(), dir.path()).unwrap(); let result = resolve_bin_dir(dir.path(), &composer); assert_eq!(result, dir.path().join("scripts")); } @@ -203,7 +211,7 @@ mod tests { ) .unwrap(); - let composer = Composer::require(dir.path()).unwrap(); + let composer = Composer::require(io(), dir.path()).unwrap(); let result = resolve_bin_dir(dir.path(), &composer); assert_eq!(result, dir.path().join("packages/commands")); } @@ -223,7 +231,7 @@ mod tests { ) .unwrap(); - let composer = Composer::require(dir.path()).unwrap(); + let composer = Composer::require(io(), dir.path()).unwrap(); let binaries = get_binaries(&composer, &bin_dir); let names: Vec<&str> = binaries.iter().map(|(n, _)| n.as_str()).collect(); assert!(names.contains(&"phpunit")); @@ -249,7 +257,7 @@ mod tests { ) .unwrap(); - let composer = Composer::require(dir.path()).unwrap(); + let composer = Composer::require(io(), dir.path()).unwrap(); let binaries = get_binaries(&composer, &bin_dir); let names: Vec<&str> = binaries.iter().map(|(n, _)| n.as_str()).collect(); assert!(names.contains(&"phpunit")); @@ -268,7 +276,7 @@ mod tests { ) .unwrap(); - let composer = Composer::require(dir.path()).unwrap(); + let composer = Composer::require(io(), dir.path()).unwrap(); let binaries = get_binaries(&composer, &bin_dir); let names: Vec<&str> = binaries.iter().map(|(n, _)| n.as_str()).collect(); assert!(names.contains(&"my-tool")); @@ -291,7 +299,7 @@ mod tests { ) .unwrap(); - let composer = Composer::require(dir.path()).unwrap(); + let composer = Composer::require(io(), dir.path()).unwrap(); let binaries = get_binaries(&composer, &bin_dir); assert!(binaries.is_empty()); } @@ -306,7 +314,7 @@ mod tests { .unwrap(); let bin_dir = dir.path().join("vendor/bin"); - let composer = Composer::require(dir.path()).unwrap(); + let composer = Composer::require(io(), dir.path()).unwrap(); let binaries = get_binaries(&composer, &bin_dir); assert!( binaries.is_empty(), @@ -323,7 +331,7 @@ mod tests { ) .unwrap(); - let composer = Composer::require(dir.path()).unwrap(); + let composer = Composer::require(io(), dir.path()).unwrap(); let bin_dir = resolve_bin_dir(dir.path(), &composer); // No binaries exist — looking up a name should find nothing diff --git a/crates/mozart/src/commands/fund.rs b/crates/mozart/src/commands/fund.rs index 6b0cd67..677137c 100644 --- a/crates/mozart/src/commands/fund.rs +++ b/crates/mozart/src/commands/fund.rs @@ -31,7 +31,7 @@ pub async fn execute( } let working_dir = cli.working_dir()?; - let composer = Composer::require(&working_dir)?; + let composer = Composer::require(io.clone(), &working_dir)?; let installed = InstalledPackages::read(composer.installation_manager().vendor_dir())?; // Configured remote repositories from `composer.json` are not yet wired diff --git a/crates/mozart/src/commands/licenses.rs b/crates/mozart/src/commands/licenses.rs index aecfe86..73f2018 100644 --- a/crates/mozart/src/commands/licenses.rs +++ b/crates/mozart/src/commands/licenses.rs @@ -79,7 +79,7 @@ pub async fn execute( ); } - let composer = Composer::require(&working_dir)?; + let composer = Composer::require(io.clone(), &working_dir)?; // TODO(plugins): dispatch CommandEvent for `licenses`. diff --git a/crates/mozart/src/commands/reinstall.rs b/crates/mozart/src/commands/reinstall.rs index d9156bc..ca23a64 100644 --- a/crates/mozart/src/commands/reinstall.rs +++ b/crates/mozart/src/commands/reinstall.rs @@ -66,7 +66,7 @@ pub async fn execute( io: std::sync::Arc<std::sync::Mutex<Box<dyn IoInterface>>>, ) -> anyhow::Result<()> { let working_dir = cli.working_dir()?; - let composer = Composer::require(&working_dir)?; + let composer = Composer::require(io.clone(), &working_dir)?; let local_repo = composer.repository_manager().local_repository(); // Selection: mirrors `ReinstallCommand::execute` lines 79-110. diff --git a/crates/mozart/src/commands/run_script.rs b/crates/mozart/src/commands/run_script.rs index c2e52a6..ab3700d 100644 --- a/crates/mozart/src/commands/run_script.rs +++ b/crates/mozart/src/commands/run_script.rs @@ -42,7 +42,7 @@ pub async fn execute( let working_dir = cli.working_dir()?; if args.list { - Composer::require(&working_dir)?; + Composer::require(io.clone(), &working_dir)?; let (scripts, descriptions) = load_scripts(&working_dir)?; return list_scripts(&scripts, &descriptions, io.clone()); } @@ -58,7 +58,7 @@ pub async fn execute( anyhow::bail!("Script \"{}\" cannot be run with this command", script); } - let composer = Composer::require(&working_dir)?; + let composer = Composer::require(io.clone(), &working_dir)?; let dev_mode = args.dev || !args.no_dev; let (scripts, _descriptions) = load_scripts(&working_dir)?; diff --git a/crates/mozart/src/commands/status.rs b/crates/mozart/src/commands/status.rs index d538138..cb33223 100644 --- a/crates/mozart/src/commands/status.rs +++ b/crates/mozart/src/commands/status.rs @@ -17,11 +17,11 @@ pub async fn execute( io: std::sync::Arc<std::sync::Mutex<Box<dyn IoInterface>>>, ) -> anyhow::Result<()> { // init repos - let composer = Composer::require(cli.working_dir()?)?; + let composer = Composer::require(io.clone(), cli.working_dir()?)?; let installed_repo = composer.repository_manager().local_repository(); - let dm = composer.download_manager(); + let dm = composer.download_manager().lock().await; let im = composer.installation_manager(); let mut errors = Vec::new(); diff --git a/crates/mozart/src/commands/update.rs b/crates/mozart/src/commands/update.rs index 362fad5..f482272 100644 --- a/crates/mozart/src/commands/update.rs +++ b/crates/mozart/src/commands/update.rs @@ -1747,7 +1747,7 @@ pub async fn run( let mode = bump_mode.as_deref().unwrap_or("all"); let dev_only = mode == "dev"; let no_dev_only = mode == "no-dev"; - let bump_composer = Composer::require(working_dir)?; + let bump_composer = Composer::require(io.clone(), working_dir)?; let bump_exit = super::bump::do_bump( io.clone(), &bump_composer, diff --git a/crates/mozart/src/commands/validate.rs b/crates/mozart/src/commands/validate.rs index a6dbf91..23925c5 100644 --- a/crates/mozart/src/commands/validate.rs +++ b/crates/mozart/src/commands/validate.rs @@ -104,7 +104,9 @@ pub async fn execute( // Load the Composer project state (optional — used for typed config, // locker, and the repository/installation managers). Mirrors // `ValidateCommand::createComposerInstance($file)`. - let composer = Composer::try_load_from_file(&file).ok().flatten(); + let composer = Composer::try_load_from_file(io.clone(), &file) + .ok() + .flatten(); // Determine whether to check the lock file using the typed config when // available, falling back to a raw JSON read for paths where the Composer diff --git a/crates/mozart/src/composer.rs b/crates/mozart/src/composer.rs index 9fd8b77..c73902e 100644 --- a/crates/mozart/src/composer.rs +++ b/crates/mozart/src/composer.rs @@ -11,13 +11,14 @@ //! `Composer\Command\BaseCommand::tryComposer()` for the upstream contract //! that [`Composer::require`] and [`Composer::try_load`] are modelled on. -use std::path::{Path, PathBuf}; - use crate::factory::create_composer; use mozart_core::composer::{AutoloadGenerator, InstallationManager, Locker, RepositoryManager}; use mozart_core::config::Config; +use mozart_core::console::IoInterface; use mozart_core::downloader::DownloadManager; use mozart_core::package::RootPackageData; +use mozart_core::package::archiver::ArchiveManager; +use std::path::{Path, PathBuf}; /// Project-level Composer state. Mirrors `Composer\PartialComposer` / /// `Composer\Composer` in PHP, exposing the subset of getters command @@ -30,9 +31,10 @@ pub struct Composer { package: RootPackageData, repository_manager: RepositoryManager, installation_manager: InstallationManager, - download_manager: DownloadManager, + download_manager: std::sync::Arc<tokio::sync::Mutex<DownloadManager>>, autoload_generator: AutoloadGenerator, locker: Locker, + archive_manager: std::sync::Arc<tokio::sync::Mutex<ArchiveManager>>, } impl Composer { @@ -48,9 +50,10 @@ impl Composer { package: RootPackageData, repository_manager: RepositoryManager, installation_manager: InstallationManager, - download_manager: DownloadManager, + download_manager: std::sync::Arc<tokio::sync::Mutex<DownloadManager>>, autoload_generator: AutoloadGenerator, locker: Locker, + archive_manager: std::sync::Arc<tokio::sync::Mutex<ArchiveManager>>, ) -> Self { Self { project_dir, @@ -61,13 +64,17 @@ impl Composer { download_manager, autoload_generator, locker, + archive_manager, } } /// Load Composer state for `project_dir`, requiring a composer.json. /// Mirrors `BaseCommand::requireComposer()`, which delegates to /// `Factory::createComposer` after asserting the file exists. - pub fn require(project_dir: impl Into<PathBuf>) -> anyhow::Result<Self> { + pub fn require( + io: std::sync::Arc<std::sync::Mutex<Box<dyn IoInterface>>>, + project_dir: impl Into<PathBuf>, + ) -> anyhow::Result<Self> { let project_dir = project_dir.into(); let composer_json = project_dir.join("composer.json"); if !composer_json.exists() { @@ -76,31 +83,37 @@ impl Composer { project_dir.display() ); } - create_composer(project_dir, &composer_json) + create_composer(io, project_dir, &composer_json) } /// Load Composer state for `project_dir`, returning `None` if no /// composer.json exists. Other I/O or parse errors still propagate. /// Mirrors `BaseCommand::tryComposer()`. - pub fn try_load(project_dir: impl Into<PathBuf>) -> anyhow::Result<Option<Self>> { + pub fn try_load( + io: std::sync::Arc<std::sync::Mutex<Box<dyn IoInterface>>>, + project_dir: impl Into<PathBuf>, + ) -> anyhow::Result<Option<Self>> { let project_dir = project_dir.into(); let composer_json = project_dir.join("composer.json"); if !composer_json.exists() { return Ok(None); } - create_composer(project_dir, &composer_json).map(Some) + create_composer(io, project_dir, &composer_json).map(Some) } /// Load Composer state keyed on a specific `composer.json` file, deriving /// the project directory from `file.parent()`. Mirrors /// `ValidateCommand::createComposerInstance($file)` — Composer keys /// instances on a file rather than a directory for non-default paths. - pub fn try_load_from_file(file: &Path) -> anyhow::Result<Option<Self>> { + pub fn try_load_from_file( + io: std::sync::Arc<std::sync::Mutex<Box<dyn IoInterface>>>, + file: &Path, + ) -> anyhow::Result<Option<Self>> { let project_dir = file .parent() .map(Path::to_path_buf) .unwrap_or_else(|| PathBuf::from(".")); - Self::try_load(project_dir) + Self::try_load(io, project_dir) } pub fn project_dir(&self) -> &Path { @@ -130,7 +143,7 @@ impl Composer { &self.installation_manager } - pub fn download_manager(&self) -> &DownloadManager { + pub fn download_manager(&self) -> &std::sync::Arc<tokio::sync::Mutex<DownloadManager>> { &self.download_manager } @@ -149,4 +162,8 @@ impl Composer { pub fn locker(&self) -> &Locker { &self.locker } + + pub fn archive_manager(&self) -> &std::sync::Arc<tokio::sync::Mutex<ArchiveManager>> { + &self.archive_manager + } } diff --git a/crates/mozart/src/factory.rs b/crates/mozart/src/factory.rs index b88e088..41f157a 100644 --- a/crates/mozart/src/factory.rs +++ b/crates/mozart/src/factory.rs @@ -14,39 +14,21 @@ use mozart_core::composer::{ AutoloadGenerator, InstallationManager, InstallationSource, LocalPackage, LocalRepository, Locker, PackageReference, RepositoryManager, }; -use mozart_core::config::resolve_references; -use mozart_core::downloader::DownloadManager; +use mozart_core::config::{Config, resolve_references}; +use mozart_core::console::IoInterface; +use mozart_core::downloader::{DownloadManager, GitDownloader, HgDownloader, SvnDownloader}; use mozart_core::factory::create_config; +use mozart_core::package::archiver::ArchiveManager; use mozart_core::package::{RootPackageData, read_from_file}; +use mozart_core::repository::cache::Cache; +use mozart_core::util::Filesystem; +use mozart_core::vcs::process::ProcessExecutor; -/// Rust port of `Factory::createComposer()`. +/// Creates a Composer instance. /// -/// Builds the project-level [`Composer`]: -/// 1. Read `composer.json` from `composer_json` and load it into both -/// the merged [`Config`] (overlaying [`create_config`]) and the -/// untyped [`crate::package::RawPackageData`]. -/// 2. Resolve all `{$home}` / `{$vendor-dir}` placeholders via -/// [`resolve_references`]. -/// 3. Resolve `vendor-dir` against `project_dir` if it is relative, so -/// the installation manager hands back absolute paths -/// (`Factory::createComposer` does the same via -/// `Filesystem::isAbsolutePath`). -/// 4. Wire up the [`InstallationManager`] and a [`RepositoryManager`] -/// whose local repository is populated from -/// `vendor/composer/installed.json` — the same role -/// `Factory::addLocalRepository` plays in PHP. -/// 5. Construct a fresh [`AutoloadGenerator`] with PHP defaults -/// (`new AutoloadGenerator($eventDispatcher, $io)` in PHP, minus the -/// not-yet-ported event dispatcher and IO dependencies). -/// 6. Construct a [`Locker`] pointed at `composer.lock` next to the -/// composer.json — same as `Factory::createComposer`'s -/// `new Locker($io, new JsonFile($lockFile, …), $im, $contents)`, -/// minus the IO/installation-manager/contents dependencies that -/// only matter once we port `setLockData`. -/// -/// The plugin manager, download manager, and event dispatcher that -/// `Factory::createComposer` also wires up are not yet ported. +/// ref: \Composer\Factory\createComposer() pub fn create_composer( + io: std::sync::Arc<std::sync::Mutex<Box<dyn IoInterface>>>, project_dir: std::path::PathBuf, composer_json: &std::path::Path, ) -> anyhow::Result<Composer> { @@ -78,7 +60,9 @@ pub fn create_composer( let repository_manager = RepositoryManager::new(LocalRepository::with_dev_mode(local_packages, dev_mode)); let installation_manager = InstallationManager::new(vendor_dir.clone()); - let download_manager = DownloadManager::new(vendor_dir.join(".cache").join("git")); + let dm = std::sync::Arc::new(tokio::sync::Mutex::new(create_download_manager( + io, &config, + ))); let autoload_generator = AutoloadGenerator::new(); // Mirrors `Factory::createComposer`'s lock-file path: the lockfile @@ -96,6 +80,7 @@ pub fn create_composer( .unwrap_or_else(|| "composer.lock".to_string()), ); let locker = Locker::new(lock_file_path); + let am = std::sync::Arc::new(tokio::sync::Mutex::new(create_archive_manager(dm.clone()))); Ok(Composer::new( project_dir, @@ -103,12 +88,66 @@ pub fn create_composer( package, repository_manager, installation_manager, - download_manager, + dm, autoload_generator, locker, + am, )) } +pub fn create_download_manager( + io: std::sync::Arc<std::sync::Mutex<Box<dyn IoInterface>>>, + config: &Config, +) -> DownloadManager { + let cache = if config.cache_files_ttl > 0 { + Some(Cache::new( + std::path::PathBuf::from(config.cache_files_dir.clone()), + config.cache_read_only, + )) + } else { + None + }; + let cache = cache.unwrap(); + + let mut dm = DownloadManager::new(io, false, Filesystem::new(), cache); + if let serde_json::Value::String(preferred) = &config.preferred_install { + match preferred.as_str() { + "dist" => dm.set_prefer_dist(true), + "source" => dm.set_prefer_source(true), + _ => (), + } + } + + if let serde_json::Value::Object(preferred) = &config.preferred_install { + _ = preferred; + unimplemented!() + } + + dm.set_downloader( + "git".to_owned(), + Box::new(GitDownloader::new( + ProcessExecutor::new(), + std::path::PathBuf::from(config.cache_files_dir.clone()), + )), + ); + dm.set_downloader( + "svn".to_owned(), + Box::new(SvnDownloader::new(ProcessExecutor::new())), + ); + dm.set_downloader( + "hg".to_owned(), + Box::new(HgDownloader::new(ProcessExecutor::new())), + ); + + dm +} + +pub fn create_archive_manager( + download_manager: std::sync::Arc<tokio::sync::Mutex<DownloadManager>>, +) -> ArchiveManager { + ArchiveManager::new(download_manager) +} + /// Read `vendor/composer/installed.json` into the minimal shape the /// installation manager needs. Mirrors the relevant slice of /// `Composer\Repository\FilesystemRepository::initialize`: accept both @@ -215,8 +254,10 @@ fn read_package_reference(value: Option<&serde_json::Value>) -> Option<PackageRe #[cfg(test)] mod tests { use super::*; + use mozart_core::console::Console; use std::fs; use std::path::Path; + use std::sync::{Arc, Mutex}; use tempfile::tempdir; fn write(path: &Path, content: &str) { @@ -224,6 +265,12 @@ mod tests { fs::write(path, content).unwrap(); } + fn io() -> Arc<Mutex<Box<dyn IoInterface>>> { + Arc::new(Mutex::new( + Box::new(Console::new(0, true, false, true, true)) as Box<dyn IoInterface>, + )) + } + #[test] fn install_path_is_vendor_dir_plus_pretty_name() { let dir = tempdir().unwrap(); @@ -233,7 +280,7 @@ mod tests { r#"{"packages": [{"name": "Vendor/Pkg", "version": "1.0.0"}]}"#, ); - let composer = Composer::require(dir.path()).unwrap(); + let composer = Composer::require(io(), dir.path()).unwrap(); let pkg = composer .repository_manager() .local_repository() @@ -261,7 +308,7 @@ mod tests { r#"{"packages": [{"name": "vendor/pkg", "target-dir": "src/lib"}]}"#, ); - let composer = Composer::require(dir.path()).unwrap(); + let composer = Composer::require(io(), dir.path()).unwrap(); let pkg = composer .repository_manager() .local_repository() @@ -282,7 +329,7 @@ mod tests { let dir = tempdir().unwrap(); write(&dir.path().join("composer.json"), r#"{"name": "acme/app"}"#); - let composer = Composer::require(dir.path()).unwrap(); + let composer = Composer::require(io(), dir.path()).unwrap(); let count = composer .repository_manager() .local_repository() @@ -303,7 +350,7 @@ mod tests { r#"[{"name": "a/a"}, {"name": "b/b"}]"#, ); - let composer = Composer::require(dir.path()).unwrap(); + let composer = Composer::require(io(), dir.path()).unwrap(); let names: Vec<&str> = composer .repository_manager() .local_repository() @@ -322,7 +369,7 @@ mod tests { ); use mozart_core::package::Package; - let composer = Composer::require(dir.path()).unwrap(); + let composer = Composer::require(io(), dir.path()).unwrap(); assert_eq!(composer.package().name(), "acme/app"); assert_eq!( composer @@ -346,7 +393,7 @@ mod tests { r#"{"packages": [{"name": "vendor/pkg"}]}"#, ); - let composer = Composer::require(dir.path()).unwrap(); + let composer = Composer::require(io(), dir.path()).unwrap(); let pkg = composer .repository_manager() .local_repository() |
