source.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. package query
  2. import (
  3. "context"
  4. "io"
  5. "sort"
  6. "strings"
  7. "github.com/Jguer/aur"
  8. "github.com/Jguer/go-alpm/v2"
  9. "github.com/leonelquinteros/gotext"
  10. "github.com/Jguer/yay/v11/pkg/db"
  11. "github.com/Jguer/yay/v11/pkg/intrange"
  12. "github.com/Jguer/yay/v11/pkg/settings/parser"
  13. "github.com/Jguer/yay/v11/pkg/stringset"
  14. "github.com/Jguer/yay/v11/pkg/text"
  15. )
  16. type SearchVerbosity int
  17. // Verbosity settings for search.
  18. const (
  19. NumberMenu SearchVerbosity = iota
  20. Detailed
  21. Minimal
  22. )
  23. type SourceQueryBuilder struct {
  24. repoQuery
  25. aurQuery
  26. sortBy string
  27. searchBy string
  28. targetMode parser.TargetMode
  29. bottomUp bool
  30. singleLineResults bool
  31. }
  32. func NewSourceQueryBuilder(
  33. sortBy string,
  34. targetMode parser.TargetMode,
  35. searchBy string,
  36. bottomUp,
  37. singleLineResults bool,
  38. ) *SourceQueryBuilder {
  39. return &SourceQueryBuilder{
  40. repoQuery: []alpm.IPackage{},
  41. aurQuery: []aur.Pkg{},
  42. bottomUp: bottomUp,
  43. sortBy: sortBy,
  44. targetMode: targetMode,
  45. searchBy: searchBy,
  46. singleLineResults: singleLineResults,
  47. }
  48. }
  49. func (s *SourceQueryBuilder) Execute(ctx context.Context, dbExecutor db.Executor, aurClient aur.ClientInterface, pkgS []string) {
  50. var aurErr error
  51. pkgS = RemoveInvalidTargets(pkgS, s.targetMode)
  52. if s.targetMode.AtLeastAUR() {
  53. s.aurQuery, aurErr = queryAUR(ctx, aurClient, pkgS, s.searchBy)
  54. s.aurQuery = filterAURResults(pkgS, s.aurQuery)
  55. sort.Sort(aurSortable{aurQuery: s.aurQuery, sortBy: s.sortBy, bottomUp: s.bottomUp})
  56. }
  57. if s.targetMode.AtLeastRepo() {
  58. s.repoQuery = repoQuery(dbExecutor.SyncPackages(pkgS...))
  59. if s.bottomUp {
  60. s.Reverse()
  61. }
  62. }
  63. if aurErr != nil && len(s.repoQuery) != 0 {
  64. text.Errorln(ErrAURSearch{inner: aurErr})
  65. text.Warnln(gotext.Get("Showing repo packages only"))
  66. }
  67. }
  68. func (s *SourceQueryBuilder) Results(w io.Writer, dbExecutor db.Executor, verboseSearch SearchVerbosity) error {
  69. if s.aurQuery == nil || s.repoQuery == nil {
  70. return ErrNoQuery{}
  71. }
  72. if s.bottomUp {
  73. if s.targetMode.AtLeastAUR() {
  74. s.aurQuery.printSearch(w, len(s.repoQuery)+1, dbExecutor, verboseSearch, s.bottomUp, s.singleLineResults)
  75. }
  76. if s.targetMode.AtLeastRepo() {
  77. s.repoQuery.printSearch(w, dbExecutor, verboseSearch, s.bottomUp, s.singleLineResults)
  78. }
  79. } else {
  80. if s.targetMode.AtLeastRepo() {
  81. s.repoQuery.printSearch(w, dbExecutor, verboseSearch, s.bottomUp, s.singleLineResults)
  82. }
  83. if s.targetMode.AtLeastAUR() {
  84. s.aurQuery.printSearch(w, len(s.repoQuery)+1, dbExecutor, verboseSearch, s.bottomUp, s.singleLineResults)
  85. }
  86. }
  87. return nil
  88. }
  89. func (s *SourceQueryBuilder) Len() int {
  90. return len(s.repoQuery) + len(s.aurQuery)
  91. }
  92. func (s *SourceQueryBuilder) GetTargets(include, exclude intrange.IntRanges,
  93. otherExclude stringset.StringSet,
  94. ) ([]string, error) {
  95. isInclude := len(exclude) == 0 && len(otherExclude) == 0
  96. var targets []string
  97. for i, pkg := range s.repoQuery {
  98. var target int
  99. if s.bottomUp {
  100. target = len(s.repoQuery) - i
  101. } else {
  102. target = i + 1
  103. }
  104. if (isInclude && include.Get(target)) || (!isInclude && !exclude.Get(target)) {
  105. targets = append(targets, pkg.DB().Name()+"/"+pkg.Name())
  106. }
  107. }
  108. for i := range s.aurQuery {
  109. var target int
  110. if s.bottomUp {
  111. target = len(s.aurQuery) - i + len(s.repoQuery)
  112. } else {
  113. target = i + 1 + len(s.repoQuery)
  114. }
  115. if (isInclude && include.Get(target)) || (!isInclude && !exclude.Get(target)) {
  116. targets = append(targets, "aur/"+s.aurQuery[i].Name)
  117. }
  118. }
  119. return targets, nil
  120. }
  121. // filter AUR results to remove strings that don't contain all of the search terms.
  122. func filterAURResults(pkgS []string, results []aur.Pkg) []aur.Pkg {
  123. aurPkgs := make([]aur.Pkg, 0, len(results))
  124. matchesSearchTerms := func(pkg *aur.Pkg, terms []string) bool {
  125. for _, pkgN := range terms {
  126. name := strings.ToLower(pkg.Name)
  127. desc := strings.ToLower(pkg.Description)
  128. targ := strings.ToLower(pkgN)
  129. if !(strings.Contains(name, targ) || strings.Contains(desc, targ)) {
  130. return false
  131. }
  132. }
  133. return true
  134. }
  135. for i := range results {
  136. if matchesSearchTerms(&results[i], pkgS) {
  137. aurPkgs = append(aurPkgs, results[i])
  138. }
  139. }
  140. return aurPkgs
  141. }
  142. // queryAUR searches AUR and narrows based on subarguments.
  143. func queryAUR(ctx context.Context, aurClient aur.ClientInterface, pkgS []string, searchBy string) ([]aur.Pkg, error) {
  144. var (
  145. err error
  146. by = getSearchBy(searchBy)
  147. )
  148. for _, word := range pkgS {
  149. var r []aur.Pkg
  150. r, err = aurClient.Search(ctx, word, by)
  151. if err == nil {
  152. return r, nil
  153. }
  154. }
  155. return nil, err
  156. }