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

1"""Homebrew formula adapter using the public formulae API.""" 

2 

3from __future__ import annotations 

4 

5import httpx 

6 

7from secchi.api.sparse import SparseAdapter 

8from secchi.models import PackageInfo, Registry, SearchResult, Version 

9 

10FORMULA_API = "https://formulae.brew.sh/api/formula" 

11 

12 

13class HomebrewAdapter(SparseAdapter): 

14 @property 

15 def registry(self) -> Registry: 

16 return Registry.HOMEBREW 

17 

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 ) 

38 

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 ]