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

@@ -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{}