print.go 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. package main
  2. import (
  3. "context"
  4. "fmt"
  5. "io"
  6. "os"
  7. "strconv"
  8. "strings"
  9. "syscall"
  10. "unicode"
  11. aur "github.com/Jguer/aur"
  12. mapset "github.com/deckarep/golang-set/v2"
  13. "github.com/leonelquinteros/gotext"
  14. "golang.org/x/sys/unix"
  15. "github.com/Jguer/yay/v12/pkg/db"
  16. "github.com/Jguer/yay/v12/pkg/dep"
  17. "github.com/Jguer/yay/v12/pkg/query"
  18. "github.com/Jguer/yay/v12/pkg/runtime"
  19. "github.com/Jguer/yay/v12/pkg/settings"
  20. "github.com/Jguer/yay/v12/pkg/settings/parser"
  21. "github.com/Jguer/yay/v12/pkg/text"
  22. "github.com/Jguer/yay/v12/pkg/upgrade"
  23. )
  24. // printInfo prints package info like pacman -Si.
  25. func printInfo(logger *text.Logger, config *settings.Configuration, a *aur.Pkg, extendedInfo bool) {
  26. printInfoValue(logger, gotext.Get("Repository"), "aur")
  27. printInfoValue(logger, gotext.Get("Name"), a.Name)
  28. printInfoValue(logger, gotext.Get("Version"), a.Version)
  29. printInfoValue(logger, gotext.Get("Description"), a.Description)
  30. printInfoValue(logger, gotext.Get("URL"), a.URL)
  31. printInfoValue(logger, gotext.Get("Licenses"), a.License...)
  32. printInfoValue(logger, gotext.Get("Groups"), a.Groups...)
  33. printInfoValue(logger, gotext.Get("Provides"), a.Provides...)
  34. printInfoValue(logger, gotext.Get("Depends On"), a.Depends...)
  35. printInfoValue(logger, gotext.Get("Optional Deps"), a.OptDepends...)
  36. printInfoValue(logger, gotext.Get("Make Deps"), a.MakeDepends...)
  37. printInfoValue(logger, gotext.Get("Check Deps"), a.CheckDepends...)
  38. printInfoValue(logger, gotext.Get("Conflicts With"), a.Conflicts...)
  39. printInfoValue(logger, gotext.Get("Replaces"), a.Replaces...)
  40. printInfoValue(logger, gotext.Get("AUR URL"), config.AURURL+"/packages/"+a.Name)
  41. printInfoValue(logger, gotext.Get("First Submitted"), text.FormatTimeQuery(a.FirstSubmitted))
  42. printInfoValue(logger, gotext.Get("Keywords"), a.Keywords...)
  43. printInfoValue(logger, gotext.Get("Last Modified"), text.FormatTimeQuery(a.LastModified))
  44. printInfoValue(logger, gotext.Get("Maintainer"), a.Maintainer)
  45. printInfoValue(logger, gotext.Get("Popularity"), fmt.Sprintf("%f", a.Popularity))
  46. printInfoValue(logger, gotext.Get("Votes"), fmt.Sprintf("%d", a.NumVotes))
  47. if a.OutOfDate != 0 {
  48. printInfoValue(logger, gotext.Get("Out-of-date"), text.FormatTimeQuery(a.OutOfDate))
  49. } else {
  50. printInfoValue(logger, gotext.Get("Out-of-date"), "No")
  51. }
  52. if extendedInfo {
  53. printInfoValue(logger, "ID", fmt.Sprintf("%d", a.ID))
  54. printInfoValue(logger, gotext.Get("Package Base ID"), fmt.Sprintf("%d", a.PackageBaseID))
  55. printInfoValue(logger, gotext.Get("Package Base"), a.PackageBase)
  56. printInfoValue(logger, gotext.Get("Snapshot URL"), config.AURURL+a.URLPath)
  57. }
  58. logger.Println()
  59. }
  60. // BiggestPackages prints the name of the ten biggest packages in the system.
  61. func biggestPackages(logger *text.Logger, dbExecutor db.Executor) {
  62. pkgS := dbExecutor.BiggestPackages()
  63. if len(pkgS) < 10 {
  64. return
  65. }
  66. for i := 0; i < 10; i++ {
  67. logger.Printf("%s: %s\n", text.Bold(pkgS[i].Name()), text.Cyan(text.Human(pkgS[i].ISize())))
  68. }
  69. }
  70. // localStatistics prints installed packages statistics.
  71. func localStatistics(ctx context.Context, run *runtime.Runtime, dbExecutor db.Executor) error {
  72. info := statistics(run, dbExecutor)
  73. remoteNames := dbExecutor.InstalledRemotePackageNames()
  74. remote := dbExecutor.InstalledRemotePackages()
  75. run.Logger.Infoln(gotext.Get("Yay version v%s", yayVersion))
  76. run.Logger.Println(text.Bold(text.Cyan("===========================================")))
  77. run.Logger.Infoln(gotext.Get("Total installed packages: %s", text.Cyan(strconv.Itoa(info.Totaln))))
  78. run.Logger.Infoln(gotext.Get("Foreign installed packages: %s", text.Cyan(strconv.Itoa(len(remoteNames)))))
  79. run.Logger.Infoln(gotext.Get("Explicitly installed packages: %s", text.Cyan(strconv.Itoa(info.Expln))))
  80. run.Logger.Infoln(gotext.Get("Total Size occupied by packages: %s", text.Cyan(text.Human(info.TotalSize))))
  81. for path, size := range info.pacmanCaches {
  82. run.Logger.Infoln(gotext.Get("Size of pacman cache %s: %s", path, text.Cyan(text.Human(size))))
  83. }
  84. run.Logger.Infoln(gotext.Get("Size of yay cache %s: %s", run.Cfg.BuildDir, text.Cyan(text.Human(info.yayCache))))
  85. run.Logger.Println(text.Bold(text.Cyan("===========================================")))
  86. run.Logger.Infoln(gotext.Get("Ten biggest packages:"))
  87. biggestPackages(run.Logger, dbExecutor)
  88. run.Logger.Println(text.Bold(text.Cyan("===========================================")))
  89. aurData, err := run.AURClient.Get(ctx, &aur.Query{
  90. Needles: remoteNames,
  91. By: aur.Name,
  92. })
  93. if err != nil {
  94. return err
  95. }
  96. warnings := query.NewWarnings(run.Logger.Child("warnings"))
  97. for i := range aurData {
  98. warnings.AddToWarnings(remote, &aurData[i])
  99. }
  100. warnings.Print()
  101. return nil
  102. }
  103. func printUpdateList(ctx context.Context, run *runtime.Runtime, cmdArgs *parser.Arguments,
  104. dbExecutor db.Executor, enableDowngrade bool, filter upgrade.Filter,
  105. ) error {
  106. quietMode := cmdArgs.ExistsArg("q", "quiet")
  107. // TODO: handle quiet mode in a better way
  108. logger := text.NewLogger(io.Discard, os.Stderr, os.Stdin, run.Cfg.Debug, "update-list")
  109. dbExecutor.SetLogger(logger.Child("db"))
  110. oldNoConfirm := settings.NoConfirm
  111. settings.NoConfirm = true
  112. // restoring global NoConfirm to make tests work properly
  113. defer func() { settings.NoConfirm = oldNoConfirm }()
  114. targets := mapset.NewThreadUnsafeSet(cmdArgs.Targets...)
  115. grapher := dep.NewGrapher(dbExecutor, run.AURClient, false, true,
  116. false, false, cmdArgs.ExistsArg("needed"), logger.Child("grapher"))
  117. upService := upgrade.NewUpgradeService(
  118. grapher, run.AURClient, dbExecutor, run.VCSStore,
  119. run.Cfg, true, logger.Child("upgrade"))
  120. graph, errSysUp := upService.GraphUpgrades(ctx, nil,
  121. enableDowngrade, filter)
  122. if errSysUp != nil {
  123. return errSysUp
  124. }
  125. if graph.Len() == 0 {
  126. return fmt.Errorf("")
  127. }
  128. noTargets := targets.Cardinality() == 0
  129. foreignFilter := cmdArgs.ExistsArg("m", "foreign")
  130. nativeFilter := cmdArgs.ExistsArg("n", "native")
  131. noUpdates := true
  132. _ = graph.ForEach(func(pkgName string, ii *dep.InstallInfo) error {
  133. if !ii.Upgrade {
  134. return nil
  135. }
  136. if noTargets || targets.Contains(pkgName) {
  137. if ii.Source == dep.Sync && foreignFilter {
  138. return nil
  139. } else if ii.Source == dep.AUR && nativeFilter {
  140. return nil
  141. }
  142. if quietMode {
  143. run.Logger.Printf("%s\n", pkgName)
  144. } else {
  145. run.Logger.Printf("%s %s -> %s\n", text.Bold(pkgName), text.Bold(text.Green(ii.LocalVersion)),
  146. text.Bold(text.Green(ii.Version)))
  147. }
  148. targets.Remove(pkgName)
  149. noUpdates = false
  150. }
  151. return nil
  152. })
  153. missing := false
  154. targets.Each(func(pkgName string) bool {
  155. if dbExecutor.LocalPackage(pkgName) == nil {
  156. run.Logger.Errorln(gotext.Get("package '%s' was not found", pkgName))
  157. missing = true
  158. }
  159. return false
  160. })
  161. if missing || noUpdates {
  162. return fmt.Errorf("")
  163. }
  164. return nil
  165. }
  166. func printInfoValue(logger *text.Logger, key string, values ...string) {
  167. const (
  168. keyLength = 32
  169. delimCount = 2
  170. )
  171. specialWordsCount := 0
  172. for _, runeValue := range key {
  173. // CJK handling: the character 'ー' is Katakana
  174. // but if use unicode.Katakana, it will return false
  175. if unicode.IsOneOf([]*unicode.RangeTable{
  176. unicode.Han,
  177. unicode.Hiragana,
  178. unicode.Katakana,
  179. unicode.Hangul,
  180. }, runeValue) || runeValue == 'ー' {
  181. specialWordsCount++
  182. }
  183. }
  184. keyTextCount := specialWordsCount - keyLength + delimCount
  185. str := fmt.Sprintf(text.Bold("%-*s: "), keyTextCount, key)
  186. if len(values) == 0 || (len(values) == 1 && values[0] == "") {
  187. logger.Printf("%s%s\n", str, gotext.Get("None"))
  188. return
  189. }
  190. maxCols := getColumnCount()
  191. cols := keyLength + len(values[0])
  192. str += values[0]
  193. for _, value := range values[1:] {
  194. if maxCols > keyLength && cols+len(value)+delimCount >= maxCols {
  195. cols = keyLength
  196. str += "\n" + strings.Repeat(" ", keyLength)
  197. } else if cols != keyLength {
  198. str += strings.Repeat(" ", delimCount)
  199. cols += delimCount
  200. }
  201. str += value
  202. cols += len(value)
  203. }
  204. logger.Println(str)
  205. }
  206. var cachedColumnCount = -1
  207. func getColumnCount() int {
  208. if cachedColumnCount > 0 {
  209. return cachedColumnCount
  210. }
  211. if count, err := strconv.Atoi(os.Getenv("COLUMNS")); err == nil {
  212. cachedColumnCount = count
  213. return cachedColumnCount
  214. }
  215. if ws, err := unix.IoctlGetWinsize(syscall.Stdout, unix.TIOCGWINSZ); err == nil {
  216. cachedColumnCount = int(ws.Col)
  217. return cachedColumnCount
  218. }
  219. return 80
  220. }