source.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  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. bottomUp bool
  27. sortBy string
  28. targetMode parser.TargetMode
  29. searchBy string
  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.Client, 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, s.bottomUp, s.sortBy)
  54. }
  55. if s.targetMode.AtLeastRepo() {
  56. s.repoQuery = queryRepo(pkgS, dbExecutor, s.bottomUp)
  57. }
  58. if aurErr != nil && len(s.repoQuery) != 0 {
  59. text.Errorln(ErrAURSearch{inner: aurErr})
  60. text.Warnln(gotext.Get("Showing repo packages only"))
  61. }
  62. }
  63. func (s *SourceQueryBuilder) Results(w io.Writer, dbExecutor db.Executor, verboseSearch SearchVerbosity) error {
  64. if s.aurQuery == nil || s.repoQuery == nil {
  65. return ErrNoQuery{}
  66. }
  67. if s.bottomUp {
  68. if s.targetMode.AtLeastAUR() {
  69. s.aurQuery.printSearch(w, len(s.repoQuery)+1, dbExecutor, verboseSearch, s.bottomUp, s.singleLineResults)
  70. }
  71. if s.targetMode.AtLeastRepo() {
  72. s.repoQuery.printSearch(w, dbExecutor, verboseSearch, s.bottomUp, s.singleLineResults)
  73. }
  74. } else {
  75. if s.targetMode.AtLeastRepo() {
  76. s.repoQuery.printSearch(w, dbExecutor, verboseSearch, s.bottomUp, s.singleLineResults)
  77. }
  78. if s.targetMode.AtLeastAUR() {
  79. s.aurQuery.printSearch(w, len(s.repoQuery)+1, dbExecutor, verboseSearch, s.bottomUp, s.singleLineResults)
  80. }
  81. }
  82. return nil
  83. }
  84. func (s *SourceQueryBuilder) Len() int {
  85. return len(s.repoQuery) + len(s.aurQuery)
  86. }
  87. func (s *SourceQueryBuilder) GetTargets(include, exclude intrange.IntRanges,
  88. otherExclude stringset.StringSet) ([]string, error) {
  89. isInclude := len(exclude) == 0 && len(otherExclude) == 0
  90. var targets []string
  91. for i, pkg := range s.repoQuery {
  92. var target int
  93. if s.bottomUp {
  94. target = len(s.repoQuery) - i
  95. } else {
  96. target = i + 1
  97. }
  98. if (isInclude && include.Get(target)) || (!isInclude && !exclude.Get(target)) {
  99. targets = append(targets, pkg.DB().Name()+"/"+pkg.Name())
  100. }
  101. }
  102. for i := range s.aurQuery {
  103. var target int
  104. if s.bottomUp {
  105. target = len(s.aurQuery) - i + len(s.repoQuery)
  106. } else {
  107. target = i + 1 + len(s.repoQuery)
  108. }
  109. if (isInclude && include.Get(target)) || (!isInclude && !exclude.Get(target)) {
  110. targets = append(targets, "aur/"+s.aurQuery[i].Name)
  111. }
  112. }
  113. return targets, nil
  114. }
  115. // queryRepo handles repo searches. Creates a RepoSearch struct.
  116. func queryRepo(pkgInputN []string, dbExecutor db.Executor, bottomUp bool) repoQuery {
  117. s := repoQuery(dbExecutor.SyncPackages(pkgInputN...))
  118. if bottomUp {
  119. s.Reverse()
  120. }
  121. return s
  122. }
  123. // queryAUR searches AUR and narrows based on subarguments.
  124. func queryAUR(ctx context.Context, aurClient *aur.Client, pkgS []string, searchBy string, bottomUp bool, sortBy string) (aurQuery, error) {
  125. var (
  126. r []aur.Pkg
  127. err error
  128. usedIndex int
  129. )
  130. by := getSearchBy(searchBy)
  131. if len(pkgS) == 0 {
  132. return nil, nil
  133. }
  134. for i, word := range pkgS {
  135. r, err = aurClient.Search(ctx, word, by)
  136. if err == nil {
  137. usedIndex = i
  138. break
  139. }
  140. }
  141. if err != nil {
  142. return nil, err
  143. }
  144. if len(pkgS) == 1 {
  145. sort.Sort(aurSortable{
  146. aurQuery: r,
  147. sortBy: sortBy,
  148. bottomUp: bottomUp,
  149. })
  150. return r, err
  151. }
  152. aq := make(aurQuery, 0, len(r))
  153. for i := range r {
  154. match := true
  155. for j, pkgN := range pkgS {
  156. if usedIndex == j {
  157. continue
  158. }
  159. name := strings.ToLower(r[i].Name)
  160. desc := strings.ToLower(r[i].Description)
  161. targ := strings.ToLower(pkgN)
  162. if !(strings.Contains(name, targ) || strings.Contains(desc, targ)) {
  163. match = false
  164. break
  165. }
  166. }
  167. if match {
  168. aq = append(aq, r[i])
  169. }
  170. }
  171. sort.Sort(aurSortable{
  172. aurQuery: aq,
  173. sortBy: sortBy,
  174. bottomUp: bottomUp,
  175. })
  176. return aq, err
  177. }