aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/mozart-core/src/downloader/download_manager.rs135
-rw-r--r--crates/mozart-core/src/downloader/downloader_interface.rs6
-rw-r--r--crates/mozart-core/src/downloader/git_downloader.rs19
-rw-r--r--crates/mozart-core/src/downloader/hg_downloader.rs17
-rw-r--r--crates/mozart-core/src/downloader/svn_downloader.rs18
-rw-r--r--crates/mozart-core/src/lib.rs1
-rw-r--r--crates/mozart-core/src/package/archiver.rs4
-rw-r--r--crates/mozart-core/src/package/archiver/archive_manager.rs (renamed from crates/mozart-core/src/package/archiver/manager.rs)41
-rw-r--r--crates/mozart-core/src/repository/downloader.rs2
-rw-r--r--crates/mozart-core/src/repository/installer_executor/filesystem.rs10
-rw-r--r--crates/mozart-core/src/util.rs3
-rw-r--r--crates/mozart-core/src/util/filesystem.rs8
-rw-r--r--crates/mozart-core/tests/git_driver_test.rs9
-rw-r--r--crates/mozart/src/commands/archive.rs39
-rw-r--r--crates/mozart/src/commands/audit.rs2
-rw-r--r--crates/mozart/src/commands/browse.rs2
-rw-r--r--crates/mozart/src/commands/bump.rs2
-rw-r--r--crates/mozart/src/commands/clear_cache.rs2
-rw-r--r--crates/mozart/src/commands/create_project.rs24
-rw-r--r--crates/mozart/src/commands/diagnose.rs2
-rw-r--r--crates/mozart/src/commands/dump_autoload.rs2
-rw-r--r--crates/mozart/src/commands/exec.rs30
-rw-r--r--crates/mozart/src/commands/fund.rs2
-rw-r--r--crates/mozart/src/commands/licenses.rs2
-rw-r--r--crates/mozart/src/commands/reinstall.rs2
-rw-r--r--crates/mozart/src/commands/run_script.rs4
-rw-r--r--crates/mozart/src/commands/status.rs4
-rw-r--r--crates/mozart/src/commands/update.rs2
-rw-r--r--crates/mozart/src/commands/validate.rs4
-rw-r--r--crates/mozart/src/composer.rs39
-rw-r--r--crates/mozart/src/factory.rs119
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()