Fix some printfs, clean regexes, switch to fuzzy matching for search

This commit is contained in:
comp500 2019-11-01 15:46:50 +00:00
parent 33c93f3ac3
commit 5ba9ff5c73
8 changed files with 97 additions and 71 deletions

View File

@ -65,7 +65,7 @@ var initCmd = &cobra.Command{
mcVersions, err := getValidMCVersions()
if err != nil {
fmt.Printf("Failed to get latest minecraft versions: %s", err)
fmt.Printf("Failed to get latest minecraft versions: %s\n", err)
os.Exit(1)
}

View File

@ -62,7 +62,7 @@ var removeCmd = &cobra.Command{
os.Exit(1)
}
fmt.Printf("Mod %s removed successfully!", args[0])
fmt.Printf("Mod %s removed successfully!\n", args[0])
},
}

View File

@ -47,7 +47,7 @@ var updateCmd = &cobra.Command{
for _, v := range index.GetAllMods() {
modData, err := core.LoadMod(v)
if err != nil {
fmt.Printf("Error reading mod file: %s", err.Error())
fmt.Printf("Error reading mod file: %s\n", err.Error())
continue
}
@ -65,7 +65,7 @@ var updateCmd = &cobra.Command{
updaterMap[k] = append(slice, modData)
}
if !updaterFound {
fmt.Printf("A supported update system for \"%s\" cannot be found.", modData.Name)
fmt.Printf("A supported update system for \"%s\" cannot be found.\n", modData.Name)
}
}

View File

@ -23,9 +23,9 @@ func init() {
}
var fileIDRegexes = [...]*regexp.Regexp{
regexp.MustCompile("^https?:\\/\\/minecraft\\.curseforge\\.com\\/projects\\/(.+)\\/files\\/(\\d+)"),
regexp.MustCompile("^https?:\\/\\/(?:www\\.)?curseforge\\.com\\/minecraft\\/mc-mods\\/(.+)\\/files\\/(\\d+)"),
regexp.MustCompile("^https?:\\/\\/(?:www\\.)?curseforge\\.com\\/minecraft\\/mc-mods\\/(.+)\\/download\\/(\\d+)"),
regexp.MustCompile("^https?://minecraft\\.curseforge\\.com/projects/(.+)/files/(\\d+)"),
regexp.MustCompile("^https?://(?:www\\.)?curseforge\\.com/minecraft/mc-mods/(.+)/files/(\\d+)"),
regexp.MustCompile("^https?://(?:www\\.)?curseforge\\.com/minecraft/mc-mods/(.+)/download/(\\d+)"),
}
func getFileIDsFromString(mod string) (bool, int, int, error) {
@ -44,8 +44,8 @@ func getFileIDsFromString(mod string) (bool, int, int, error) {
}
var modSlugRegexes = [...]*regexp.Regexp{
regexp.MustCompile("^https?:\\/\\/minecraft\\.curseforge\\.com\\/projects\\/([^\\/]+)"),
regexp.MustCompile("^https?:\\/\\/(?:www\\.)?curseforge\\.com\\/minecraft\\/mc-mods\\/([^\\/]+)"),
regexp.MustCompile("^https?://minecraft\\.curseforge\\.com/projects/([^/]+)"),
regexp.MustCompile("^https?://(?:www\\.)?curseforge\\.com/minecraft/mc-mods/([^/]+)"),
// Exact slug matcher
regexp.MustCompile("^[a-z][\\da-z\\-_]{0,127}$"),
}

View File

@ -4,11 +4,10 @@ import (
"bufio"
"errors"
"fmt"
"github.com/sahilm/fuzzy"
"os"
"sort"
"strings"
"github.com/agnivade/levenshtein"
"github.com/comp500/packwiz/core"
"github.com/spf13/cobra"
"gopkg.in/dixonwille/wmenu.v4"
@ -71,63 +70,14 @@ var installCmd = &cobra.Command{
var modInfoData modInfo
if !done {
fmt.Println("Searching CurseForge...")
searchTerm := strings.Join(args, " ")
results, err := getSearch(searchTerm, mcVersion)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
if len(results) == 0 {
fmt.Println("No mods found!")
os.Exit(1)
} else if len(results) == 1 {
modInfoData = results[0]
modID = modInfoData.ID
modInfoObtained = true
done = true
} else {
// Find the closest value to the search term
sort.Slice(results, func(i, j int) bool {
return levenshtein.ComputeDistance(searchTerm, results[i].Name) < levenshtein.ComputeDistance(searchTerm, results[j].Name)
})
menu := wmenu.NewMenu("Choose a number:")
for i, v := range results {
menu.Option(v.Name, v, i == 0, nil)
}
menu.Option("Cancel", nil, false, nil)
menu.Action(func(menuRes []wmenu.Opt) error {
if len(menuRes) != 1 || menuRes[0].Value == nil {
fmt.Println("Cancelled!")
return nil
}
// Why is variable shadowing a thing!!!!
var ok bool
modInfoData, ok = menuRes[0].Value.(modInfo)
if !ok {
fmt.Println("Error converting interface from wmenu")
os.Exit(1)
}
modID = modInfoData.ID
modInfoObtained = true
done = true
return nil
})
err = menu.Run()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
if !done {
return
}
var cancelled bool
cancelled, modInfoData = searchCurseforgeInternal(args, mcVersion)
if cancelled {
return
}
done = true
modID = modInfoData.ID
modInfoObtained = true
}
if !done {
@ -220,6 +170,7 @@ var installCmd = &cobra.Command{
depFileInfo, err := getLatestFile(currData, mcVersion, 0)
if err != nil {
fmt.Printf("Error retrieving dependency data: %s\n", err.Error())
continue
}
for _, dep := range depFileInfo.Dependencies {
@ -296,6 +247,79 @@ var installCmd = &cobra.Command{
},
}
// Used to implement interface for fuzzy matching
type modResultsList []modInfo
func (r modResultsList) String(i int) string {
return r[i].Name
}
func (r modResultsList) Len() int {
return len(r)
}
func searchCurseforgeInternal(args []string, mcVersion string) (bool, modInfo) {
fmt.Println("Searching CurseForge...")
searchTerm := strings.Join(args, " ")
results, err := getSearch(searchTerm, mcVersion)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
if len(results) == 0 {
fmt.Println("No mods found!")
os.Exit(1)
} else if len(results) == 1 {
return false, results[0]
} else {
// Fuzzy search on results list
fuzzySearchResults := fuzzy.FindFrom(searchTerm, modResultsList(results))
menu := wmenu.NewMenu("Choose a number:")
if len(fuzzySearchResults) == 0 {
for i, v := range results {
menu.Option(v.Name, v, i == 0, nil)
}
} else {
for i, v := range fuzzySearchResults {
menu.Option(results[v.Index].Name, results[v.Index], i == 0, nil)
}
}
menu.Option("Cancel", nil, false, nil)
var modInfoData modInfo
var cancelled bool
menu.Action(func(menuRes []wmenu.Opt) error {
if len(menuRes) != 1 || menuRes[0].Value == nil {
fmt.Println("Cancelled!")
cancelled = true
return nil
}
// Why is variable shadowing a thing!!!!
var ok bool
modInfoData, ok = menuRes[0].Value.(modInfo)
if !ok {
return errors.New("error converting interface from wmenu")
}
return nil
})
err = menu.Run()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
if cancelled {
return true, modInfo{}
}
return false, modInfoData
}
// This should never be executed, but Go requires it!
return false, modInfo{}
}
func getLatestFile(modInfoData modInfo, mcVersion string, fileID int) (modFileInfo, error) {
if fileID == 0 {
// TODO: change to timestamp-based comparison??

View File

@ -256,7 +256,6 @@ func getFileInfo(modID int, fileID int) (modFileInfo, error) {
return infoRes, nil
}
// TODO: pass gameVersion?
func getSearch(searchText string, gameVersion string) ([]modInfo, error) {
var infoRes []modInfo
client := &http.Client{}

3
go.mod
View File

@ -2,7 +2,6 @@ module github.com/comp500/packwiz
require (
github.com/BurntSushi/toml v0.3.1
github.com/agnivade/levenshtein v1.0.2
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect
github.com/daviddengcn/go-colortext v0.0.0-20180409174941-186a3d44e920 // indirect
github.com/denormal/go-gitignore v0.0.0-20180930084346-ae8ad1d07817
@ -11,8 +10,10 @@ require (
github.com/golangplus/fmt v0.0.0-20150411045040-2a5d6d7d2995 // indirect
github.com/golangplus/testing v0.0.0-20180327235837-af21d9c3145e // indirect
github.com/igorsobreira/titlecase v0.0.0-20140109233139-4156b5b858ac
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/mattn/go-isatty v0.0.4 // indirect
github.com/mitchellh/mapstructure v1.1.2
github.com/sahilm/fuzzy v0.1.0
github.com/skratchdot/open-golang v0.0.0-20190402232053-79abb63cd66e
github.com/spf13/cobra v0.0.5
github.com/spf13/viper v1.4.0

6
go.sum
View File

@ -4,8 +4,6 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/VividCortex/ewma v1.1.1 h1:MnEK4VOv6n0RSY4vtRe3h11qjxL3+t0B8yOL8iMXdcM=
github.com/VividCortex/ewma v1.1.1/go.mod h1:2Tkkvm3sRDVXaiyucHiACn4cqf7DpdyLvmxzcbUokwA=
github.com/agnivade/levenshtein v1.0.2 h1:xKF7WlEzoa+ZVkzBxy0ukdzI2etYiWGlTPMNTBGncKI=
github.com/agnivade/levenshtein v1.0.2/go.mod h1:JLvzGblJATanj48SD0YhHTEFGkWvw3ASLFWSiMIFXsE=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
@ -76,6 +74,8 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
@ -104,6 +104,8 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI=
github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/skratchdot/open-golang v0.0.0-20190402232053-79abb63cd66e h1:VAzdS5Nw68fbf5RZ8RDVlUvPXNU6Z3jtPCK/qvm4FoQ=
github.com/skratchdot/open-golang v0.0.0-20190402232053-79abb63cd66e/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=