source.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  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/metadata"
  13. "github.com/Jguer/yay/v11/pkg/settings/parser"
  14. "github.com/Jguer/yay/v11/pkg/stringset"
  15. "github.com/Jguer/yay/v11/pkg/text"
  16. )
  17. type SearchVerbosity int
  18. // Verbosity settings for search.
  19. const (
  20. NumberMenu SearchVerbosity = iota
  21. Detailed
  22. Minimal
  23. )
  24. type SourceQueryBuilder struct {
  25. repoQuery
  26. aurQuery
  27. useAURCache bool
  28. sortBy string
  29. searchBy string
  30. targetMode parser.TargetMode
  31. bottomUp bool
  32. singleLineResults bool
  33. aurClient aur.ClientInterface
  34. aurCache *metadata.AURCache
  35. }
  36. func NewSourceQueryBuilder(
  37. aurClient aur.ClientInterface,
  38. aurCache *metadata.AURCache,
  39. sortBy string,
  40. targetMode parser.TargetMode,
  41. searchBy string,
  42. bottomUp,
  43. singleLineResults bool,
  44. useAURCache bool,
  45. ) *SourceQueryBuilder {
  46. return &SourceQueryBuilder{
  47. aurClient: aurClient,
  48. aurCache: aurCache,
  49. repoQuery: []alpm.IPackage{},
  50. aurQuery: []aur.Pkg{},
  51. bottomUp: bottomUp,
  52. sortBy: sortBy,
  53. targetMode: targetMode,
  54. searchBy: searchBy,
  55. singleLineResults: singleLineResults,
  56. useAURCache: useAURCache,
  57. }
  58. }
  59. func (s *SourceQueryBuilder) Execute(ctx context.Context,
  60. dbExecutor db.Executor,
  61. pkgS []string,
  62. ) {
  63. var aurErr error
  64. pkgS = RemoveInvalidTargets(pkgS, s.targetMode)
  65. if s.targetMode.AtLeastAUR() {
  66. s.aurQuery, aurErr = queryAUR(ctx, s.aurClient, s.aurCache, pkgS, s.searchBy, s.useAURCache)
  67. s.aurQuery = filterAURResults(pkgS, s.aurQuery)
  68. sort.Sort(aurSortable{aurQuery: s.aurQuery, sortBy: s.sortBy, bottomUp: s.bottomUp})
  69. }
  70. if s.targetMode.AtLeastRepo() {
  71. s.repoQuery = repoQuery(dbExecutor.SyncPackages(pkgS...))
  72. if s.bottomUp {
  73. s.Reverse()
  74. }
  75. }
  76. if aurErr != nil && len(s.repoQuery) != 0 {
  77. text.Errorln(ErrAURSearch{inner: aurErr})
  78. text.Warnln(gotext.Get("Showing repo packages only"))
  79. }
  80. }
  81. func (s *SourceQueryBuilder) Results(w io.Writer, dbExecutor db.Executor, verboseSearch SearchVerbosity) error {
  82. if s.aurQuery == nil || s.repoQuery == nil {
  83. return ErrNoQuery{}
  84. }
  85. if s.bottomUp {
  86. if s.targetMode.AtLeastAUR() {
  87. s.aurQuery.printSearch(w, len(s.repoQuery)+1, dbExecutor, verboseSearch, s.bottomUp, s.singleLineResults)
  88. }
  89. if s.targetMode.AtLeastRepo() {
  90. s.repoQuery.printSearch(w, dbExecutor, verboseSearch, s.bottomUp, s.singleLineResults)
  91. }
  92. } else {
  93. if s.targetMode.AtLeastRepo() {
  94. s.repoQuery.printSearch(w, dbExecutor, verboseSearch, s.bottomUp, s.singleLineResults)
  95. }
  96. if s.targetMode.AtLeastAUR() {
  97. s.aurQuery.printSearch(w, len(s.repoQuery)+1, dbExecutor, verboseSearch, s.bottomUp, s.singleLineResults)
  98. }
  99. }
  100. return nil
  101. }
  102. func (s *SourceQueryBuilder) Len() int {
  103. return len(s.repoQuery) + len(s.aurQuery)
  104. }
  105. func (s *SourceQueryBuilder) GetTargets(include, exclude intrange.IntRanges,
  106. otherExclude stringset.StringSet,
  107. ) ([]string, error) {
  108. isInclude := len(exclude) == 0 && len(otherExclude) == 0
  109. var targets []string
  110. for i, pkg := range s.repoQuery {
  111. var target int
  112. if s.bottomUp {
  113. target = len(s.repoQuery) - i
  114. } else {
  115. target = i + 1
  116. }
  117. if (isInclude && include.Get(target)) || (!isInclude && !exclude.Get(target)) {
  118. targets = append(targets, pkg.DB().Name()+"/"+pkg.Name())
  119. }
  120. }
  121. for i := range s.aurQuery {
  122. var target int
  123. if s.bottomUp {
  124. target = len(s.aurQuery) - i + len(s.repoQuery)
  125. } else {
  126. target = i + 1 + len(s.repoQuery)
  127. }
  128. if (isInclude && include.Get(target)) || (!isInclude && !exclude.Get(target)) {
  129. targets = append(targets, "aur/"+s.aurQuery[i].Name)
  130. }
  131. }
  132. return targets, nil
  133. }
  134. // filter AUR results to remove strings that don't contain all of the search terms.
  135. func filterAURResults(pkgS []string, results []aur.Pkg) []aur.Pkg {
  136. aurPkgs := make([]aur.Pkg, 0, len(results))
  137. matchesSearchTerms := func(pkg *aur.Pkg, terms []string) bool {
  138. for _, pkgN := range terms {
  139. name := strings.ToLower(pkg.Name)
  140. desc := strings.ToLower(pkg.Description)
  141. targ := strings.ToLower(pkgN)
  142. if !(strings.Contains(name, targ) || strings.Contains(desc, targ)) {
  143. return false
  144. }
  145. }
  146. return true
  147. }
  148. for i := range results {
  149. if matchesSearchTerms(&results[i], pkgS) {
  150. aurPkgs = append(aurPkgs, results[i])
  151. }
  152. }
  153. return aurPkgs
  154. }
  155. // queryAUR searches AUR and narrows based on subarguments.
  156. func queryAUR(ctx context.Context,
  157. aurClient aur.ClientInterface, aurMetadata *metadata.AURCache,
  158. pkgS []string, searchBy string, newEngine bool,
  159. ) ([]aur.Pkg, error) {
  160. var (
  161. err error
  162. by = getSearchBy(searchBy)
  163. )
  164. for _, word := range pkgS {
  165. var r []aur.Pkg
  166. if aurMetadata != nil && newEngine {
  167. q, errM := aurMetadata.Get(ctx, &metadata.AURQuery{
  168. Needles: []string{word},
  169. By: by,
  170. Contains: true,
  171. })
  172. for _, pkg := range q {
  173. r = append(r, *pkg)
  174. }
  175. if errM == nil {
  176. return r, nil
  177. }
  178. text.Warnln("AUR Metadata search failed:", err)
  179. }
  180. // if one of the search terms returns a result we start filtering by it
  181. if aurClient != nil {
  182. text.Debugln("AUR RPC:", by, word)
  183. if r, err = aurClient.Search(ctx, word, by); err == nil {
  184. return r, nil
  185. }
  186. }
  187. }
  188. return nil, err
  189. }