aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/mozart-core
diff options
context:
space:
mode:
Diffstat (limited to 'crates/mozart-core')
-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
13 files changed, 170 insertions, 103 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");