source.go 5.3 KB

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