aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/mozart-registry/src/composer_repo.rs
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-05-03 20:03:42 +0900
committernsfisis <nsfisis@gmail.com>2026-05-03 20:03:42 +0900
commitd3cdb9e3f73314e04061d4d18f654e7e80b0dc18 (patch)
tree92dfda3903a7a7f1c631b18eaf544063e9b7f4f6 /crates/mozart-registry/src/composer_repo.rs
parent756e0b9c18af8b2b5887ae1fb265a03187ca9c00 (diff)
downloadphp-mozart-d3cdb9e3f73314e04061d4d18f654e7e80b0dc18.tar.gz
php-mozart-d3cdb9e3f73314e04061d4d18f654e7e80b0dc18.tar.zst
php-mozart-d3cdb9e3f73314e04061d4d18f654e7e80b0dc18.zip
feat(repository): support only/exclude/canonical repo filters
Composer's FilterRepository wraps a repository with three knobs: `only` / `exclude` to drop packages by name, and `canonical: false` to relax the repo's authoritative claim on its package names so lower-priority repos can still answer. Mozart was ignoring all three, so first-listed inline / composer-repo entries always shadowed later repos and `only` / `exclude` lists were silently no-ops. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/mozart-registry/src/composer_repo.rs')
-rw-r--r--crates/mozart-registry/src/composer_repo.rs25
1 files changed, 25 insertions, 0 deletions
diff --git a/crates/mozart-registry/src/composer_repo.rs b/crates/mozart-registry/src/composer_repo.rs
index 28063cc..6594668 100644
--- a/crates/mozart-registry/src/composer_repo.rs
+++ b/crates/mozart-registry/src/composer_repo.rs
@@ -20,6 +20,8 @@
//! 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;
@@ -35,6 +37,7 @@ pub struct ComposerRepoPackage {
/// `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;
@@ -55,18 +58,34 @@ pub fn collect_composer_packages(repositories: &[RawRepository]) -> Vec<Composer
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
@@ -98,6 +117,9 @@ mod tests {
repo_type: "composer".to_string(),
url: Some(url),
package: None,
+ only: None,
+ exclude: None,
+ canonical: None,
}
}
@@ -132,6 +154,9 @@ mod tests {
repo_type: "vcs".to_string(),
url: Some("https://example.com/foo.git".to_string()),
package: None,
+ only: None,
+ exclude: None,
+ canonical: None,
}];
assert!(collect_composer_packages(&repos).is_empty());
}