aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/mozart-registry/src/repository/mod.rs
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-05-08 03:12:05 +0900
committernsfisis <nsfisis@gmail.com>2026-05-08 03:12:05 +0900
commite6f35b567564665c6cb741a06e4c4afcdc5ab317 (patch)
treea1ae0e55afab95598ff0f6bec5b6a2e9f4f86cc0 /crates/mozart-registry/src/repository/mod.rs
parentb3fcdf3b5cf0d6b43109c4bcb3dfcbb6576abce5 (diff)
downloadphp-mozart-e6f35b567564665c6cb741a06e4c4afcdc5ab317.tar.gz
php-mozart-e6f35b567564665c6cb741a06e4c4afcdc5ab317.tar.zst
php-mozart-e6f35b567564665c6cb741a06e4c4afcdc5ab317.zip
fix(search): align with Composer's RepositoryInterface::search
Replace the HTTP-only post-filtered implementation with a Repository::search trait dispatch that mirrors ComposerRepository::search semantics for all three modes (FULLTEXT/NAME/VENDOR). --only-name now does an OR-of-tokens regex match against the full Packagist list.json index instead of a substring match against a fulltext page, so e.g. \`mozart search --only-name mono log\` matches \`monolog/monolog\` like Composer does. Other parity fixes: regex::escape on non-fulltext queries, format check before mutex check, 4-space JSON indent, OSC 8 terminal hyperlink emission when a result has a url, <warning>\! Abandoned \!</warning> styling on abandoned rows, and the Mozart-only "No packages found" warning is dropped to match Composer's silent empty-result behavior. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/mozart-registry/src/repository/mod.rs')
-rw-r--r--crates/mozart-registry/src/repository/mod.rs53
1 files changed, 52 insertions, 1 deletions
diff --git a/crates/mozart-registry/src/repository/mod.rs b/crates/mozart-registry/src/repository/mod.rs
index 21752b9..6642638 100644
--- a/crates/mozart-registry/src/repository/mod.rs
+++ b/crates/mozart-registry/src/repository/mod.rs
@@ -10,12 +10,29 @@
//! the live Packagist HTTP repo, [`inline_package_repo`] for `type: package`
//! entries embedded in `composer.json`, and [`vcs_repo`] for VCS repositories.
-use crate::packagist::PackagistVersion;
+use crate::packagist::{PackagistVersion, SearchResult};
pub mod inline_package_repo;
pub mod packagist_repo;
pub mod vcs_repo;
+/// Search modes for [`Repository::search`].
+///
+/// Mirrors Composer's `RepositoryInterface::SEARCH_FULLTEXT|SEARCH_NAME|SEARCH_VENDOR`
+/// constants (`composer/src/Composer/Repository/RepositoryInterface.php`).
+#[derive(Copy, Clone, Eq, PartialEq, Debug)]
+pub enum SearchMode {
+ /// Full-text search over name, description, and keywords (Packagist's
+ /// `search.json` API).
+ Fulltext,
+ /// Match the regex against package names. Tokens are split on whitespace
+ /// and joined as `(?:t1|t2|...)`; callers must pre-quote regex metachars.
+ Name,
+ /// Match the regex against vendor names. Result rows have only `name`
+ /// populated (the vendor part).
+ Vendor,
+}
+
/// One name-keyed lookup against a repository.
///
/// Matches the `$packageNameMap` argument of Composer's `loadPackages`. The
@@ -65,6 +82,22 @@ pub trait Repository: Send + Sync {
/// Look up every version of every queried name this repo knows about.
async fn load_packages(&self, queries: &[PackageQuery<'_>]) -> anyhow::Result<LoadResult>;
+
+ /// Search this repository.
+ ///
+ /// The default returns an empty result so repositories that don't
+ /// participate in search (e.g. inline / VCS repos that only resolve
+ /// known names) can opt out. Mirrors Composer's
+ /// `RepositoryInterface::search` whose default behavior on
+ /// `ArrayRepository` walks the in-memory list.
+ async fn search(
+ &self,
+ _query: &str,
+ _mode: SearchMode,
+ _package_type: Option<&str>,
+ ) -> anyhow::Result<Vec<SearchResult>> {
+ Ok(Vec::new())
+ }
}
/// Ordered list of repositories. Mirrors `Composer\Repository\RepositoryManager`.
@@ -140,4 +173,22 @@ impl RepositorySet {
Ok(packages)
}
+
+ /// Fan-out search across every repository, concatenating results in
+ /// priority order. Mirrors Composer's
+ /// `CompositeRepository::search` which `array_merge`s per-repo results
+ /// without de-duplication.
+ pub async fn search(
+ &self,
+ query: &str,
+ mode: SearchMode,
+ package_type: Option<&str>,
+ ) -> anyhow::Result<Vec<SearchResult>> {
+ let mut all = Vec::new();
+ for repo in &self.repos {
+ let mut hits = repo.search(query, mode, package_type).await?;
+ all.append(&mut hits);
+ }
+ Ok(all)
+ }
}