aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/mozart-registry/src/browse_repos.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/browse_repos.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/browse_repos.rs')
-rw-r--r--crates/mozart-registry/src/browse_repos.rs293
1 files changed, 0 insertions, 293 deletions
diff --git a/crates/mozart-registry/src/browse_repos.rs b/crates/mozart-registry/src/browse_repos.rs
deleted file mode 100644
index 0f9b169..0000000
--- a/crates/mozart-registry/src/browse_repos.rs
+++ /dev/null
@@ -1,293 +0,0 @@
-//! Composite of repositories consulted by the `browse` command.
-//!
-//! Mirrors `Composer\Command\HomeCommand::initializeRepos()`:
-//! root package + local installed repository + remote(s). Each repo
-//! exposes a uniform [`BrowseRepo::find_packages`] that yields
-//! [`CompletePackageView`]s — the trio of fields
-//! `Composer\Command\HomeCommand::handlePackage` reads off
-//! `CompletePackageInterface` (`getSupport()['source']`,
-//! `getSourceUrl()`, `getHomepage()`).
-
-use crate::cache::Cache;
-use crate::installed::{InstalledPackageEntry, InstalledPackages};
-use crate::lockfile::LockedPackage;
-use crate::packagist::{self, PackagistVersion};
-use mozart_core::package::RawPackageData;
-
-/// Subset of `Composer\Package\CompletePackageInterface` consumed by
-/// `HomeCommand::handlePackage`. Every backing repo flattens its
-/// package shape into this so URL selection lives in one place.
-#[derive(Debug, Clone, Default, PartialEq, Eq)]
-pub struct CompletePackageView {
- /// `$package->getSupport()['source']`.
- pub support_source: Option<String>,
- /// `$package->getSourceUrl()`.
- pub source_url: Option<String>,
- /// `$package->getHomepage()`.
- pub homepage: Option<String>,
-}
-
-impl From<&LockedPackage> for CompletePackageView {
- fn from(pkg: &LockedPackage) -> Self {
- Self {
- support_source: pkg
- .support
- .as_ref()
- .and_then(|s| s.get("source"))
- .and_then(|s| s.as_str())
- .map(str::to_string),
- source_url: pkg.source.as_ref().map(|s| s.url.clone()),
- homepage: pkg.homepage.clone(),
- }
- }
-}
-
-impl From<&InstalledPackageEntry> for CompletePackageView {
- fn from(pkg: &InstalledPackageEntry) -> Self {
- Self {
- support_source: pkg
- .support
- .as_ref()
- .and_then(|s| s.get("source"))
- .and_then(|s| s.as_str())
- .map(str::to_string),
- source_url: pkg
- .source
- .as_ref()
- .and_then(|s| s.get("url"))
- .and_then(|s| s.as_str())
- .map(str::to_string),
- homepage: pkg.homepage.clone(),
- }
- }
-}
-
-impl From<&PackagistVersion> for CompletePackageView {
- fn from(pkg: &PackagistVersion) -> Self {
- Self {
- support_source: pkg
- .support
- .as_ref()
- .and_then(|s| s.get("source"))
- .and_then(|s| s.as_str())
- .map(str::to_string),
- source_url: pkg.source.as_ref().map(|s| s.url.clone()),
- homepage: pkg.homepage.clone(),
- }
- }
-}
-
-/// `RawPackageData` lacks a typed `support` field — the root package's
-/// `support` block lives inside `extra_fields` because the schema is not
-/// yet ported. Read it manually here.
-pub fn view_from_raw(pkg: &RawPackageData) -> CompletePackageView {
- CompletePackageView {
- support_source: pkg
- .extra_fields
- .get("support")
- .and_then(|s| s.get("source"))
- .and_then(|s| s.as_str())
- .map(str::to_string),
- source_url: None,
- homepage: pkg.homepage.clone(),
- }
-}
-
-/// One repository in the composite. Mirrors the three repo kinds
-/// `HomeCommand::initializeRepos()` returns:
-/// `RootPackageRepository` + local installed + remotes.
-pub enum BrowseRepo {
- /// Stand-in for `Composer\Repository\RootPackageRepository` —
- /// a one-package array containing the root composer.json.
- /// Boxed because `RawPackageData` is much larger than the other
- /// variants (clippy::large_enum_variant).
- Root(Box<RawPackageData>),
- /// Stand-in for `RepositoryManager::getLocalRepository()` —
- /// the installed.json view of `vendor/`.
- Installed(InstalledPackages),
- /// Stand-in for the configured remote. For now Mozart only knows
- /// the default Packagist remote (`RepositoryFactory::defaultRepos`).
- Packagist { cache: Cache },
-}
-
-impl BrowseRepo {
- /// Mirrors `RepositoryInterface::findPackages($name)` — case-insensitive
- /// match by package name, returning every match the repo holds.
- pub async fn find_packages(&self, name: &str) -> anyhow::Result<Vec<CompletePackageView>> {
- match self {
- BrowseRepo::Root(pkg) => {
- if pkg.name.eq_ignore_ascii_case(name) {
- Ok(vec![view_from_raw(pkg)])
- } else {
- Ok(Vec::new())
- }
- }
- BrowseRepo::Installed(installed) => Ok(installed
- .packages
- .iter()
- .filter(|p| p.name.eq_ignore_ascii_case(name))
- .map(CompletePackageView::from)
- .collect()),
- BrowseRepo::Packagist { cache } => {
- let versions = packagist::fetch_package_versions(name, cache).await?;
- Ok(versions.iter().map(CompletePackageView::from).collect())
- }
- }
- }
-}
-
-/// Ordered composite consulted by `HomeCommand::execute()`'s outer
-/// `foreach ($repos as $repo)` loop.
-pub struct BrowseRepos {
- repos: Vec<BrowseRepo>,
-}
-
-impl BrowseRepos {
- /// Build the composite. `root` and `installed` are passed in
- /// rather than read here so callers can decide whether to load
- /// them from `Composer` (when composer.json is present) or skip
- /// them entirely (the `defaultReposWithDefaultManager` fallback).
- pub fn new(
- root: Option<RawPackageData>,
- installed: Option<InstalledPackages>,
- packagist_cache: Cache,
- ) -> Self {
- let mut repos: Vec<BrowseRepo> = Vec::with_capacity(3);
- if let Some(root) = root {
- repos.push(BrowseRepo::Root(Box::new(root)));
- }
- if let Some(installed) = installed {
- repos.push(BrowseRepo::Installed(installed));
- }
- repos.push(BrowseRepo::Packagist {
- cache: packagist_cache,
- });
- Self { repos }
- }
-
- pub fn iter(&self) -> std::slice::Iter<'_, BrowseRepo> {
- self.repos.iter()
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use std::collections::BTreeMap;
-
- fn locked(
- name: &str,
- source_url: Option<&str>,
- homepage: Option<&str>,
- support_source: Option<&str>,
- ) -> LockedPackage {
- LockedPackage {
- name: name.to_string(),
- version: "1.0.0".to_string(),
- version_normalized: None,
- source: source_url.map(|url| crate::lockfile::LockedSource {
- source_type: "git".to_string(),
- url: url.to_string(),
- reference: None,
- }),
- dist: None,
- require: BTreeMap::new(),
- require_dev: BTreeMap::new(),
- conflict: BTreeMap::new(),
- provide: BTreeMap::new(),
- replace: BTreeMap::new(),
- suggest: None,
- package_type: None,
- autoload: None,
- autoload_dev: None,
- license: None,
- description: None,
- homepage: homepage.map(str::to_string),
- keywords: None,
- authors: None,
- support: support_source.map(|s| serde_json::json!({"source": s})),
- funding: None,
- time: None,
- extra_fields: BTreeMap::new(),
- }
- }
-
- #[test]
- fn view_from_locked_package_carries_three_urls() {
- let pkg = locked(
- "vendor/pkg",
- Some("https://github.com/vendor/pkg.git"),
- Some("https://vendor.example.com"),
- Some("https://github.com/vendor/pkg"),
- );
- let view = CompletePackageView::from(&pkg);
- assert_eq!(
- view.support_source.as_deref(),
- Some("https://github.com/vendor/pkg")
- );
- assert_eq!(
- view.source_url.as_deref(),
- Some("https://github.com/vendor/pkg.git")
- );
- assert_eq!(view.homepage.as_deref(), Some("https://vendor.example.com"));
- }
-
- #[test]
- fn view_from_installed_entry_extracts_source_url() {
- let mut entry = InstalledPackageEntry {
- name: "vendor/pkg".to_string(),
- version: "1.0.0".to_string(),
- version_normalized: None,
- source: Some(serde_json::json!({"url": "https://github.com/vendor/pkg.git"})),
- dist: None,
- package_type: None,
- install_path: None,
- autoload: None,
- aliases: vec![],
- homepage: Some("https://vendor.example.com".to_string()),
- support: Some(serde_json::json!({"source": "https://github.com/vendor/pkg"})),
- extra_fields: BTreeMap::new(),
- };
- let view = CompletePackageView::from(&entry);
- assert_eq!(
- view.source_url.as_deref(),
- Some("https://github.com/vendor/pkg.git")
- );
- assert_eq!(
- view.support_source.as_deref(),
- Some("https://github.com/vendor/pkg")
- );
- assert_eq!(view.homepage.as_deref(), Some("https://vendor.example.com"));
-
- entry.support = None;
- entry.source = None;
- entry.homepage = None;
- let empty = CompletePackageView::from(&entry);
- assert_eq!(empty, CompletePackageView::default());
- }
-
- #[test]
- fn view_from_raw_reads_support_via_extra_fields() {
- let mut raw = RawPackageData::new("vendor/root".to_string());
- raw.homepage = Some("https://vendor.example.com".to_string());
- raw.extra_fields.insert(
- "support".to_string(),
- serde_json::json!({"source": "https://github.com/vendor/root"}),
- );
- let view = view_from_raw(&raw);
- assert_eq!(
- view.support_source.as_deref(),
- Some("https://github.com/vendor/root")
- );
- assert!(view.source_url.is_none());
- assert_eq!(view.homepage.as_deref(), Some("https://vendor.example.com"));
- }
-
- #[tokio::test]
- async fn root_repo_matches_case_insensitively() {
- let raw = RawPackageData::new("Vendor/Root".to_string());
- let repo = BrowseRepo::Root(Box::new(raw));
- assert_eq!(repo.find_packages("vendor/root").await.unwrap().len(), 1);
- assert_eq!(repo.find_packages("other/pkg").await.unwrap().len(), 0);
- }
-}