aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/mozart-registry/src/composer_repo.rs
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-05-10 00:32:08 +0900
committernsfisis <nsfisis@gmail.com>2026-05-10 00:32:08 +0900
commit8cc1ba8a02c0318b65658f1634de378c780392b9 (patch)
treefdd5cb61e488018891a486b25991b87c84220bb8 /crates/mozart-registry/src/composer_repo.rs
parent72b2e877c01e67ba7edd37e34ac2eadb7a1c62c4 (diff)
downloadphp-mozart-8cc1ba8a02c0318b65658f1634de378c780392b9.tar.gz
php-mozart-8cc1ba8a02c0318b65658f1634de378c780392b9.tar.zst
php-mozart-8cc1ba8a02c0318b65658f1634de378c780392b9.zip
refactor(workspace): consolidate crates into mozart-core
Merged mozart-archiver, mozart-autoload, mozart-registry, mozart-sat-resolver, and mozart-vcs into mozart-core to align the source layout with Composer's structure. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'crates/mozart-registry/src/composer_repo.rs')
-rw-r--r--crates/mozart-registry/src/composer_repo.rs173
1 files changed, 0 insertions, 173 deletions
diff --git a/crates/mozart-registry/src/composer_repo.rs b/crates/mozart-registry/src/composer_repo.rs
deleted file mode 100644
index ef091ef..0000000
--- a/crates/mozart-registry/src/composer_repo.rs
+++ /dev/null
@@ -1,173 +0,0 @@
-//! Support for `type: composer` repositories.
-//!
-//! A Composer repository is a directory (or HTTP endpoint) hosting a
-//! `packages.json` file. The legacy format embeds full package metadata
-//! directly:
-//!
-//! ```json
-//! {
-//! "packages": {
-//! "a/a": {
-//! "dev-foobar": { "name": "a/a", "version": "dev-foobar", ... }
-//! }
-//! }
-//! }
-//! ```
-//!
-//! Mirrors `Composer\Repository\ComposerRepository` for the file:// case
-//! used by the test fixtures. Lazy / v2 / provider-includes / metadata-url
-//! variants are out of scope here — the in-process installer fixtures only
-//! exercise the legacy embedded-packages form.
-
-use crate::packagist::PackagistVersion;
-use crate::repository_filter::RepositoryFilter;
-use indexmap::IndexSet;
-use mozart_core::package::RawRepository;
-use std::path::PathBuf;
-
-/// One package version drawn from a `type: composer` repository.
-pub struct ComposerRepoPackage {
- pub name: String,
- pub version: PackagistVersion,
-}
-
-/// Read every package version from `type: composer` repositories declared in
-/// `composer.json`. Only `file://` URLs are supported here — they're what
-/// the installer fixtures use after the harness rewrites
-/// `file://foobar` → `file:///abs/path/to/fixtures/foobar`.
-pub fn collect_composer_packages(repositories: &[RawRepository]) -> Vec<ComposerRepoPackage> {
- let mut out = Vec::new();
- let mut claimed: IndexSet<String> = IndexSet::new();
- for repo in repositories {
- if repo.repo_type != "composer" {
- continue;
- }
- let Some(url) = repo.url.as_deref() else {
- continue;
- };
- let Some(dir) = file_url_to_path(url) else {
- continue;
- };
- let packages_json = dir.join("packages.json");
- let Ok(content) = std::fs::read_to_string(&packages_json) else {
- continue;
- };
- let Ok(parsed) = serde_json::from_str::<serde_json::Value>(&content) else {
- continue;
- };
- let Some(packages) = parsed.get("packages").and_then(|v| v.as_object()) else {
- continue;
- };
- let filter = RepositoryFilter::from_repo(repo);
- let mut names_this_repo: IndexSet<String> = IndexSet::new();
- for (name, versions) in packages {
- if !filter.is_allowed(name) {
- continue;
- }
- if claimed.contains(name) {
- continue;
- }
- let Some(versions_obj) = versions.as_object() else {
- continue;
- };
- let mut emitted = false;
- for (_, version_value) in versions_obj {
- if let Ok(pv) = serde_json::from_value::<PackagistVersion>(version_value.clone()) {
- out.push(ComposerRepoPackage {
- name: name.clone(),
- version: pv,
- });
- emitted = true;
- }
- }
- if emitted {
- names_this_repo.insert(name.clone());
- }
- }
- if filter.canonical {
- claimed.extend(names_this_repo);
- }
- }
- out
-}
-
-/// Turn a `file://` URL into a filesystem path. Accepts both
-/// `file:///abs/path` (RFC 8089 form) and `file://abs/path` (Composer's
-/// loose form). Returns `None` for non-`file://` URLs.
-fn file_url_to_path(url: &str) -> Option<PathBuf> {
- let rest = url.strip_prefix("file://")?;
- // RFC 8089: file:///abs/path → empty authority, rest starts with `/`.
- // Composer's harness writes `file:///abs/...` after rewriting, so the
- // typical input here is one leading `/`.
- Some(PathBuf::from(rest))
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use std::fs;
- use tempfile::TempDir;
-
- fn write_packages_json(dir: &std::path::Path, body: &str) {
- fs::write(dir.join("packages.json"), body).unwrap();
- }
-
- fn composer_repo(url: String) -> RawRepository {
- RawRepository {
- repo_type: "composer".to_string(),
- url: Some(url),
- package: None,
- only: None,
- exclude: None,
- canonical: None,
- security_advisories: None,
- }
- }
-
- #[test]
- fn reads_legacy_packages_json() {
- let tmp = TempDir::new().unwrap();
- write_packages_json(
- tmp.path(),
- r#"{
- "packages": {
- "a/a": {
- "dev-foobar": {
- "name": "a/a",
- "version": "dev-foobar",
- "version_normalized": "dev-foobar"
- }
- }
- }
- }"#,
- );
- let url = format!("file://{}", tmp.path().display());
- let repos = vec![composer_repo(url)];
- let pkgs = collect_composer_packages(&repos);
- assert_eq!(pkgs.len(), 1);
- assert_eq!(pkgs[0].name, "a/a");
- assert_eq!(pkgs[0].version.version, "dev-foobar");
- }
-
- #[test]
- fn ignores_non_composer_types() {
- let repos = vec![RawRepository {
- repo_type: "vcs".to_string(),
- url: Some("https://example.com/foo.git".to_string()),
- package: None,
- only: None,
- exclude: None,
- canonical: None,
- security_advisories: None,
- }];
- assert!(collect_composer_packages(&repos).is_empty());
- }
-
- #[test]
- fn skips_missing_packages_json() {
- let tmp = TempDir::new().unwrap();
- let url = format!("file://{}", tmp.path().display());
- let repos = vec![composer_repo(url)];
- assert!(collect_composer_packages(&repos).is_empty());
- }
-}