Coverage for src/secchi/api/homebrew.py: 96%
28 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-08-04 22:15 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-08-04 22:15 +0000
1"""Homebrew formula adapter using the public formulae API."""
3from __future__ import annotations
5import httpx
7from secchi.api.sparse import SparseAdapter
8from secchi.models import PackageInfo, Registry, SearchResult, Version
10FORMULA_API = "https://formulae.brew.sh/api/formula"
13class HomebrewAdapter(SparseAdapter):
14 @property
15 def registry(self) -> Registry:
16 return Registry.HOMEBREW
18 async def fetch_package(self, name: str) -> PackageInfo:
19 async with self._client_scope() as client:
20 response = await client.get(f"{FORMULA_API}/{name}.json")
21 response.raise_for_status()
22 data = response.json()
23 stable = data.get("versions", {}).get("stable", "")
24 homepage = data.get("homepage", "")
25 formula_url = f"https://formulae.brew.sh/formula/{name}"
26 return PackageInfo(
27 name=data.get("name", name),
28 registry=Registry.HOMEBREW,
29 description=data.get("desc", ""),
30 homepage=homepage,
31 repository_url=homepage,
32 latest_version=stable,
33 license=data.get("license", "") or "",
34 versions=[Version(version=stable)] if stable else [],
35 package_kind="Library",
36 documentation_url=formula_url,
37 )
39 async def search(self, query: str, limit: int = 10) -> list[SearchResult]:
40 async with self._client_scope() as client:
41 try:
42 response = await client.get(f"{FORMULA_API}/{query}.json")
43 response.raise_for_status()
44 except httpx.HTTPError:
45 return []
46 data = response.json()
47 name = data.get("name", query)
48 return [
49 SearchResult(
50 name=name,
51 registry=Registry.HOMEBREW,
52 version=data.get("versions", {}).get("stable", ""),
53 description=data.get("desc", ""),
54 url=f"https://formulae.brew.sh/formula/{name}",
55 score=1.0,
56 exact=name.casefold() == query.casefold(),
57 )
58 ]