repos / dbin

📦 Poor man's package manager.
git clone https://github.com/xplshn/dbin.git

xplshn  ·  2025-08-13

search.go

Go
  1package main
  2
  3import (
  4	"context"
  5	"path/filepath"
  6	"strings"
  7
  8	"github.com/urfave/cli/v3"
  9	"github.com/zeebo/errs"
 10)
 11
 12var (
 13	errSearchFailed = errs.Class("search failed")
 14)
 15
 16func searchCommand() *cli.Command {
 17	return &cli.Command{
 18		Name:  "search",
 19		Usage: "Search for a binary by supplying one or more search terms",
 20		Flags: []cli.Flag{
 21			&cli.UintFlag{
 22				Name:    "limit",
 23				Aliases: []string{"l"},
 24				Usage:   "Set the limit of entries to be shown at once on the screen",
 25			},
 26			&cli.StringFlag{
 27				Name:    "repo",
 28				Aliases: []string{"repos", "r"},
 29				Usage:   "Filter binaries by repository name, comma-separated",
 30			},
 31		},
 32		Action: func(_ context.Context, c *cli.Command) error {
 33			config, err := loadConfig()
 34			if err != nil {
 35				return errSearchFailed.Wrap(err)
 36			}
 37
 38			if uint(c.Uint("limit")) > 0 {
 39				config.Limit = uint(c.Uint("limit"))
 40			}
 41
 42			uRepoIndex, err := fetchRepoIndex(config)
 43			if err != nil {
 44				return errSearchFailed.Wrap(err)
 45			}
 46
 47			// Apply repository filter if specified
 48			if repoNames := c.String("repo"); repoNames != "" {
 49				repoSet := make(map[string]struct{})
 50				for _, repo := range strings.Split(repoNames, ",") {
 51					repoSet[strings.TrimSpace(repo)] = struct{}{}
 52				}
 53				filterBEntries(&uRepoIndex, func(entry binaryEntry) bool {
 54					_, ok := repoSet[entry.Repository.Name]
 55					return ok
 56				})
 57			}
 58
 59			return fSearch(config, c.Args().Slice(), uRepoIndex)
 60		},
 61	}
 62}
 63
 64func fSearch(config *config, searchTerms []string, uRepoIndex []binaryEntry) error {
 65	var results []binaryEntry
 66	for _, bin := range uRepoIndex {
 67		name, pkgID, version, description, rank, repo := bin.Name, bin.PkgID, bin.Version, bin.Description, bin.Rank, bin.Repository
 68		if name == "" || description == "" {
 69			continue
 70		}
 71		match := true
 72		for _, term := range searchTerms {
 73			if !strings.Contains(strings.ToLower(name), strings.ToLower(term)) &&
 74				!strings.Contains(strings.ToLower(description), strings.ToLower(term)) &&
 75				!strings.Contains(strings.ToLower(pkgID), strings.ToLower(term)) {
 76				match = false
 77				break
 78			}
 79		}
 80		if match {
 81			results = append(results, binaryEntry{
 82				Name:        name,
 83				PkgID:       pkgID,
 84				Version:     version,
 85				Description: description,
 86				Rank:        rank,
 87				Repository:  repo,
 88			})
 89		}
 90	}
 91	if len(results) == 0 {
 92		return errSearchFailed.New("no matching binaries found for '%s'", strings.Join(searchTerms, " "))
 93	} else if uint(len(results)) > config.Limit {
 94		return errSearchFailed.New("too many matching binaries (+%d. [Use --limit or -l before your query]) found for '%s'", len(results), strings.Join(searchTerms, " "))
 95	}
 96	disableTruncation := config.DisableTruncation
 97	for _, result := range results {
 98		prefix := "[-]"
 99		if bEntryOfinstalledBinary(filepath.Join(config.InstallDir, filepath.Base(result.Name))).PkgID == result.PkgID {
100			prefix = "[i]"
101		} else if _, err := isCached(config, result); err == nil {
102			prefix = "[c]"
103		}
104		truncatePrintf(disableTruncation, "%s %s - %s\n",
105			prefix, parseBinaryEntry(result, true), result.Description)
106	}
107	return nil
108}