From 2f2df47639f1ecc917c50cb0f011bdbcfdf926e9 Mon Sep 17 00:00:00 2001 From: Jguer Date: Fri, 2 Dec 2016 12:19:03 +0000 Subject: [PATCH] Added SortOrder --- actions.go | 107 +++++++++++++++++++++++-------- aur/aur.go | 66 +++++++++++++------ aur/aur_test.go | 4 +- cmd/yay/yay.go | 13 ++-- pacman/pacman.go | 164 +++++++++++++++++++++-------------------------- 5 files changed, 210 insertions(+), 144 deletions(-) diff --git a/actions.go b/actions.go index 1d9d6836..7f3cb9aa 100644 --- a/actions.go +++ b/actions.go @@ -19,23 +19,43 @@ const BuildDir string = "/tmp/yaytmp/" // SearchMode is search without numbers. const SearchMode int = -1 +// SortMode NumberMenu and Search +var SortMode = DownTop + +// Determines NumberMenu and Search Order +const ( + DownTop = iota + TopDown +) + +// Config copies settings over to AUR and Pacman packages +func Config() { + aur.SortMode = SortMode +} + // NumberMenu presents a CLI for selecting packages to install. func NumberMenu(pkgName string, flags []string) (err error) { var num int var numberString string var args []string - a, n, err := aur.Search(pkgName, true) - r, err := pac.SearchPackages(pkgName) + a, nA, err := aur.Search(pkgName, true) + r, nR, err := pac.Search(pkgName) if err != nil { return } - if len(r.Results) == 0 && n == 0 { - return fmt.Errorf("no Packages match search") + if nR == 0 && nA == 0 { + return fmt.Errorf("no packages match search") + } + + if aur.SortMode == aur.DownTop { + a.PrintSearch(nR) + r.PrintSearch(0) + } else { + r.PrintSearch(0) + a.PrintSearch(nR) } - r.PrintSearch(0) - a.PrintSearch(len(r.Results)) args = append(args, "pacman", "-S") @@ -52,15 +72,24 @@ func NumberMenu(pkgName string, flags []string) (err error) { for _, numS := range result { num, err = strconv.Atoi(numS) if err != nil { - fmt.Println(err) continue } // Install package - if num > len(r.Results)-1 { - aurInstall = append(aurInstall, a[num-len(r.Results)]) + if num > nA+nR-1 || num < 0 { + continue + } else if num > nR-1 { + if aur.SortMode == aur.DownTop { + aurInstall = append(aurInstall, a[nA+nR-num-1]) + } else { + aurInstall = append(aurInstall, a[num-nR]) + } } else { - args = append(args, r.Results[num].Name) + if aur.SortMode == aur.DownTop { + args = append(args, r[nR-num-1].Name) + } else { + args = append(args, r[num].Name) + } } } @@ -87,23 +116,9 @@ func NumberMenu(pkgName string, flags []string) (err error) { // Install handles package installs func Install(pkgs []string, flags []string) error { - var args []string - args = append(args, "pacman") - args = append(args, "-S") - - args = append(args, flags...) - aurs, repos, _ := pac.PackageSlices(pkgs) - args = append(args, repos...) - if len(repos) != 0 { - var cmd *exec.Cmd - cmd = exec.Command("sudo", args...) - cmd.Stdout = os.Stdout - cmd.Stdin = os.Stdin - cmd.Stderr = os.Stderr - cmd.Run() - } + pac.Install(repos, flags) q, n, err := aur.MultiInfo(aurs) if len(aurs) != n { @@ -138,9 +153,18 @@ func Search(pkg string) (err error) { if err != nil { return err } + r, _, err := pac.Search(pkg) + if err != nil { + return err + } - pac.SearchRepos(pkg, SearchMode) - a.PrintSearch(SearchMode) + if aur.SortMode == aur.DownTop { + a.PrintSearch(SearchMode) + r.PrintSearch(SearchMode) + } else { + r.PrintSearch(SearchMode) + a.PrintSearch(SearchMode) + } return nil } @@ -195,3 +219,32 @@ func size(s int64) string { return fmt.Sprintf(format+"%s", size, symbols[int(i)]) } + +// PassToPacman outsorces execution to pacman binary without modifications. +func PassToPacman(op string, pkgs []string, flags []string) error { + var cmd *exec.Cmd + var args []string + + args = append(args, op) + if len(pkgs) != 0 { + args = append(args, pkgs...) + } + + if len(flags) != 0 { + args = append(args, flags...) + } + + if strings.Contains(op, "-Q") { + cmd = exec.Command("pacman", args...) + } else { + args = append([]string{"pacman"}, args...) + cmd = exec.Command("sudo", args...) + } + + cmd.Stdout = os.Stdout + cmd.Stdin = os.Stdin + cmd.Stderr = os.Stderr + err := cmd.Run() + return err + +} diff --git a/aur/aur.go b/aur/aur.go index f4e820e4..98adbfbb 100644 --- a/aur/aur.go +++ b/aur/aur.go @@ -23,6 +23,15 @@ const MakepkgBin string = "/usr/bin/makepkg" // SearchMode is search without numbers. const SearchMode int = -1 +// SortMode determines top down package or down top package display +var SortMode = DownTop + +// Describes Sorting method for numberdisplay +const ( + DownTop = iota + TopDown +) + // Result describes an AUR package. type Result struct { ID int `json:"ID"` @@ -57,7 +66,10 @@ func (q Query) Len() int { } func (q Query) Less(i, j int) bool { - return q[i].NumVotes < q[j].NumVotes + if SortMode == DownTop { + return q[i].NumVotes < q[j].NumVotes + } + return q[i].NumVotes > q[j].NumVotes } func (q Query) Swap(i, j int) { @@ -67,20 +79,20 @@ func (q Query) Swap(i, j int) { // PrintSearch handles printing search results in a given format func (q Query) PrintSearch(start int) { for i, res := range q { - switch { - case start != SearchMode && res.Installed: - fmt.Printf("%d \x1b[1m%s/\x1b[33m%s \x1b[36m%s \x1b[0m(%d) \x1b[32;40mInstalled\x1b[0m\n%s\n", - start+i, "aur", res.Name, res.Version, res.NumVotes, res.Description) - case start != SearchMode && !res.Installed: - fmt.Printf("%d \x1b[1m%s/\x1b[33m%s \x1b[36m%s \x1b[0m(%d)\n%s\n", - start+i, "aur", res.Name, res.Version, res.NumVotes, res.Description) - case start == SearchMode && res.Installed: - fmt.Printf("\x1b[1m%s/\x1b[33m%s \x1b[36m%s \x1b[32;40mInstalled\x1b[0m\n%s\n", - "aur", res.Name, res.Version, res.Description) - case start == SearchMode && !res.Installed: - fmt.Printf("\x1b[1m%s/\x1b[33m%s \x1b[36m%s\x1b[0m\n%s\n", - "aur", res.Name, res.Version, res.Description) + var toprint string + if start != SearchMode { + if SortMode == DownTop { + toprint += fmt.Sprintf("%d ", len(q)+start-i-1) + } else { + toprint += fmt.Sprintf("%d ", start+i) + } } + toprint += fmt.Sprintf("\x1b[1m%s/\x1b[33m%s \x1b[36m%s \x1b[0m(%d) ", "aur", res.Name, res.Version, res.NumVotes) + if res.Installed == true { + toprint += fmt.Sprintf("\x1b[32;40mInstalled\x1b[0m") + } + toprint += "\n" + res.Description + fmt.Println(toprint) } } @@ -96,13 +108,30 @@ func Search(pkg string, sortS bool) (Query, int, error) { if sortS { sort.Sort(r.Results) } + setter := pacman.PFactory(pFSetTrue) - // for _, res := range r.Results { - // res.Installed, err = IspkgInstalled(res.Name) - // } + for i, res := range r.Results { + if i == len(r.Results)-1 { + setter(res.Name, &r.Results[i], true) + continue + } + setter(res.Name, &r.Results[i], false) + } return r.Results, r.ResultCount, err } +// This is very dirty but it works so good. +func pFSetTrue(res interface{}) { + f, ok := res.(*Result) + if !ok { + fmt.Println("Unable to convert back to Result") + return + } + f.Installed = true + + return +} + // Info returns an AUR search with package details func Info(pkg string) (Query, int, error) { type returned struct { @@ -180,7 +209,6 @@ func Upgrade(baseDir string, flags []string) error { outdated = append(outdated, res) } } - } //If there are no outdated packages, don't prompt @@ -265,7 +293,7 @@ func (a *Result) Install(baseDir string, flags []string) (err error) { // Repo dependencies if len(repoDeps) != 0 { - pacman.PassToPacman("-S", repoDeps, []string{"--asdeps", "--needed"}) + pacman.Install(repoDeps, []string{"--asdeps", "--needed"}) } err = os.Chdir(dir.String()) diff --git a/aur/aur_test.go b/aur/aur_test.go index 91d4bb35..2fb8c7aa 100644 --- a/aur/aur_test.go +++ b/aur/aur_test.go @@ -7,7 +7,7 @@ import ( func TestSearch(t *testing.T) { eN := "yay" - eD := "Yet another pacman wrapper with AUR support" + eD := "Yet another yogurt. Pacman wrapper with AUR support written in go." result, _, err := Search("yay", true) if err != nil { t.Fatalf("Expected err to be nil but it was %s", err) @@ -39,7 +39,7 @@ func BenchmarkSearchComplexSorted(b *testing.B) { benchmarkSearch("linux", true, func TestInfo(t *testing.T) { eN := "yay" - eD := "Yet another pacman wrapper with AUR support" + eD := "Yet another yogurt. Pacman wrapper with AUR support written in go." eM := []string{"go", "git"} result, _, err := Info("yay") if err != nil { diff --git a/cmd/yay/yay.go b/cmd/yay/yay.go index 3806c931..2048af4a 100644 --- a/cmd/yay/yay.go +++ b/cmd/yay/yay.go @@ -5,7 +5,6 @@ import ( "os" "github.com/jguer/yay" - pac "github.com/jguer/yay/pacman" ) func usage() { @@ -42,8 +41,13 @@ func parser() (op string, options []string, packages []string, err error) { if arg[0] == '-' && arg[1] == '-' { if arg == "--help" { op = arg + } else if arg == "--topdown" { + yay.SortMode = yay.TopDown + } else if arg == "--downtop" { + yay.SortMode = yay.DownTop + } else { + options = append(options, arg) } - options = append(options, arg) } if arg[0] != '-' { @@ -59,13 +63,14 @@ func parser() (op string, options []string, packages []string, err error) { } func main() { - op, options, pkgs, err := parser() if err != nil { fmt.Println(err) os.Exit(1) } + yay.Config() + switch op { case "-Qstats": err = yay.LocalStatistics(version) @@ -85,7 +90,7 @@ func main() { case "--help", "-h": usage() default: - err = pac.PassToPacman(op, pkgs, options) + err = yay.PassToPacman(op, pkgs, options) } if err != nil { diff --git a/pacman/pacman.go b/pacman/pacman.go index bea6ec97..8fcbe6db 100644 --- a/pacman/pacman.go +++ b/pacman/pacman.go @@ -10,9 +10,7 @@ import ( ) // RepoSearch describes a Repository search. -type RepoSearch struct { - Results []Result -} +type RepoSearch []Result // Result describes a pkg. type Result struct { @@ -20,12 +18,10 @@ type Result struct { Repository string Version string Description string + Group string Installed bool } -// SearchMode is search without numbers. -const SearchMode int = -1 - // PacmanConf describes the default pacman config file const PacmanConf string = "/etc/pacman.conf" @@ -63,75 +59,41 @@ func UpdatePackages(flags []string) error { return err } -// SearchRepos searches and prints packages in repo -func SearchRepos(pkgName string, mode int) (err error) { +// Search handles repo searches. Creates a RepoSearch struct. +func Search(pkgName string) (s RepoSearch, n int, err error) { h, err := conf.CreateHandle() defer h.Release() if err != nil { } - dbList, _ := h.SyncDbs() - localdb, _ := h.LocalDb() - - var installed bool - var i int - for _, db := range dbList.Slice() { - for _, pkg := range db.PkgCache().Slice() { - if strings.Contains(pkg.Name(), pkgName) { - if r, _ := localdb.PkgByName(pkg.Name()); r != nil { - installed = true - } else { - installed = false - } - - switch { - case mode != SearchMode && !installed: - fmt.Printf("%d \x1b[1m%s/\x1b[33m%s \x1b[36m%s \x1b[32;40mInstalled\x1b[0m\n%s\n", - i, db.Name(), pkg.Name(), pkg.Version(), pkg.Description()) - case mode != SearchMode && !installed: - fmt.Printf("%d \x1b[1m%s/\x1b[33m%s \x1b[36m%s\x1b[0m\n%s\n", - i, db.Name(), pkg.Name(), pkg.Version(), pkg.Description()) - case mode == SearchMode && !installed: - fmt.Printf("\x1b[1m%s/\x1b[33m%s \x1b[36m%s \x1b[32;40mInstalled\x1b[0m\n%s\n", - db.Name(), pkg.Name(), pkg.Version(), pkg.Description()) - case mode == SearchMode && !installed: - fmt.Printf("\x1b[1m%s/\x1b[33m%s \x1b[36m%s\x1b[0m\n%s\n", - db.Name(), pkg.Name(), pkg.Version(), pkg.Description()) - } - i++ - } - } - } - return -} - -// SearchPackages handles repo searches. Creates a RepoSearch struct. -func SearchPackages(pkgName string) (s RepoSearch, err error) { - h, err := conf.CreateHandle() - defer h.Release() + localDb, err := h.LocalDb() if err != nil { + return + } + dbList, err := h.SyncDbs() + if err != nil { + return } - - dbList, _ := h.SyncDbs() - localdb, _ := h.LocalDb() var installed bool for _, db := range dbList.Slice() { for _, pkg := range db.PkgCache().Slice() { if strings.Contains(pkg.Name(), pkgName) { - if r, _ := localdb.PkgByName(pkg.Name()); r != nil { + if r, _ := localDb.PkgByName(pkg.Name()); r != nil { installed = true } else { installed = false } - s.Results = append(s.Results, Result{ + s = append(s, Result{ Name: pkg.Name(), Description: pkg.Description(), Version: pkg.Version(), Repository: db.Name(), + Group: strings.Join(pkg.Groups().Slice(), ","), Installed: installed, }) + n++ } } } @@ -139,52 +101,49 @@ func SearchPackages(pkgName string) (s RepoSearch, err error) { } //PrintSearch receives a RepoSearch type and outputs pretty text. -func (s *RepoSearch) PrintSearch(mode int) { - for i, pkg := range s.Results { - switch { - case mode != SearchMode && pkg.Installed: - fmt.Printf("%d \033[1m%s/\x1B[33m%s \x1B[36m%s \x1B[32;40mInstalled\033[0m\n%s\n", - i, pkg.Repository, pkg.Name, pkg.Version, pkg.Description) - case mode != SearchMode && !pkg.Installed: - fmt.Printf("%d \033[1m%s/\x1B[33m%s \x1B[36m%s\033[0m\n%s\n", - i, pkg.Repository, pkg.Name, pkg.Version, pkg.Description) - case mode == SearchMode && pkg.Installed: - fmt.Printf("\033[1m%s/\x1B[33m%s \x1B[36m%s \x1B[32;40mInstalled\033[0m\n%s\n", - pkg.Repository, pkg.Name, pkg.Version, pkg.Description) - case mode == SearchMode && !pkg.Installed: - fmt.Printf("\033[1m%s/\x1B[33m%s \x1B[36m%s\033[0m\n%s\n", - pkg.Repository, pkg.Name, pkg.Version, pkg.Description) +func (s RepoSearch) PrintSearch(mode int) { + for i, res := range s { + var toprint string + if mode != -1 { + if mode == 0 { + toprint += fmt.Sprintf("%d ", len(s)-i-1) + } else { + toprint += fmt.Sprintf("%d ", i) + } } + toprint += fmt.Sprintf("\x1b[1m%s/\x1b[33m%s \x1b[36m%s \x1b[0m", + res.Repository, res.Name, res.Version) + + if len(res.Group) != 0 { + toprint += fmt.Sprintf("(%s) ", res.Group) + } + + if res.Installed == true { + toprint += fmt.Sprintf("\x1b[32;40mInstalled\x1b[0m") + } + + toprint += "\n" + res.Description + fmt.Println(toprint) } } -// PassToPacman outsorces execution to pacman binary without modifications. -func PassToPacman(op string, pkgs []string, flags []string) error { - var cmd *exec.Cmd - var args []string +// PFactory execute an action over a series of packages without reopening the handle everytime. +// Everybody told me it wouln't work. It does. It's just not pretty. +// When it worked: https://youtu.be/a4Z5BdEL0Ag?t=1m11s +func PFactory(action func(interface{})) func(name string, object interface{}, rel bool) { + h, _ := conf.CreateHandle() + localDb, _ := h.LocalDb() - args = append(args, op) - if len(pkgs) != 0 { - args = append(args, pkgs...) + return func(name string, object interface{}, rel bool) { + _, err := localDb.PkgByName(name) + if err == nil { + action(object) + } + + if rel { + h.Release() + } } - - if len(flags) != 0 { - args = append(args, flags...) - } - - if strings.Contains(op, "-Q") { - cmd = exec.Command("pacman", args...) - } else { - args = append([]string{"pacman"}, args...) - cmd = exec.Command("sudo", args...) - } - - cmd.Stdout = os.Stdout - cmd.Stdin = os.Stdin - cmd.Stderr = os.Stderr - err := cmd.Run() - return err - } // PackageSlices separates an input slice into aur and repo slices @@ -281,6 +240,27 @@ func OutofRepo(toCheck []string) (aur []string, repo []string, err error) { return } +// Install sends an install command to pacman with the pkgName slice +func Install(pkgName []string, flags []string) (err error) { + if len(pkgName) == 0 { + return nil + } + + var cmd *exec.Cmd + var args []string + args = append(args, "pacman") + args = append(args, "-S") + args = append(args, pkgName...) + args = append(args, flags...) + + cmd = exec.Command("sudo", args...) + cmd.Stdout = os.Stdout + cmd.Stdin = os.Stdin + cmd.Stderr = os.Stderr + cmd.Run() + return nil +} + // ForeignPackages returns a map of foreign packages, with their version and date as values. func ForeignPackages() (foreign map[string]*struct { Version string