Browse Source

Merge pull request #1048 from Jguer/module-types

Module types
J Guerreiro 5 years ago
parent
commit
6e2a4def99
21 changed files with 637 additions and 489 deletions
  1. 9 7
      clean.go
  2. 4 3
      cmd.go
  3. 2 1
      dep.go
  4. 19 18
      depCheck.go
  5. 7 6
      depOrder.go
  6. 23 22
      depPool.go
  7. 2 1
      download.go
  8. 60 59
      install.go
  9. 3 122
      parser.go
  10. 0 101
      parser_test.go
  11. 121 0
      pkg/types/intrange.go
  12. 145 0
      pkg/types/intrange_test.go
  13. 41 0
      pkg/types/multierror.go
  14. 30 0
      pkg/types/runes.go
  15. 33 0
      pkg/types/runes_test.go
  16. 98 0
      pkg/types/stringset.go
  17. 9 8
      print.go
  18. 6 5
      query.go
  19. 23 22
      upgrade.go
  20. 0 113
      utils.go
  21. 2 1
      vcs.go

+ 9 - 7
clean.go

@@ -5,6 +5,8 @@ import (
 	"io/ioutil"
 	"os"
 	"path/filepath"
+
+	"github.com/Jguer/yay/v9/pkg/types"
 )
 
 // GetPkgbuild gets the pkgbuild of the package 'pkg' trying the ABS first and then the AUR trying the ABS first and then the AUR.
@@ -104,8 +106,8 @@ func syncClean(parser *arguments) error {
 func cleanAUR(keepInstalled, keepCurrent, removeAll bool) error {
 	fmt.Println("removing AUR packages from cache...")
 
-	installedBases := make(stringSet)
-	inAURBases := make(stringSet)
+	installedBases := make(types.StringSet)
+	inAURBases := make(types.StringSet)
 
 	_, remotePackages, _, _, err := filterPackages()
 	if err != nil {
@@ -137,15 +139,15 @@ func cleanAUR(keepInstalled, keepCurrent, removeAll bool) error {
 		}
 
 		for _, pkg := range info {
-			inAURBases.set(pkg.PackageBase)
+			inAURBases.Set(pkg.PackageBase)
 		}
 	}
 
 	for _, pkg := range remotePackages {
 		if pkg.Base() != "" {
-			installedBases.set(pkg.Base())
+			installedBases.Set(pkg.Base())
 		} else {
-			installedBases.set(pkg.Name())
+			installedBases.Set(pkg.Name())
 		}
 	}
 
@@ -155,11 +157,11 @@ func cleanAUR(keepInstalled, keepCurrent, removeAll bool) error {
 		}
 
 		if !removeAll {
-			if keepInstalled && installedBases.get(file.Name()) {
+			if keepInstalled && installedBases.Get(file.Name()) {
 				continue
 			}
 
-			if keepCurrent && inAURBases.get(file.Name()) {
+			if keepCurrent && inAURBases.Get(file.Name()) {
 				continue
 			}
 		}

+ 4 - 3
cmd.go

@@ -8,6 +8,7 @@ import (
 
 	alpm "github.com/Jguer/go-alpm"
 	"github.com/Jguer/yay/v9/pkg/completion"
+	"github.com/Jguer/yay/v9/pkg/types"
 )
 
 var cmdArgs = makeArguments()
@@ -354,7 +355,7 @@ func displayNumberMenu(pkgS []string) (err error) {
 		return fmt.Errorf("Input too long")
 	}
 
-	include, exclude, _, otherExclude := parseNumberMenu(string(numberBuf))
+	include, exclude, _, otherExclude := types.ParseNumberMenu(string(numberBuf))
 	arguments := makeArguments()
 
 	isInclude := len(exclude) == 0 && len(otherExclude) == 0
@@ -370,7 +371,7 @@ func displayNumberMenu(pkgS []string) (err error) {
 			return fmt.Errorf("Invalid Sort Mode. Fix with yay -Y --bottomup --save")
 		}
 
-		if (isInclude && include.get(target)) || (!isInclude && !exclude.get(target)) {
+		if (isInclude && include.Get(target)) || (!isInclude && !exclude.Get(target)) {
 			arguments.addTarget(pkg.DB().Name() + "/" + pkg.Name())
 		}
 	}
@@ -387,7 +388,7 @@ func displayNumberMenu(pkgS []string) (err error) {
 			return fmt.Errorf("Invalid Sort Mode. Fix with yay -Y --bottomup --save")
 		}
 
-		if (isInclude && include.get(target)) || (!isInclude && !exclude.get(target)) {
+		if (isInclude && include.Get(target)) || (!isInclude && !exclude.Get(target)) {
 			arguments.addTarget("aur/" + pkg.Name)
 		}
 	}

+ 2 - 1
dep.go

@@ -5,6 +5,7 @@ import (
 	"strings"
 
 	alpm "github.com/Jguer/go-alpm"
+	"github.com/Jguer/yay/v9/pkg/types"
 	rpc "github.com/mikkeloscar/aur"
 )
 
@@ -33,7 +34,7 @@ func (q providers) Less(i, j int) bool {
 		return false
 	}
 
-	return lessRunes([]rune(q.Pkgs[i].Name), []rune(q.Pkgs[j].Name))
+	return types.LessRunes([]rune(q.Pkgs[i].Name), []rune(q.Pkgs[j].Name))
 }
 
 func (q providers) Swap(i, j int) {

+ 19 - 18
depCheck.go

@@ -7,9 +7,10 @@ import (
 	"sync"
 
 	alpm "github.com/Jguer/go-alpm"
+	"github.com/Jguer/yay/v9/pkg/types"
 )
 
-func (dp *depPool) checkInnerConflict(name string, conflict string, conflicts mapStringSet) {
+func (dp *depPool) checkInnerConflict(name string, conflict string, conflicts types.MapStringSet) {
 	for _, pkg := range dp.Aur {
 		if pkg.Name == name {
 			continue
@@ -31,7 +32,7 @@ func (dp *depPool) checkInnerConflict(name string, conflict string, conflicts ma
 	}
 }
 
-func (dp *depPool) checkForwardConflict(name string, conflict string, conflicts mapStringSet) {
+func (dp *depPool) checkForwardConflict(name string, conflict string, conflicts types.MapStringSet) {
 	dp.LocalDB.PkgCache().ForEach(func(pkg alpm.Package) error {
 		if pkg.Name() == name || dp.hasPackage(pkg.Name()) {
 			return nil
@@ -49,7 +50,7 @@ func (dp *depPool) checkForwardConflict(name string, conflict string, conflicts
 	})
 }
 
-func (dp *depPool) checkReverseConflict(name string, conflict string, conflicts mapStringSet) {
+func (dp *depPool) checkReverseConflict(name string, conflict string, conflicts types.MapStringSet) {
 	for _, pkg := range dp.Aur {
 		if pkg.Name == name {
 			continue
@@ -79,7 +80,7 @@ func (dp *depPool) checkReverseConflict(name string, conflict string, conflicts
 	}
 }
 
-func (dp *depPool) checkInnerConflicts(conflicts mapStringSet) {
+func (dp *depPool) checkInnerConflicts(conflicts types.MapStringSet) {
 	for _, pkg := range dp.Aur {
 		for _, conflict := range pkg.Conflicts {
 			dp.checkInnerConflict(pkg.Name, conflict, conflicts)
@@ -94,7 +95,7 @@ func (dp *depPool) checkInnerConflicts(conflicts mapStringSet) {
 	}
 }
 
-func (dp *depPool) checkForwardConflicts(conflicts mapStringSet) {
+func (dp *depPool) checkForwardConflicts(conflicts types.MapStringSet) {
 	for _, pkg := range dp.Aur {
 		for _, conflict := range pkg.Conflicts {
 			dp.checkForwardConflict(pkg.Name, conflict, conflicts)
@@ -109,7 +110,7 @@ func (dp *depPool) checkForwardConflicts(conflicts mapStringSet) {
 	}
 }
 
-func (dp *depPool) checkReverseConflicts(conflicts mapStringSet) {
+func (dp *depPool) checkReverseConflicts(conflicts types.MapStringSet) {
 	dp.LocalDB.PkgCache().ForEach(func(pkg alpm.Package) error {
 		if dp.hasPackage(pkg.Name()) {
 			return nil
@@ -124,10 +125,10 @@ func (dp *depPool) checkReverseConflicts(conflicts mapStringSet) {
 	})
 }
 
-func (dp *depPool) CheckConflicts() (mapStringSet, error) {
+func (dp *depPool) CheckConflicts() (types.MapStringSet, error) {
 	var wg sync.WaitGroup
-	innerConflicts := make(mapStringSet)
-	conflicts := make(mapStringSet)
+	innerConflicts := make(types.MapStringSet)
+	conflicts := make(types.MapStringSet)
 	wg.Add(2)
 
 	fmt.Println(bold(cyan("::") + bold(" Checking for conflicts...")))
@@ -181,9 +182,9 @@ func (dp *depPool) CheckConflicts() (mapStringSet, error) {
 	// These are used to decide what to pass --ask to (if set) or don't pass --noconfirm to
 	// As we have no idea what the order is yet we add every inner conflict to the slice
 	for name, pkgs := range innerConflicts {
-		conflicts[name] = make(stringSet)
+		conflicts[name] = make(types.StringSet)
 		for pkg := range pkgs {
-			conflicts[pkg] = make(stringSet)
+			conflicts[pkg] = make(types.StringSet)
 		}
 	}
 
@@ -203,12 +204,12 @@ func (dp *depPool) CheckConflicts() (mapStringSet, error) {
 }
 
 type missing struct {
-	Good    stringSet
+	Good    types.StringSet
 	Missing map[string][][]string
 }
 
 func (dp *depPool) _checkMissing(dep string, stack []string, missing *missing) {
-	if missing.Good.get(dep) {
+	if missing.Good.Get(dep) {
 		return
 	}
 
@@ -224,11 +225,11 @@ func (dp *depPool) _checkMissing(dep string, stack []string, missing *missing) {
 
 	aurPkg := dp.findSatisfierAur(dep)
 	if aurPkg != nil {
-		missing.Good.set(dep)
+		missing.Good.Set(dep)
 		for _, deps := range [3][]string{aurPkg.Depends, aurPkg.MakeDepends, aurPkg.CheckDepends} {
 			for _, aurDep := range deps {
 				if _, err := dp.LocalDB.PkgCache().FindSatisfier(aurDep); err == nil {
-					missing.Good.set(aurDep)
+					missing.Good.Set(aurDep)
 					continue
 				}
 
@@ -241,10 +242,10 @@ func (dp *depPool) _checkMissing(dep string, stack []string, missing *missing) {
 
 	repoPkg := dp.findSatisfierRepo(dep)
 	if repoPkg != nil {
-		missing.Good.set(dep)
+		missing.Good.Set(dep)
 		repoPkg.Depends().ForEach(func(repoDep alpm.Depend) error {
 			if _, err := dp.LocalDB.PkgCache().FindSatisfier(repoDep.String()); err == nil {
-				missing.Good.set(repoDep.String())
+				missing.Good.Set(repoDep.String())
 				return nil
 			}
 
@@ -260,7 +261,7 @@ func (dp *depPool) _checkMissing(dep string, stack []string, missing *missing) {
 
 func (dp *depPool) CheckMissing() error {
 	missing := &missing{
-		make(stringSet),
+		make(types.StringSet),
 		make(map[string][][]string),
 	}
 

+ 7 - 6
depOrder.go

@@ -2,6 +2,7 @@ package main
 
 import (
 	alpm "github.com/Jguer/go-alpm"
+	"github.com/Jguer/yay/v9/pkg/types"
 	rpc "github.com/mikkeloscar/aur"
 )
 
@@ -22,14 +23,14 @@ func (b Base) URLPath() string {
 type depOrder struct {
 	Aur     []Base
 	Repo    []*alpm.Package
-	Runtime stringSet
+	Runtime types.StringSet
 }
 
 func makeDepOrder() *depOrder {
 	return &depOrder{
 		make([]Base, 0),
 		make([]*alpm.Package, 0),
-		make(stringSet),
+		make(types.StringSet),
 	}
 }
 
@@ -59,7 +60,7 @@ func getDepOrder(dp *depPool) *depOrder {
 
 func (do *depOrder) orderPkgAur(pkg *rpc.Pkg, dp *depPool, runtime bool) {
 	if runtime {
-		do.Runtime.set(pkg.Name)
+		do.Runtime.Set(pkg.Name)
 	}
 	delete(dp.Aur, pkg.Name)
 
@@ -89,7 +90,7 @@ func (do *depOrder) orderPkgAur(pkg *rpc.Pkg, dp *depPool, runtime bool) {
 
 func (do *depOrder) orderPkgRepo(pkg *alpm.Package, dp *depPool, runtime bool) {
 	if runtime {
-		do.Runtime.set(pkg.Name())
+		do.Runtime.Set(pkg.Name())
 	}
 	delete(dp.Repo, pkg.Name())
 
@@ -119,14 +120,14 @@ func (do *depOrder) getMake() []string {
 
 	for _, base := range do.Aur {
 		for _, pkg := range base {
-			if !do.Runtime.get(pkg.Name) {
+			if !do.Runtime.Get(pkg.Name) {
 				makeOnly = append(makeOnly, pkg.Name)
 			}
 		}
 	}
 
 	for _, pkg := range do.Repo {
-		if !do.Runtime.get(pkg.Name()) {
+		if !do.Runtime.Get(pkg.Name()) {
 			makeOnly = append(makeOnly, pkg.Name())
 		}
 	}

+ 23 - 22
depPool.go

@@ -6,6 +6,7 @@ import (
 	"sync"
 
 	alpm "github.com/Jguer/go-alpm"
+	"github.com/Jguer/yay/v9/pkg/types"
 	rpc "github.com/mikkeloscar/aur"
 )
 
@@ -42,7 +43,7 @@ func (t target) String() string {
 
 type depPool struct {
 	Targets  []target
-	Explicit stringSet
+	Explicit types.StringSet
 	Repo     map[string]*alpm.Package
 	Aur      map[string]*rpc.Pkg
 	AurCache map[string]*rpc.Pkg
@@ -64,7 +65,7 @@ func makeDepPool() (*depPool, error) {
 
 	dp := &depPool{
 		make([]target, 0),
-		make(stringSet),
+		make(types.StringSet),
 		make(map[string]*alpm.Package),
 		make(map[string]*rpc.Pkg),
 		make(map[string]*rpc.Pkg),
@@ -82,7 +83,7 @@ func (dp *depPool) ResolveTargets(pkgs []string) error {
 	// RPC requests are slow
 	// Combine as many AUR package requests as possible into a single RPC
 	// call
-	aurTargets := make(stringSet)
+	aurTargets := make(types.StringSet)
 
 	pkgs = removeInvalidTargets(pkgs)
 
@@ -105,7 +106,7 @@ func (dp *depPool) ResolveTargets(pkgs []string) error {
 		// aur/ prefix means we only check the aur
 		if target.DB == "aur" || mode == modeAUR {
 			dp.Targets = append(dp.Targets, target)
-			aurTargets.set(target.DepString())
+			aurTargets.Set(target.DepString())
 			continue
 		}
 
@@ -123,7 +124,7 @@ func (dp *depPool) ResolveTargets(pkgs []string) error {
 
 		if err == nil {
 			dp.Targets = append(dp.Targets, target)
-			dp.Explicit.set(foundPkg.Name())
+			dp.Explicit.Set(foundPkg.Name())
 			dp.ResolveRepoDependency(foundPkg)
 			continue
 		} else {
@@ -138,7 +139,7 @@ func (dp *depPool) ResolveTargets(pkgs []string) error {
 			if !group.Empty() {
 				dp.Groups = append(dp.Groups, target.String())
 				group.ForEach(func(pkg alpm.Package) error {
-					dp.Explicit.set(pkg.Name())
+					dp.Explicit.Set(pkg.Name())
 					return nil
 				})
 				continue
@@ -147,7 +148,7 @@ func (dp *depPool) ResolveTargets(pkgs []string) error {
 
 		//if there was no db prefix check the aur
 		if target.DB == "" {
-			aurTargets.set(target.DepString())
+			aurTargets.Set(target.DepString())
 		}
 
 		dp.Targets = append(dp.Targets, target)
@@ -172,7 +173,7 @@ func (dp *depPool) ResolveTargets(pkgs []string) error {
 // positives.
 //
 // This method increases dependency resolve time
-func (dp *depPool) findProvides(pkgs stringSet) error {
+func (dp *depPool) findProvides(pkgs types.StringSet) error {
 	var mux sync.Mutex
 	var wg sync.WaitGroup
 
@@ -200,7 +201,7 @@ func (dp *depPool) findProvides(pkgs stringSet) error {
 		for _, result := range results {
 			mux.Lock()
 			if _, ok := dp.AurCache[result.Name]; !ok {
-				pkgs.set(result.Name)
+				pkgs.Set(result.Name)
 			}
 			mux.Unlock()
 		}
@@ -219,13 +220,13 @@ func (dp *depPool) findProvides(pkgs stringSet) error {
 	return nil
 }
 
-func (dp *depPool) cacheAURPackages(_pkgs stringSet) error {
-	pkgs := _pkgs.copy()
+func (dp *depPool) cacheAURPackages(_pkgs types.StringSet) error {
+	pkgs := _pkgs.Copy()
 	query := make([]string, 0)
 
 	for pkg := range pkgs {
 		if _, ok := dp.AurCache[pkg]; ok {
-			pkgs.remove(pkg)
+			pkgs.Remove(pkg)
 		}
 	}
 
@@ -260,9 +261,9 @@ func (dp *depPool) cacheAURPackages(_pkgs stringSet) error {
 	return nil
 }
 
-func (dp *depPool) resolveAURPackages(pkgs stringSet, explicit bool) error {
-	newPackages := make(stringSet)
-	newAURPackages := make(stringSet)
+func (dp *depPool) resolveAURPackages(pkgs types.StringSet, explicit bool) error {
+	newPackages := make(types.StringSet)
+	newAURPackages := make(types.StringSet)
 
 	err := dp.cacheAURPackages(pkgs)
 	if err != nil {
@@ -285,13 +286,13 @@ func (dp *depPool) resolveAURPackages(pkgs stringSet, explicit bool) error {
 		}
 
 		if explicit {
-			dp.Explicit.set(pkg.Name)
+			dp.Explicit.Set(pkg.Name)
 		}
 		dp.Aur[pkg.Name] = pkg
 
 		for _, deps := range [3][]string{pkg.Depends, pkg.MakeDepends, pkg.CheckDepends} {
 			for _, dep := range deps {
-				newPackages.set(dep)
+				newPackages.Set(dep)
 			}
 		}
 	}
@@ -317,7 +318,7 @@ func (dp *depPool) resolveAURPackages(pkgs stringSet, explicit bool) error {
 
 		//assume it's in the aur
 		//ditch the versioning because the RPC can't handle it
-		newAURPackages.set(dep)
+		newAURPackages.Set(dep)
 
 	}
 
@@ -386,7 +387,7 @@ func (dp *depPool) findSatisfierAur(dep string) *rpc.Pkg {
 // TODO: maybe intermix repo providers in the menu
 func (dp *depPool) findSatisfierAurCache(dep string) *rpc.Pkg {
 	depName, _, _ := splitDep(dep)
-	seen := make(stringSet)
+	seen := make(types.StringSet)
 	providers := makeProviders(depName)
 
 	if dp.LocalDB.Pkg(depName) != nil {
@@ -409,20 +410,20 @@ func (dp *depPool) findSatisfierAurCache(dep string) *rpc.Pkg {
 	}
 
 	for _, pkg := range dp.AurCache {
-		if seen.get(pkg.Name) {
+		if seen.Get(pkg.Name) {
 			continue
 		}
 
 		if pkgSatisfies(pkg.Name, pkg.Version, dep) {
 			providers.Pkgs = append(providers.Pkgs, pkg)
-			seen.set(pkg.Name)
+			seen.Set(pkg.Name)
 			continue
 		}
 
 		for _, provide := range pkg.Provides {
 			if provideSatisfies(provide, dep) {
 				providers.Pkgs = append(providers.Pkgs, pkg)
-				seen.set(pkg.Name)
+				seen.Set(pkg.Name)
 				continue
 			}
 		}

+ 2 - 1
download.go

@@ -11,6 +11,7 @@ import (
 	"sync"
 
 	alpm "github.com/Jguer/go-alpm"
+	"github.com/Jguer/yay/v9/pkg/types"
 )
 
 // Decide what download method to use:
@@ -224,7 +225,7 @@ func getPkgbuilds(pkgs []string) error {
 func getPkgbuildsfromABS(pkgs []string, path string) (bool, error) {
 	var wg sync.WaitGroup
 	var mux sync.Mutex
-	var errs MultiError
+	var errs types.MultiError
 	names := make(map[string]string)
 	missing := make([]string, 0)
 	downloaded := 0

+ 60 - 59
install.go

@@ -11,12 +11,13 @@ import (
 
 	alpm "github.com/Jguer/go-alpm"
 	"github.com/Jguer/yay/v9/pkg/completion"
+	"github.com/Jguer/yay/v9/pkg/types"
 	gosrc "github.com/Morganamilo/go-srcinfo"
 )
 
 // Install handles package installs
 func install(parser *arguments) (err error) {
-	var incompatible stringSet
+	var incompatible types.StringSet
 	var do *depOrder
 
 	var aurUp upSlice
@@ -54,8 +55,8 @@ func install(parser *arguments) (err error) {
 		return err
 	}
 
-	remoteNamesCache := sliceToStringSet(remoteNames)
-	localNamesCache := sliceToStringSet(localNames)
+	remoteNamesCache := types.SliceToStringSet(remoteNames)
+	localNamesCache := types.SliceToStringSet(localNames)
 
 	requestTargets := parser.copy().targets
 
@@ -85,7 +86,7 @@ func install(parser *arguments) (err error) {
 		}
 
 		for _, up := range repoUp {
-			if !ignore.get(up.Name) {
+			if !ignore.Get(up.Name) {
 				requestTargets = append(requestTargets, up.Name)
 				parser.addTarget(up.Name)
 			}
@@ -99,7 +100,7 @@ func install(parser *arguments) (err error) {
 		value, _, exists := cmdArgs.getArg("ignore")
 
 		if len(ignore) > 0 {
-			ignoreStr := strings.Join(ignore.toSlice(), ",")
+			ignoreStr := strings.Join(ignore.ToSlice(), ",")
 			if exists {
 				ignoreStr += "," + value
 			}
@@ -107,7 +108,7 @@ func install(parser *arguments) (err error) {
 		}
 	}
 
-	targets := sliceToStringSet(parser.targets)
+	targets := types.SliceToStringSet(parser.targets)
 
 	dp, err := getDepPool(requestTargets, warnings)
 	if err != nil {
@@ -289,14 +290,14 @@ func install(parser *arguments) (err error) {
 		expArguments.addArg("D", "asexplicit")
 
 		for _, pkg := range do.Repo {
-			if !dp.Explicit.get(pkg.Name()) && !localNamesCache.get(pkg.Name()) && !remoteNamesCache.get(pkg.Name()) {
+			if !dp.Explicit.Get(pkg.Name()) && !localNamesCache.Get(pkg.Name()) && !remoteNamesCache.Get(pkg.Name()) {
 				depArguments.addTarget(pkg.Name())
 				continue
 			}
 
-			if parser.existsArg("asdeps", "asdep") && dp.Explicit.get(pkg.Name()) {
+			if parser.existsArg("asdeps", "asdep") && dp.Explicit.Get(pkg.Name()) {
 				depArguments.addTarget(pkg.Name())
-			} else if parser.existsArg("asexp", "asexplicit") && dp.Explicit.get(pkg.Name()) {
+			} else if parser.existsArg("asexp", "asexplicit") && dp.Explicit.Get(pkg.Name()) {
 				expArguments.addTarget(pkg.Name())
 			}
 		}
@@ -411,8 +412,8 @@ func earlyRefresh(parser *arguments) error {
 	return show(passToPacman(arguments))
 }
 
-func getIncompatible(bases []Base, srcinfos map[string]*gosrc.Srcinfo) (stringSet, error) {
-	incompatible := make(stringSet)
+func getIncompatible(bases []Base, srcinfos map[string]*gosrc.Srcinfo) (types.StringSet, error) {
+	incompatible := make(types.StringSet)
 	basesMap := make(map[string]Base)
 	alpmArch, err := alpmHandle.Arch()
 	if err != nil {
@@ -427,7 +428,7 @@ nextpkg:
 			}
 		}
 
-		incompatible.set(base.Pkgbase())
+		incompatible.Set(base.Pkgbase())
 		basesMap[base.Pkgbase()] = base
 	}
 
@@ -495,7 +496,7 @@ func anyExistInCache(bases []Base) bool {
 	return false
 }
 
-func pkgbuildNumberMenu(bases []Base, installed stringSet) bool {
+func pkgbuildNumberMenu(bases []Base, installed types.StringSet) bool {
 	toPrint := ""
 	askClean := false
 
@@ -508,7 +509,7 @@ func pkgbuildNumberMenu(bases []Base, installed stringSet) bool {
 
 		anyInstalled := false
 		for _, b := range base {
-			anyInstalled = anyInstalled || installed.get(b.Name)
+			anyInstalled = anyInstalled || installed.Get(b.Name)
 		}
 
 		if anyInstalled {
@@ -528,7 +529,7 @@ func pkgbuildNumberMenu(bases []Base, installed stringSet) bool {
 	return askClean
 }
 
-func cleanNumberMenu(bases []Base, installed stringSet, hasClean bool) ([]Base, error) {
+func cleanNumberMenu(bases []Base, installed types.StringSet, hasClean bool) ([]Base, error) {
 	toClean := make([]Base, 0)
 
 	if !hasClean {
@@ -543,19 +544,19 @@ func cleanNumberMenu(bases []Base, installed stringSet, hasClean bool) ([]Base,
 		return nil, err
 	}
 
-	cInclude, cExclude, cOtherInclude, cOtherExclude := parseNumberMenu(cleanInput)
+	cInclude, cExclude, cOtherInclude, cOtherExclude := types.ParseNumberMenu(cleanInput)
 	cIsInclude := len(cExclude) == 0 && len(cOtherExclude) == 0
 
-	if cOtherInclude.get("abort") || cOtherInclude.get("ab") {
+	if cOtherInclude.Get("abort") || cOtherInclude.Get("ab") {
 		return nil, fmt.Errorf("Aborting due to user")
 	}
 
-	if !cOtherInclude.get("n") && !cOtherInclude.get("none") {
+	if !cOtherInclude.Get("n") && !cOtherInclude.Get("none") {
 		for i, base := range bases {
 			pkg := base.Pkgbase()
 			anyInstalled := false
 			for _, b := range base {
-				anyInstalled = anyInstalled || installed.get(b.Name)
+				anyInstalled = anyInstalled || installed.Get(b.Name)
 			}
 
 			dir := filepath.Join(config.BuildDir, pkg)
@@ -563,31 +564,31 @@ func cleanNumberMenu(bases []Base, installed stringSet, hasClean bool) ([]Base,
 				continue
 			}
 
-			if !cIsInclude && cExclude.get(len(bases)-i) {
+			if !cIsInclude && cExclude.Get(len(bases)-i) {
 				continue
 			}
 
-			if anyInstalled && (cOtherInclude.get("i") || cOtherInclude.get("installed")) {
+			if anyInstalled && (cOtherInclude.Get("i") || cOtherInclude.Get("installed")) {
 				toClean = append(toClean, base)
 				continue
 			}
 
-			if !anyInstalled && (cOtherInclude.get("no") || cOtherInclude.get("notinstalled")) {
+			if !anyInstalled && (cOtherInclude.Get("no") || cOtherInclude.Get("notinstalled")) {
 				toClean = append(toClean, base)
 				continue
 			}
 
-			if cOtherInclude.get("a") || cOtherInclude.get("all") {
+			if cOtherInclude.Get("a") || cOtherInclude.Get("all") {
 				toClean = append(toClean, base)
 				continue
 			}
 
-			if cIsInclude && (cInclude.get(len(bases)-i) || cOtherInclude.get(pkg)) {
+			if cIsInclude && (cInclude.Get(len(bases)-i) || cOtherInclude.Get(pkg)) {
 				toClean = append(toClean, base)
 				continue
 			}
 
-			if !cIsInclude && (!cExclude.get(len(bases)-i) && !cOtherExclude.get(pkg)) {
+			if !cIsInclude && (!cExclude.Get(len(bases)-i) && !cOtherExclude.Get(pkg)) {
 				toClean = append(toClean, base)
 				continue
 			}
@@ -597,15 +598,15 @@ func cleanNumberMenu(bases []Base, installed stringSet, hasClean bool) ([]Base,
 	return toClean, nil
 }
 
-func editNumberMenu(bases []Base, installed stringSet) ([]Base, error) {
+func editNumberMenu(bases []Base, installed types.StringSet) ([]Base, error) {
 	return editDiffNumberMenu(bases, installed, false)
 }
 
-func diffNumberMenu(bases []Base, installed stringSet) ([]Base, error) {
+func diffNumberMenu(bases []Base, installed types.StringSet) ([]Base, error) {
 	return editDiffNumberMenu(bases, installed, true)
 }
 
-func editDiffNumberMenu(bases []Base, installed stringSet, diff bool) ([]Base, error) {
+func editDiffNumberMenu(bases []Base, installed types.StringSet, diff bool) ([]Base, error) {
 	toEdit := make([]Base, 0)
 	var editInput string
 	var err error
@@ -628,45 +629,45 @@ func editDiffNumberMenu(bases []Base, installed stringSet, diff bool) ([]Base, e
 		}
 	}
 
-	eInclude, eExclude, eOtherInclude, eOtherExclude := parseNumberMenu(editInput)
+	eInclude, eExclude, eOtherInclude, eOtherExclude := types.ParseNumberMenu(editInput)
 	eIsInclude := len(eExclude) == 0 && len(eOtherExclude) == 0
 
-	if eOtherInclude.get("abort") || eOtherInclude.get("ab") {
+	if eOtherInclude.Get("abort") || eOtherInclude.Get("ab") {
 		return nil, fmt.Errorf("Aborting due to user")
 	}
 
-	if !eOtherInclude.get("n") && !eOtherInclude.get("none") {
+	if !eOtherInclude.Get("n") && !eOtherInclude.Get("none") {
 		for i, base := range bases {
 			pkg := base.Pkgbase()
 			anyInstalled := false
 			for _, b := range base {
-				anyInstalled = anyInstalled || installed.get(b.Name)
+				anyInstalled = anyInstalled || installed.Get(b.Name)
 			}
 
-			if !eIsInclude && eExclude.get(len(bases)-i) {
+			if !eIsInclude && eExclude.Get(len(bases)-i) {
 				continue
 			}
 
-			if anyInstalled && (eOtherInclude.get("i") || eOtherInclude.get("installed")) {
+			if anyInstalled && (eOtherInclude.Get("i") || eOtherInclude.Get("installed")) {
 				toEdit = append(toEdit, base)
 				continue
 			}
 
-			if !anyInstalled && (eOtherInclude.get("no") || eOtherInclude.get("notinstalled")) {
+			if !anyInstalled && (eOtherInclude.Get("no") || eOtherInclude.Get("notinstalled")) {
 				toEdit = append(toEdit, base)
 				continue
 			}
 
-			if eOtherInclude.get("a") || eOtherInclude.get("all") {
+			if eOtherInclude.Get("a") || eOtherInclude.Get("all") {
 				toEdit = append(toEdit, base)
 				continue
 			}
 
-			if eIsInclude && (eInclude.get(len(bases)-i) || eOtherInclude.get(pkg)) {
+			if eIsInclude && (eInclude.Get(len(bases)-i) || eOtherInclude.Get(pkg)) {
 				toEdit = append(toEdit, base)
 			}
 
-			if !eIsInclude && (!eExclude.get(len(bases)-i) && !eOtherExclude.get(pkg)) {
+			if !eIsInclude && (!eExclude.Get(len(bases)-i) && !eOtherExclude.Get(pkg)) {
 				toEdit = append(toEdit, base)
 			}
 		}
@@ -675,14 +676,14 @@ func editDiffNumberMenu(bases []Base, installed stringSet, diff bool) ([]Base, e
 	return toEdit, nil
 }
 
-func showPkgbuildDiffs(bases []Base, cloned stringSet) error {
+func showPkgbuildDiffs(bases []Base, cloned types.StringSet) error {
 	for _, base := range bases {
 		pkg := base.Pkgbase()
 		dir := filepath.Join(config.BuildDir, pkg)
 		if shouldUseGit(dir) {
 			start := "HEAD"
 
-			if cloned.get(pkg) {
+			if cloned.Get(pkg) {
 				start = gitEmptyTree
 			} else {
 				hasDiff, err := gitHasDiff(config.BuildDir, pkg)
@@ -774,13 +775,13 @@ func parseSrcinfoFiles(bases []Base, errIsFatal bool) (map[string]*gosrc.Srcinfo
 	return srcinfos, nil
 }
 
-func pkgbuildsToSkip(bases []Base, targets stringSet) stringSet {
-	toSkip := make(stringSet)
+func pkgbuildsToSkip(bases []Base, targets types.StringSet) types.StringSet {
+	toSkip := make(types.StringSet)
 
 	for _, base := range bases {
 		isTarget := false
 		for _, pkg := range base {
-			isTarget = isTarget || targets.get(pkg.Name)
+			isTarget = isTarget || targets.Get(pkg.Name)
 		}
 
 		if (config.ReDownload == "yes" && isTarget) || config.ReDownload == "all" {
@@ -792,7 +793,7 @@ func pkgbuildsToSkip(bases []Base, targets stringSet) stringSet {
 
 		if err == nil {
 			if alpm.VerCmp(pkgbuild.Version(), base.Version()) >= 0 {
-				toSkip.set(base.Pkgbase())
+				toSkip.Set(base.Pkgbase())
 			}
 		}
 	}
@@ -813,18 +814,18 @@ func mergePkgbuilds(bases []Base) error {
 	return nil
 }
 
-func downloadPkgbuilds(bases []Base, toSkip stringSet, buildDir string) (stringSet, error) {
-	cloned := make(stringSet)
+func downloadPkgbuilds(bases []Base, toSkip types.StringSet, buildDir string) (types.StringSet, error) {
+	cloned := make(types.StringSet)
 	downloaded := 0
 	var wg sync.WaitGroup
 	var mux sync.Mutex
-	var errs MultiError
+	var errs types.MultiError
 
 	download := func(k int, base Base) {
 		defer wg.Done()
 		pkg := base.Pkgbase()
 
-		if toSkip.get(pkg) {
+		if toSkip.Get(pkg) {
 			mux.Lock()
 			downloaded++
 			str := bold(cyan("::") + " PKGBUILD up to date, Skipping (%d/%d): %s\n")
@@ -841,7 +842,7 @@ func downloadPkgbuilds(bases []Base, toSkip stringSet, buildDir string) (stringS
 			}
 			if clone {
 				mux.Lock()
-				cloned.set(pkg)
+				cloned.Set(pkg)
 				mux.Unlock()
 			}
 		} else {
@@ -874,13 +875,13 @@ func downloadPkgbuilds(bases []Base, toSkip stringSet, buildDir string) (stringS
 	return cloned, errs.Return()
 }
 
-func downloadPkgbuildsSources(bases []Base, incompatible stringSet) (err error) {
+func downloadPkgbuildsSources(bases []Base, incompatible types.StringSet) (err error) {
 	for _, base := range bases {
 		pkg := base.Pkgbase()
 		dir := filepath.Join(config.BuildDir, pkg)
 		args := []string{"--verifysource", "-Ccf"}
 
-		if incompatible.get(pkg) {
+		if incompatible.Get(pkg) {
 			args = append(args, "--ignorearch")
 		}
 
@@ -893,7 +894,7 @@ func downloadPkgbuildsSources(bases []Base, incompatible stringSet) (err error)
 	return
 }
 
-func buildInstallPkgbuilds(dp *depPool, do *depOrder, srcinfos map[string]*gosrc.Srcinfo, parser *arguments, incompatible stringSet, conflicts mapStringSet) error {
+func buildInstallPkgbuilds(dp *depPool, do *depOrder, srcinfos map[string]*gosrc.Srcinfo, parser *arguments, incompatible types.StringSet, conflicts types.MapStringSet) error {
 	for _, base := range do.Aur {
 		pkg := base.Pkgbase()
 		dir := filepath.Join(config.BuildDir, pkg)
@@ -903,7 +904,7 @@ func buildInstallPkgbuilds(dp *depPool, do *depOrder, srcinfos map[string]*gosrc
 
 		args := []string{"--nobuild", "-fC"}
 
-		if incompatible.get(pkg) {
+		if incompatible.Get(pkg) {
 			args = append(args, "--ignorearch")
 		}
 
@@ -920,7 +921,7 @@ func buildInstallPkgbuilds(dp *depPool, do *depOrder, srcinfos map[string]*gosrc
 
 		isExplicit := false
 		for _, b := range base {
-			isExplicit = isExplicit || dp.Explicit.get(b.Name)
+			isExplicit = isExplicit || dp.Explicit.Get(b.Name)
 		}
 		if config.ReBuild == "no" || (config.ReBuild == "yes" && !isExplicit) {
 			for _, split := range base {
@@ -962,7 +963,7 @@ func buildInstallPkgbuilds(dp *depPool, do *depOrder, srcinfos map[string]*gosrc
 		} else {
 			args := []string{"-cf", "--noconfirm", "--noextract", "--noprepare", "--holdver"}
 
-			if incompatible.get(pkg) {
+			if incompatible.Get(pkg) {
 				args = append(args, "--ignorearch")
 			}
 
@@ -1017,8 +1018,8 @@ func buildInstallPkgbuilds(dp *depPool, do *depOrder, srcinfos map[string]*gosrc
 
 		//cache as a stringset. maybe make it return a string set in the first
 		//place
-		remoteNamesCache := sliceToStringSet(remoteNames)
-		localNamesCache := sliceToStringSet(localNames)
+		remoteNamesCache := types.SliceToStringSet(remoteNames)
+		localNamesCache := types.SliceToStringSet(localNames)
 
 		for _, split := range base {
 			pkgdest, ok := pkgdests[split.Name]
@@ -1027,11 +1028,11 @@ func buildInstallPkgbuilds(dp *depPool, do *depOrder, srcinfos map[string]*gosrc
 			}
 
 			arguments.addTarget(pkgdest)
-			if !dp.Explicit.get(split.Name) && !localNamesCache.get(split.Name) && !remoteNamesCache.get(split.Name) {
+			if !dp.Explicit.Get(split.Name) && !localNamesCache.Get(split.Name) && !remoteNamesCache.Get(split.Name) {
 				depArguments.addTarget(split.Name)
 			}
 
-			if dp.Explicit.get(split.Name) {
+			if dp.Explicit.Get(split.Name) {
 				if parser.existsArg("asdeps", "asdep") {
 					depArguments.addTarget(split.Name)
 				} else if parser.existsArg("asexplicit", "asexp") {

+ 3 - 122
parser.go

@@ -8,73 +8,18 @@ import (
 	"os"
 	"strconv"
 	"strings"
-	"unicode"
 
+	"github.com/Jguer/yay/v9/pkg/types"
 	rpc "github.com/mikkeloscar/aur"
 )
 
-// A basic set implementation for strings.
-// This is used a lot so it deserves its own type.
-// Other types of sets are used throughout the code but do not have
-// their own typedef.
-// String sets and <type>sets should be used throughout the code when applicable,
-// they are a lot more flexible than slices and provide easy lookup.
-type stringSet map[string]struct{}
-
-func (set stringSet) set(v string) {
-	set[v] = struct{}{}
-}
-
-func (set stringSet) get(v string) bool {
-	_, exists := set[v]
-	return exists
-}
-
-func (set stringSet) remove(v string) {
-	delete(set, v)
-}
-
-func (set stringSet) toSlice() []string {
-	slice := make([]string, 0, len(set))
-
-	for v := range set {
-		slice = append(slice, v)
-	}
-
-	return slice
-}
-
-func (set stringSet) copy() stringSet {
-	newSet := make(stringSet)
-
-	for str := range set {
-		newSet.set(str)
-	}
-
-	return newSet
-}
-
-func sliceToStringSet(in []string) stringSet {
-	set := make(stringSet)
-
-	for _, v := range in {
-		set.set(v)
-	}
-
-	return set
-}
-
-func makeStringSet(in ...string) stringSet {
-	return sliceToStringSet(in)
-}
-
 // Parses command line arguments in a way we can interact with programmatically but
 // also in a way that can easily be passed to pacman later on.
 type arguments struct {
 	op      string
 	options map[string]string
 	globals map[string]string
-	doubles stringSet // Tracks args passed twice such as -yy and -dd
+	doubles types.StringSet // Tracks args passed twice such as -yy and -dd
 	targets []string
 }
 
@@ -83,7 +28,7 @@ func makeArguments() *arguments {
 		"",
 		make(map[string]string),
 		make(map[string]string),
-		make(stringSet),
+		make(types.StringSet),
 		make([]string, 0),
 	}
 }
@@ -886,70 +831,6 @@ func (parser *arguments) extractYayOptions() {
 	config.AURURL = strings.TrimRight(config.AURURL, "/")
 }
 
-//parses input for number menus split by spaces or commas
-//supports individual selection: 1 2 3 4
-//supports range selections: 1-4 10-20
-//supports negation: ^1 ^1-4
-//
-//include and excule holds numbers that should be added and should not be added
-//respectively. other holds anything that can't be parsed as an int. This is
-//intended to allow words inside of number menus. e.g. 'all' 'none' 'abort'
-//of course the implementation is up to the caller, this function mearley parses
-//the input and organizes it
-func parseNumberMenu(input string) (intRanges, intRanges, stringSet, stringSet) {
-	include := make(intRanges, 0)
-	exclude := make(intRanges, 0)
-	otherInclude := make(stringSet)
-	otherExclude := make(stringSet)
-
-	words := strings.FieldsFunc(input, func(c rune) bool {
-		return unicode.IsSpace(c) || c == ','
-	})
-
-	for _, word := range words {
-		var num1 int
-		var num2 int
-		var err error
-		invert := false
-		other := otherInclude
-
-		if word[0] == '^' {
-			invert = true
-			other = otherExclude
-			word = word[1:]
-		}
-
-		ranges := strings.SplitN(word, "-", 2)
-
-		num1, err = strconv.Atoi(ranges[0])
-		if err != nil {
-			other.set(strings.ToLower(word))
-			continue
-		}
-
-		if len(ranges) == 2 {
-			num2, err = strconv.Atoi(ranges[1])
-			if err != nil {
-				other.set(strings.ToLower(word))
-				continue
-			}
-		} else {
-			num2 = num1
-		}
-
-		mi := min(num1, num2)
-		ma := max(num1, num2)
-
-		if !invert {
-			include = append(include, makeIntRange(mi, ma))
-		} else {
-			exclude = append(exclude, makeIntRange(mi, ma))
-		}
-	}
-
-	return include, exclude, otherInclude, otherExclude
-}
-
 // Crude html parsing, good enough for the arch news
 // This is only displayed in the terminal so there should be no security
 // concerns

+ 0 - 101
parser_test.go

@@ -1,101 +0,0 @@
-package main
-
-import "testing"
-
-func intRangesEqual(a, b intRanges) bool {
-	if a == nil && b == nil {
-		return true
-	}
-
-	if a == nil || b == nil {
-		return false
-	}
-
-	if len(a) != len(b) {
-		return false
-	}
-
-	for n := range a {
-		r1 := a[n]
-		r2 := b[n]
-
-		if r1.min != r2.min || r1.max != r2.max {
-			return false
-		}
-	}
-
-	return true
-}
-
-func stringSetEqual(a, b stringSet) bool {
-	if a == nil && b == nil {
-		return true
-	}
-
-	if a == nil || b == nil {
-		return false
-	}
-
-	if len(a) != len(b) {
-		return false
-	}
-
-	for n := range a {
-		if !b.get(n) {
-			return false
-		}
-	}
-
-	return true
-}
-
-func TestParseNumberMenu(t *testing.T) {
-	type result struct {
-		Include      intRanges
-		Exclude      intRanges
-		OtherInclude stringSet
-		OtherExclude stringSet
-	}
-
-	inputs := []string{
-		"1 2 3 4 5",
-		"1-10 5-15",
-		"10-5 90-85",
-		"1 ^2 ^10-5 99 ^40-38 ^123 60-62",
-		"abort all none",
-		"a-b ^a-b ^abort",
-		"1\t2   3      4\t\t  \t 5",
-		"1 2,3, 4,  5,6 ,7  ,8",
-		"",
-		"   \t   ",
-		"A B C D E",
-	}
-
-	expected := []result{
-		{intRanges{makeIntRange(1, 1), makeIntRange(2, 2), makeIntRange(3, 3), makeIntRange(4, 4), makeIntRange(5, 5)}, intRanges{}, make(stringSet), make(stringSet)},
-		{intRanges{makeIntRange(1, 10), makeIntRange(5, 15)}, intRanges{}, make(stringSet), make(stringSet)},
-		{intRanges{makeIntRange(5, 10), makeIntRange(85, 90)}, intRanges{}, make(stringSet), make(stringSet)},
-		{intRanges{makeIntRange(1, 1), makeIntRange(99, 99), makeIntRange(60, 62)}, intRanges{makeIntRange(2, 2), makeIntRange(5, 10), makeIntRange(38, 40), makeIntRange(123, 123)}, make(stringSet), make(stringSet)},
-		{intRanges{}, intRanges{}, makeStringSet("abort", "all", "none"), make(stringSet)},
-		{intRanges{}, intRanges{}, makeStringSet("a-b"), makeStringSet("abort", "a-b")},
-		{intRanges{makeIntRange(1, 1), makeIntRange(2, 2), makeIntRange(3, 3), makeIntRange(4, 4), makeIntRange(5, 5)}, intRanges{}, make(stringSet), make(stringSet)},
-		{intRanges{makeIntRange(1, 1), makeIntRange(2, 2), makeIntRange(3, 3), makeIntRange(4, 4), makeIntRange(5, 5), makeIntRange(6, 6), makeIntRange(7, 7), makeIntRange(8, 8)}, intRanges{}, make(stringSet), make(stringSet)},
-		{intRanges{}, intRanges{}, make(stringSet), make(stringSet)},
-		{intRanges{}, intRanges{}, make(stringSet), make(stringSet)},
-		{intRanges{}, intRanges{}, makeStringSet("a", "b", "c", "d", "e"), make(stringSet)},
-	}
-
-	for n, in := range inputs {
-		res := expected[n]
-		include, exclude, otherInclude, otherExclude := parseNumberMenu(in)
-
-		if !intRangesEqual(include, res.Include) ||
-			!intRangesEqual(exclude, res.Exclude) ||
-			!stringSetEqual(otherInclude, res.OtherInclude) ||
-			!stringSetEqual(otherExclude, res.OtherExclude) {
-
-			t.Fatalf("Test %d Failed: Expected: include=%+v exclude=%+v otherInclude=%+v otherExclude=%+v got include=%+v excluive=%+v otherInclude=%+v otherExclude=%+v",
-				n+1, res.Include, res.Exclude, res.OtherInclude, res.OtherExclude, include, exclude, otherInclude, otherExclude)
-		}
-	}
-}

+ 121 - 0
pkg/types/intrange.go

@@ -0,0 +1,121 @@
+package types
+
+import (
+	"strconv"
+	"strings"
+	"unicode"
+)
+
+// IntRange stores a max and min amount for range
+type IntRange struct {
+	min int
+	max int
+}
+
+// IntRanges is a slice of IntRange
+type IntRanges []IntRange
+
+func makeIntRange(min, max int) IntRange {
+	return IntRange{
+		min,
+		max,
+	}
+}
+
+// Get returns true if the argument n is included in the closed range
+// between min and max
+func (r IntRange) Get(n int) bool {
+	return n >= r.min && n <= r.max
+}
+
+// Get returns true if the argument n is included in the closed range
+// between min and max of any of the provided IntRanges
+func (rs IntRanges) Get(n int) bool {
+	for _, r := range rs {
+		if r.Get(n) {
+			return true
+		}
+	}
+
+	return false
+}
+
+// Min returns min value between a and b
+func Min(a, b int) int {
+	if a < b {
+		return a
+	}
+	return b
+}
+
+// Max returns max value between a and b
+func Max(a, b int) int {
+	if a < b {
+		return b
+	}
+	return a
+}
+
+// ParseNumberMenu parses input for number menus split by spaces or commas
+//supports individual selection: 1 2 3 4
+//supports range selections: 1-4 10-20
+//supports negation: ^1 ^1-4
+//
+//include and excule holds numbers that should be added and should not be added
+//respectively. other holds anything that can't be parsed as an int. This is
+//intended to allow words inside of number menus. e.g. 'all' 'none' 'abort'
+//of course the implementation is up to the caller, this function mearley parses
+//the input and organizes it
+func ParseNumberMenu(input string) (IntRanges, IntRanges, StringSet, StringSet) {
+	include := make(IntRanges, 0)
+	exclude := make(IntRanges, 0)
+	otherInclude := make(StringSet)
+	otherExclude := make(StringSet)
+
+	words := strings.FieldsFunc(input, func(c rune) bool {
+		return unicode.IsSpace(c) || c == ','
+	})
+
+	for _, word := range words {
+		var num1 int
+		var num2 int
+		var err error
+		invert := false
+		other := otherInclude
+
+		if word[0] == '^' {
+			invert = true
+			other = otherExclude
+			word = word[1:]
+		}
+
+		ranges := strings.SplitN(word, "-", 2)
+
+		num1, err = strconv.Atoi(ranges[0])
+		if err != nil {
+			other.Set(strings.ToLower(word))
+			continue
+		}
+
+		if len(ranges) == 2 {
+			num2, err = strconv.Atoi(ranges[1])
+			if err != nil {
+				other.Set(strings.ToLower(word))
+				continue
+			}
+		} else {
+			num2 = num1
+		}
+
+		mi := Min(num1, num2)
+		ma := Max(num1, num2)
+
+		if !invert {
+			include = append(include, makeIntRange(mi, ma))
+		} else {
+			exclude = append(exclude, makeIntRange(mi, ma))
+		}
+	}
+
+	return include, exclude, otherInclude, otherExclude
+}

+ 145 - 0
pkg/types/intrange_test.go

@@ -0,0 +1,145 @@
+package types
+
+import (
+	"testing"
+)
+
+func TestParseNumberMenu(t *testing.T) {
+	type result struct {
+		Include      IntRanges
+		Exclude      IntRanges
+		OtherInclude StringSet
+		OtherExclude StringSet
+	}
+
+	inputs := []string{
+		"1 2 3 4 5",
+		"1-10 5-15",
+		"10-5 90-85",
+		"1 ^2 ^10-5 99 ^40-38 ^123 60-62",
+		"abort all none",
+		"a-b ^a-b ^abort",
+		"-9223372036854775809-9223372036854775809",
+		"1\t2   3      4\t\t  \t 5",
+		"1 2,3, 4,  5,6 ,7  ,8",
+		"",
+		"   \t   ",
+		"A B C D E",
+	}
+
+	expected := []result{
+		{IntRanges{makeIntRange(1, 1), makeIntRange(2, 2), makeIntRange(3, 3), makeIntRange(4, 4), makeIntRange(5, 5)}, IntRanges{}, make(StringSet), make(StringSet)},
+		{IntRanges{makeIntRange(1, 10), makeIntRange(5, 15)}, IntRanges{}, make(StringSet), make(StringSet)},
+		{IntRanges{makeIntRange(5, 10), makeIntRange(85, 90)}, IntRanges{}, make(StringSet), make(StringSet)},
+		{IntRanges{makeIntRange(1, 1), makeIntRange(99, 99), makeIntRange(60, 62)}, IntRanges{makeIntRange(2, 2), makeIntRange(5, 10), makeIntRange(38, 40), makeIntRange(123, 123)}, make(StringSet), make(StringSet)},
+		{IntRanges{}, IntRanges{}, MakeStringSet("abort", "all", "none"), make(StringSet)},
+		{IntRanges{}, IntRanges{}, MakeStringSet("a-b"), MakeStringSet("abort", "a-b")},
+		{IntRanges{}, IntRanges{}, MakeStringSet("-9223372036854775809-9223372036854775809"), make(StringSet)},
+		{IntRanges{makeIntRange(1, 1), makeIntRange(2, 2), makeIntRange(3, 3), makeIntRange(4, 4), makeIntRange(5, 5)}, IntRanges{}, make(StringSet), make(StringSet)},
+		{IntRanges{makeIntRange(1, 1), makeIntRange(2, 2), makeIntRange(3, 3), makeIntRange(4, 4), makeIntRange(5, 5), makeIntRange(6, 6), makeIntRange(7, 7), makeIntRange(8, 8)}, IntRanges{}, make(StringSet), make(StringSet)},
+		{IntRanges{}, IntRanges{}, make(StringSet), make(StringSet)},
+		{IntRanges{}, IntRanges{}, make(StringSet), make(StringSet)},
+		{IntRanges{}, IntRanges{}, MakeStringSet("a", "b", "c", "d", "e"), make(StringSet)},
+	}
+
+	for n, in := range inputs {
+		res := expected[n]
+		include, exclude, otherInclude, otherExclude := ParseNumberMenu(in)
+
+		if !intRangesEqual(include, res.Include) ||
+			!intRangesEqual(exclude, res.Exclude) ||
+			!StringSetEqual(otherInclude, res.OtherInclude) ||
+			!StringSetEqual(otherExclude, res.OtherExclude) {
+
+			t.Fatalf("Test %d Failed: Expected: include=%+v exclude=%+v otherInclude=%+v otherExclude=%+v got include=%+v excluive=%+v otherInclude=%+v otherExclude=%+v",
+				n+1, res.Include, res.Exclude, res.OtherInclude, res.OtherExclude, include, exclude, otherInclude, otherExclude)
+		}
+	}
+}
+
+func TestIntRange_Get(t *testing.T) {
+	type fields struct {
+		min int
+		max int
+	}
+	type args struct {
+		n int
+	}
+	tests := []struct {
+		name   string
+		fields fields
+		args   args
+		want   bool
+	}{
+		{name: "normal range true", fields: fields{0, 10}, args: args{5}, want: true},
+		{name: "normal start range true", fields: fields{0, 10}, args: args{0}, want: true},
+		{name: "normal end range true", fields: fields{0, 10}, args: args{10}, want: true},
+		{name: "small range true", fields: fields{1, 1}, args: args{1}, want: true},
+		{name: "normal start range false", fields: fields{1, 2}, args: args{0}, want: false},
+		{name: "normal end range false", fields: fields{1, 2}, args: args{3}, want: false},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			r := IntRange{
+				min: tt.fields.min,
+				max: tt.fields.max,
+			}
+			if got := r.Get(tt.args.n); got != tt.want {
+				t.Errorf("IntRange.Get() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func intRangesEqual(a, b IntRanges) bool {
+	if a == nil && b == nil {
+		return true
+	}
+
+	if a == nil || b == nil {
+		return false
+	}
+
+	if len(a) != len(b) {
+		return false
+	}
+
+	for n := range a {
+		r1 := a[n]
+		r2 := b[n]
+
+		if r1.min != r2.min || r1.max != r2.max {
+			return false
+		}
+	}
+
+	return true
+}
+
+func TestIntRanges_Get(t *testing.T) {
+	type args struct {
+		n int
+	}
+	tests := []struct {
+		name string
+		rs   IntRanges
+		args args
+		want bool
+	}{
+		{name: "normal range true", rs: IntRanges{{0, 10}}, args: args{5}, want: true},
+		{name: "normal ranges inbetween true", rs: IntRanges{{0, 4}, {5, 10}}, args: args{5}, want: true},
+		{name: "normal ranges inbetween false", rs: IntRanges{{0, 4}, {6, 10}}, args: args{5}, want: false},
+		{name: "normal start range true", rs: IntRanges{{0, 10}}, args: args{0}, want: true},
+		{name: "normal end range true", rs: IntRanges{{0, 10}}, args: args{10}, want: true},
+		{name: "small range true", rs: IntRanges{{1, 1}, {3, 3}}, args: args{1}, want: true},
+		{name: "normal start range false", rs: IntRanges{{1, 2}}, args: args{0}, want: false},
+		{name: "normal end range false", rs: IntRanges{{1, 2}}, args: args{3}, want: false},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := tt.rs.Get(tt.args.n); got != tt.want {
+				t.Errorf("IntRanges.Get() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}

+ 41 - 0
pkg/types/multierror.go

@@ -0,0 +1,41 @@
+package types
+
+import "sync"
+
+// MultiError type handles error accumulation from goroutines
+type MultiError struct {
+	Errors []error
+	mux    sync.Mutex
+}
+
+// Error turns the MultiError structure into a string
+func (err *MultiError) Error() string {
+	str := ""
+
+	for _, e := range err.Errors {
+		str += e.Error() + "\n"
+	}
+
+	return str[:len(str)-1]
+}
+
+// Add adds an error to the Multierror structure
+func (err *MultiError) Add(e error) {
+	if e == nil {
+		return
+	}
+
+	err.mux.Lock()
+	err.Errors = append(err.Errors, e)
+	err.mux.Unlock()
+}
+
+// Return is used as a wrapper on return on wether to return the
+// MultiError Structure if errors exist or nil instead of delivering an empty structure
+func (err *MultiError) Return() error {
+	if len(err.Errors) > 0 {
+		return err
+	}
+
+	return nil
+}

+ 30 - 0
pkg/types/runes.go

@@ -0,0 +1,30 @@
+package types
+
+import "unicode"
+
+// LessRunes compares two rune values, and returns true if the first argument is lexicographicaly smaller.
+func LessRunes(iRunes, jRunes []rune) bool {
+	max := len(iRunes)
+	if max > len(jRunes) {
+		max = len(jRunes)
+	}
+
+	for idx := 0; idx < max; idx++ {
+		ir := iRunes[idx]
+		jr := jRunes[idx]
+
+		lir := unicode.ToLower(ir)
+		ljr := unicode.ToLower(jr)
+
+		if lir != ljr {
+			return lir < ljr
+		}
+
+		// the lowercase runes are the same, so compare the original
+		if ir != jr {
+			return ir < jr
+		}
+	}
+
+	return len(iRunes) < len(jRunes)
+}

+ 33 - 0
pkg/types/runes_test.go

@@ -0,0 +1,33 @@
+package types
+
+import "testing"
+
+func TestLessRunes(t *testing.T) {
+	t.Parallel()
+	type args struct {
+		iRunes []rune
+		jRunes []rune
+	}
+	tests := []struct {
+		name string
+		args args
+		want bool
+	}{
+		{name: "nilslices", args: args{iRunes: nil, jRunes: nil}, want: false},
+		{name: "emptyslices", args: args{iRunes: []rune{}, jRunes: []rune{}}, want: false},
+		{name: "simpleslice a,b", args: args{iRunes: []rune{'a'}, jRunes: []rune{'b'}}, want: true},
+		{name: "simpleslice b,a", args: args{iRunes: []rune{'b'}, jRunes: []rune{'a'}}, want: false},
+		{name: "equalslice", args: args{iRunes: []rune{'a', 'a', 'a'}, jRunes: []rune{'a', 'a', 'a'}}, want: false},
+		{name: "uppercase", args: args{iRunes: []rune{'a'}, jRunes: []rune{'A'}}, want: false},
+		{name: "longerFirstArg", args: args{iRunes: []rune{'a', 'b'}, jRunes: []rune{'a'}}, want: false},
+		{name: "longerSecondArg", args: args{iRunes: []rune{'a'}, jRunes: []rune{'a', 'b'}}, want: true},
+		{name: "utf8 less", args: args{iRunes: []rune{'世', '2', '0'}, jRunes: []rune{'世', '界', '3'}}, want: true},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := LessRunes(tt.args.iRunes, tt.args.jRunes); got != tt.want {
+				t.Errorf("LessRunes() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}

+ 98 - 0
pkg/types/stringset.go

@@ -0,0 +1,98 @@
+package types
+
+// StringSet is a basic set implementation for strings.
+// This is used a lot so it deserves its own type.
+// Other types of sets are used throughout the code but do not have
+// their own typedef.
+// String sets and <type>sets should be used throughout the code when applicable,
+// they are a lot more flexible than slices and provide easy lookup.
+type StringSet map[string]struct{}
+
+// MapStringSet is a Map of StringSets.
+type MapStringSet map[string]StringSet
+
+// Add adds a new value to the Map.
+func (mss MapStringSet) Add(n string, v string) {
+	_, ok := mss[n]
+	if !ok {
+		mss[n] = make(StringSet)
+	}
+	mss[n].Set(v)
+}
+
+// Set sets key in StringSet.
+func (set StringSet) Set(v string) {
+	set[v] = struct{}{}
+}
+
+// Get returns true if the key exists in the set.
+func (set StringSet) Get(v string) bool {
+	_, exists := set[v]
+	return exists
+}
+
+// Remove deletes a key from the set.
+func (set StringSet) Remove(v string) {
+	delete(set, v)
+}
+
+// ToSlice turns all keys into a string slice.
+func (set StringSet) ToSlice() []string {
+	slice := make([]string, 0, len(set))
+
+	for v := range set {
+		slice = append(slice, v)
+	}
+
+	return slice
+}
+
+// Copy copies a StringSet into a new structure of the same type.
+func (set StringSet) Copy() StringSet {
+	newSet := make(StringSet)
+
+	for str := range set {
+		newSet.Set(str)
+	}
+
+	return newSet
+}
+
+// SliceToStringSet creates a new StringSet from an input slice
+func SliceToStringSet(in []string) StringSet {
+	set := make(StringSet)
+
+	for _, v := range in {
+		set.Set(v)
+	}
+
+	return set
+}
+
+// MakeStringSet creates a new StringSet from a set of arguments
+func MakeStringSet(in ...string) StringSet {
+	return SliceToStringSet(in)
+}
+
+// StringSetEqual compares if two StringSets have the same values
+func StringSetEqual(a, b StringSet) bool {
+	if a == nil && b == nil {
+		return true
+	}
+
+	if a == nil || b == nil {
+		return false
+	}
+
+	if len(a) != len(b) {
+		return false
+	}
+
+	for n := range a {
+		if !b.Get(n) {
+			return false
+		}
+	}
+
+	return true
+}

+ 9 - 8
print.go

@@ -12,6 +12,7 @@ import (
 	"strings"
 	"time"
 
+	"github.com/Jguer/yay/v9/pkg/types"
 	rpc "github.com/mikkeloscar/aur"
 )
 
@@ -176,8 +177,8 @@ func (u upSlice) print() {
 		packNameLen := len(pack.StylizedNameWithRepository())
 		version, _ := getVersionDiff(pack.LocalVersion, pack.RemoteVersion)
 		packVersionLen := len(version)
-		longestName = max(packNameLen, longestName)
-		longestVersion = max(packVersionLen, longestVersion)
+		longestName = types.Max(packNameLen, longestName)
+		longestVersion = types.Max(packVersionLen, longestVersion)
 	}
 
 	namePadding := fmt.Sprintf("%%-%ds  ", longestName)
@@ -208,7 +209,7 @@ func (do *depOrder) Print() {
 	aurMakeLen := 0
 
 	for _, pkg := range do.Repo {
-		if do.Runtime.get(pkg.Name()) {
+		if do.Runtime.Get(pkg.Name()) {
 			repo += "  " + pkg.Name() + "-" + pkg.Version()
 			repoLen++
 		} else {
@@ -231,7 +232,7 @@ func (do *depOrder) Print() {
 			pkgStrMake += " ("
 
 			for _, split := range base {
-				if do.Runtime.get(split.Name) {
+				if do.Runtime.Get(split.Name) {
 					pkgStr += split.Name + " "
 					aurLen++
 					push = true
@@ -244,7 +245,7 @@ func (do *depOrder) Print() {
 
 			pkgStr = pkgStr[:len(pkgStr)-1] + ")"
 			pkgStrMake = pkgStrMake[:len(pkgStrMake)-1] + ")"
-		case do.Runtime.get(base[0].Name):
+		case do.Runtime.Get(base[0].Name):
 			aurLen++
 			push = true
 		default:
@@ -389,7 +390,7 @@ func printNumberOfUpdates() error {
 
 //TODO: Make it less hacky
 func printUpdateList(parser *arguments) error {
-	targets := sliceToStringSet(parser.targets)
+	targets := types.SliceToStringSet(parser.targets)
 	warnings := &aurWarnings{}
 	old := os.Stdout // keep backup of the real stdout
 	os.Stdout = nil
@@ -408,7 +409,7 @@ func printUpdateList(parser *arguments) error {
 
 	if !parser.existsArg("m", "foreign") {
 		for _, pkg := range repoUp {
-			if noTargets || targets.get(pkg.Name) {
+			if noTargets || targets.Get(pkg.Name) {
 				if parser.existsArg("q", "quiet") {
 					fmt.Printf("%s\n", pkg.Name)
 				} else {
@@ -421,7 +422,7 @@ func printUpdateList(parser *arguments) error {
 
 	if !parser.existsArg("n", "native") {
 		for _, pkg := range aurUp {
-			if noTargets || targets.get(pkg.Name) {
+			if noTargets || targets.Get(pkg.Name) {
 				if parser.existsArg("q", "quiet") {
 					fmt.Printf("%s\n", pkg.Name)
 				} else {

+ 6 - 5
query.go

@@ -9,6 +9,7 @@ import (
 	"time"
 
 	alpm "github.com/Jguer/go-alpm"
+	"github.com/Jguer/yay/v9/pkg/types"
 	rpc "github.com/mikkeloscar/aur"
 )
 
@@ -37,9 +38,9 @@ func (q aurQuery) Less(i, j int) bool {
 	case "popularity":
 		result = q[i].Popularity > q[j].Popularity
 	case "name":
-		result = lessRunes([]rune(q[i].Name), []rune(q[j].Name))
+		result = types.LessRunes([]rune(q[i].Name), []rune(q[j].Name))
 	case "base":
-		result = lessRunes([]rune(q[i].PackageBase), []rune(q[j].PackageBase))
+		result = types.LessRunes([]rune(q[i].PackageBase), []rune(q[j].PackageBase))
 	case "submitted":
 		result = q[i].FirstSubmitted < q[j].FirstSubmitted
 	case "modified":
@@ -341,7 +342,7 @@ func hangingPackages(removeOptional bool) (hanging []string, err error) {
 	// State = 2 - Keep package and have iterated over dependencies
 	safePackages := make(map[string]uint8)
 	// provides stores a mapping from the provides name back to the original package name
-	provides := make(mapStringSet)
+	provides := make(types.MapStringSet)
 	packages := localDB.PkgCache()
 
 	// Mark explicit dependencies and enumerate the provides list
@@ -478,7 +479,7 @@ func aurInfo(names []string, warnings *aurWarnings) ([]*rpc.Pkg, error) {
 	seen := make(map[string]int)
 	var mux sync.Mutex
 	var wg sync.WaitGroup
-	var errs MultiError
+	var errs types.MultiError
 
 	makeRequest := func(n, max int) {
 		defer wg.Done()
@@ -496,7 +497,7 @@ func aurInfo(names []string, warnings *aurWarnings) ([]*rpc.Pkg, error) {
 	}
 
 	for n := 0; n < len(names); n += config.RequestSplitN {
-		max := min(len(names), n+config.RequestSplitN)
+		max := types.Min(len(names), n+config.RequestSplitN)
 		wg.Add(1)
 		go makeRequest(n, max)
 	}

+ 23 - 22
upgrade.go

@@ -7,6 +7,7 @@ import (
 	"unicode"
 
 	alpm "github.com/Jguer/go-alpm"
+	"github.com/Jguer/yay/v9/pkg/types"
 	rpc "github.com/mikkeloscar/aur"
 )
 
@@ -28,14 +29,14 @@ func (u upSlice) Less(i, j int) bool {
 	if u[i].Repository == u[j].Repository {
 		iRunes := []rune(u[i].Name)
 		jRunes := []rune(u[j].Name)
-		return lessRunes(iRunes, jRunes)
+		return types.LessRunes(iRunes, jRunes)
 	}
 
 	syncDB, err := alpmHandle.SyncDBs()
 	if err != nil {
 		iRunes := []rune(u[i].Repository)
 		jRunes := []rune(u[j].Repository)
-		return lessRunes(iRunes, jRunes)
+		return types.LessRunes(iRunes, jRunes)
 	}
 
 	less := false
@@ -58,7 +59,7 @@ func (u upSlice) Less(i, j int) bool {
 
 	iRunes := []rune(u[i].Repository)
 	jRunes := []rune(u[j].Repository)
-	return lessRunes(iRunes, jRunes)
+	return types.LessRunes(iRunes, jRunes)
 
 }
 
@@ -120,7 +121,7 @@ func upList(warnings *aurWarnings) (upSlice, upSlice, error) {
 	var repoUp upSlice
 	var aurUp upSlice
 
-	var errs MultiError
+	var errs types.MultiError
 
 	aurdata := make(map[string]*rpc.Pkg)
 
@@ -168,12 +169,12 @@ func upList(warnings *aurWarnings) (upSlice, upSlice, error) {
 	printLocalNewerThanAUR(remote, aurdata)
 
 	if develUp != nil {
-		names := make(stringSet)
+		names := make(types.StringSet)
 		for _, up := range develUp {
-			names.set(up.Name)
+			names.Set(up.Name)
 		}
 		for _, up := range aurUp {
-			if !names.get(up.Name) {
+			if !names.Get(up.Name) {
 				develUp = append(develUp, up)
 			}
 		}
@@ -324,9 +325,9 @@ func upRepo(local []alpm.Package) (upSlice, error) {
 }
 
 // upgradePkgs handles updating the cache and installing updates.
-func upgradePkgs(aurUp, repoUp upSlice) (stringSet, stringSet, error) {
-	ignore := make(stringSet)
-	aurNames := make(stringSet)
+func upgradePkgs(aurUp, repoUp upSlice) (types.StringSet, types.StringSet, error) {
+	ignore := make(types.StringSet)
+	aurNames := make(types.StringSet)
 
 	allUpLen := len(repoUp) + len(aurUp)
 	if allUpLen == 0 {
@@ -335,7 +336,7 @@ func upgradePkgs(aurUp, repoUp upSlice) (stringSet, stringSet, error) {
 
 	if !config.UpgradeMenu {
 		for _, pkg := range aurUp {
-			aurNames.set(pkg.Name)
+			aurNames.Set(pkg.Name)
 		}
 
 		return ignore, aurNames, nil
@@ -358,37 +359,37 @@ func upgradePkgs(aurUp, repoUp upSlice) (stringSet, stringSet, error) {
 	//upgrade menu asks you which packages to NOT upgrade so in this case
 	//include and exclude are kind of swapped
 	//include, exclude, other := parseNumberMenu(string(numberBuf))
-	include, exclude, otherInclude, otherExclude := parseNumberMenu(numbers)
+	include, exclude, otherInclude, otherExclude := types.ParseNumberMenu(numbers)
 
 	isInclude := len(exclude) == 0 && len(otherExclude) == 0
 
 	for i, pkg := range repoUp {
-		if isInclude && otherInclude.get(pkg.Repository) {
-			ignore.set(pkg.Name)
+		if isInclude && otherInclude.Get(pkg.Repository) {
+			ignore.Set(pkg.Name)
 		}
 
-		if isInclude && !include.get(len(repoUp)-i+len(aurUp)) {
+		if isInclude && !include.Get(len(repoUp)-i+len(aurUp)) {
 			continue
 		}
 
-		if !isInclude && (exclude.get(len(repoUp)-i+len(aurUp)) || otherExclude.get(pkg.Repository)) {
+		if !isInclude && (exclude.Get(len(repoUp)-i+len(aurUp)) || otherExclude.Get(pkg.Repository)) {
 			continue
 		}
 
-		ignore.set(pkg.Name)
+		ignore.Set(pkg.Name)
 	}
 
 	for i, pkg := range aurUp {
-		if isInclude && otherInclude.get(pkg.Repository) {
+		if isInclude && otherInclude.Get(pkg.Repository) {
 			continue
 		}
 
-		if isInclude && !include.get(len(aurUp)-i) {
-			aurNames.set(pkg.Name)
+		if isInclude && !include.Get(len(aurUp)-i) {
+			aurNames.Set(pkg.Name)
 		}
 
-		if !isInclude && (exclude.get(len(aurUp)-i) || otherExclude.get(pkg.Repository)) {
-			aurNames.set(pkg.Name)
+		if !isInclude && (exclude.Get(len(aurUp)-i) || otherExclude.Get(pkg.Repository)) {
+			aurNames.Set(pkg.Name)
 		}
 	}
 

+ 0 - 113
utils.go

@@ -2,90 +2,10 @@ package main
 
 import (
 	"fmt"
-	"sync"
-	"unicode"
 )
 
 const gitEmptyTree = "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
 
-type mapStringSet map[string]stringSet
-
-type intRange struct {
-	min int
-	max int
-}
-
-func makeIntRange(min, max int) intRange {
-	return intRange{
-		min,
-		max,
-	}
-}
-
-func (r intRange) get(n int) bool {
-	return n >= r.min && n <= r.max
-}
-
-type intRanges []intRange
-
-func (rs intRanges) get(n int) bool {
-	for _, r := range rs {
-		if r.get(n) {
-			return true
-		}
-	}
-
-	return false
-}
-
-func min(a, b int) int {
-	if a < b {
-		return a
-	}
-	return b
-}
-
-func max(a, b int) int {
-	if a < b {
-		return b
-	}
-	return a
-}
-
-func (mss mapStringSet) Add(n string, v string) {
-	_, ok := mss[n]
-	if !ok {
-		mss[n] = make(stringSet)
-	}
-	mss[n].set(v)
-}
-
-func lessRunes(iRunes, jRunes []rune) bool {
-	max := len(iRunes)
-	if max > len(jRunes) {
-		max = len(jRunes)
-	}
-
-	for idx := 0; idx < max; idx++ {
-		ir := iRunes[idx]
-		jr := jRunes[idx]
-
-		lir := unicode.ToLower(ir)
-		ljr := unicode.ToLower(jr)
-
-		if lir != ljr {
-			return lir < ljr
-		}
-
-		// the lowercase runes are the same, so compare the original
-		if ir != jr {
-			return ir < jr
-		}
-	}
-
-	return len(iRunes) < len(jRunes)
-}
-
 func stringSliceEqual(a, b []string) bool {
 	if a == nil && b == nil {
 		return true
@@ -129,36 +49,3 @@ func removeInvalidTargets(targets []string) []string {
 
 	return filteredTargets
 }
-
-type MultiError struct {
-	Errors []error
-	mux    sync.Mutex
-}
-
-func (err *MultiError) Error() string {
-	str := ""
-
-	for _, e := range err.Errors {
-		str += e.Error() + "\n"
-	}
-
-	return str[:len(str)-1]
-}
-
-func (err *MultiError) Add(e error) {
-	if e == nil {
-		return
-	}
-
-	err.mux.Lock()
-	err.Errors = append(err.Errors, e)
-	err.mux.Unlock()
-}
-
-func (err *MultiError) Return() error {
-	if len(err.Errors) > 0 {
-		return err
-	}
-
-	return nil
-}

+ 2 - 1
vcs.go

@@ -9,6 +9,7 @@ import (
 	"sync"
 	"time"
 
+	"github.com/Jguer/yay/v9/pkg/types"
 	gosrc "github.com/Morganamilo/go-srcinfo"
 )
 
@@ -37,7 +38,7 @@ func createDevelDB() error {
 	}
 
 	bases := getBases(info)
-	toSkip := pkgbuildsToSkip(bases, sliceToStringSet(remoteNames))
+	toSkip := pkgbuildsToSkip(bases, types.SliceToStringSet(remoteNames))
 	downloadPkgbuilds(bases, toSkip, config.BuildDir)
 	srcinfos, _ := parseSrcinfoFiles(bases, false)