query.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. package main
  2. import (
  3. "context"
  4. "fmt"
  5. "os"
  6. aur "github.com/Jguer/aur"
  7. alpm "github.com/Jguer/go-alpm/v2"
  8. "github.com/Jguer/yay/v11/pkg/db"
  9. "github.com/Jguer/yay/v11/pkg/query"
  10. "github.com/Jguer/yay/v11/pkg/settings"
  11. "github.com/Jguer/yay/v11/pkg/settings/parser"
  12. "github.com/Jguer/yay/v11/pkg/stringset"
  13. "github.com/Jguer/yay/v11/pkg/text"
  14. )
  15. // SyncSearch presents a query to the local repos and to the AUR.
  16. func syncSearch(ctx context.Context, pkgS []string, aurClient *aur.Client, dbExecutor db.Executor, verbose bool) error {
  17. queryBuilder := query.NewSourceQueryBuilder(config.SortBy, config.Runtime.Mode, config.SearchBy, config.BottomUp, config.SingleLineResults)
  18. queryBuilder.Execute(ctx, dbExecutor, aurClient, pkgS)
  19. searchMode := query.Minimal
  20. if verbose {
  21. searchMode = query.Detailed
  22. }
  23. return queryBuilder.Results(os.Stdout, dbExecutor, searchMode)
  24. }
  25. // SyncInfo serves as a pacman -Si for repo packages and AUR packages.
  26. func syncInfo(ctx context.Context, cmdArgs *parser.Arguments, pkgS []string, dbExecutor db.Executor) error {
  27. var (
  28. info []*aur.Pkg
  29. err error
  30. missing = false
  31. )
  32. pkgS = query.RemoveInvalidTargets(pkgS, config.Runtime.Mode)
  33. aurS, repoS := packageSlices(pkgS, dbExecutor)
  34. if len(aurS) != 0 {
  35. noDB := make([]string, 0, len(aurS))
  36. for _, pkg := range aurS {
  37. _, name := text.SplitDBFromName(pkg)
  38. noDB = append(noDB, name)
  39. }
  40. info, err = query.AURInfoPrint(ctx, config.Runtime.AURClient, noDB, config.RequestSplitN)
  41. if err != nil {
  42. missing = true
  43. fmt.Fprintln(os.Stderr, err)
  44. }
  45. }
  46. // Repo always goes first
  47. if len(repoS) != 0 {
  48. arguments := cmdArgs.Copy()
  49. arguments.ClearTargets()
  50. arguments.AddTarget(repoS...)
  51. err = config.Runtime.CmdBuilder.Show(config.Runtime.CmdBuilder.BuildPacmanCmd(ctx,
  52. cmdArgs, config.Runtime.Mode, settings.NoConfirm))
  53. if err != nil {
  54. return err
  55. }
  56. }
  57. if len(aurS) != len(info) {
  58. missing = true
  59. }
  60. if len(info) != 0 {
  61. for _, pkg := range info {
  62. PrintInfo(pkg, cmdArgs.ExistsDouble("i"))
  63. }
  64. }
  65. if missing {
  66. err = fmt.Errorf("")
  67. }
  68. return err
  69. }
  70. // PackageSlices separates an input slice into aur and repo slices.
  71. func packageSlices(toCheck []string, dbExecutor db.Executor) (aurNames, repoNames []string) {
  72. for _, _pkg := range toCheck {
  73. dbName, name := text.SplitDBFromName(_pkg)
  74. if dbName == "aur" || config.Runtime.Mode == parser.ModeAUR {
  75. aurNames = append(aurNames, _pkg)
  76. continue
  77. } else if dbName != "" || config.Runtime.Mode == parser.ModeRepo {
  78. repoNames = append(repoNames, _pkg)
  79. continue
  80. }
  81. if dbExecutor.SyncSatisfierExists(name) ||
  82. len(dbExecutor.PackagesFromGroup(name)) != 0 {
  83. repoNames = append(repoNames, _pkg)
  84. } else {
  85. aurNames = append(aurNames, _pkg)
  86. }
  87. }
  88. return aurNames, repoNames
  89. }
  90. // HangingPackages returns a list of packages installed as deps
  91. // and unneeded by the system
  92. // removeOptional decides whether optional dependencies are counted or not.
  93. func hangingPackages(removeOptional bool, dbExecutor db.Executor) (hanging []string) {
  94. // safePackages represents every package in the system in one of 3 states
  95. // State = 0 - Remove package from the system
  96. // State = 1 - Keep package in the system; need to iterate over dependencies
  97. // State = 2 - Keep package and have iterated over dependencies
  98. safePackages := make(map[string]uint8)
  99. // provides stores a mapping from the provides name back to the original package name
  100. provides := make(stringset.MapStringSet)
  101. packages := dbExecutor.LocalPackages()
  102. // Mark explicit dependencies and enumerate the provides list
  103. for _, pkg := range packages {
  104. if pkg.Reason() == alpm.PkgReasonExplicit {
  105. safePackages[pkg.Name()] = 1
  106. } else {
  107. safePackages[pkg.Name()] = 0
  108. }
  109. for _, dep := range dbExecutor.PackageProvides(pkg) {
  110. provides.Add(dep.Name, pkg.Name())
  111. }
  112. }
  113. iterateAgain := true
  114. for iterateAgain {
  115. iterateAgain = false
  116. for _, pkg := range packages {
  117. if state := safePackages[pkg.Name()]; state == 0 || state == 2 {
  118. continue
  119. }
  120. safePackages[pkg.Name()] = 2
  121. deps := dbExecutor.PackageDepends(pkg)
  122. if !removeOptional {
  123. deps = append(deps, dbExecutor.PackageOptionalDepends(pkg)...)
  124. }
  125. // Update state for dependencies
  126. for _, dep := range deps {
  127. // Don't assume a dependency is installed
  128. state, ok := safePackages[dep.Name]
  129. if !ok {
  130. // Check if dep is a provides rather than actual package name
  131. if pset, ok2 := provides[dep.Name]; ok2 {
  132. for p := range pset {
  133. if safePackages[p] == 0 {
  134. iterateAgain = true
  135. safePackages[p] = 1
  136. }
  137. }
  138. }
  139. continue
  140. }
  141. if state == 0 {
  142. iterateAgain = true
  143. safePackages[dep.Name] = 1
  144. }
  145. }
  146. }
  147. }
  148. // Build list of packages to be removed
  149. for _, pkg := range packages {
  150. if safePackages[pkg.Name()] == 0 {
  151. hanging = append(hanging, pkg.Name())
  152. }
  153. }
  154. return hanging
  155. }
  156. // Statistics returns statistics about packages installed in system.
  157. func statistics(dbExecutor db.Executor) *struct {
  158. Totaln int
  159. Expln int
  160. TotalSize int64
  161. } {
  162. var (
  163. totalSize int64
  164. localPackages = dbExecutor.LocalPackages()
  165. totalInstalls = 0
  166. explicitInstalls = 0
  167. )
  168. for _, pkg := range localPackages {
  169. totalSize += pkg.ISize()
  170. totalInstalls++
  171. if pkg.Reason() == alpm.PkgReasonExplicit {
  172. explicitInstalls++
  173. }
  174. }
  175. info := &struct {
  176. Totaln int
  177. Expln int
  178. TotalSize int64
  179. }{
  180. totalInstalls, explicitInstalls, totalSize,
  181. }
  182. return info
  183. }