Redo update system to work with batched updates

This commit is contained in:
comp500 2019-06-16 14:25:54 +01:00
parent adcde05693
commit b77e2080c7
No known key found for this signature in database
GPG Key ID: 214C822FFEC586B5
4 changed files with 76 additions and 44 deletions

View File

@ -7,8 +7,8 @@ import (
"os"
"path/filepath"
"sort"
"time"
"strings"
"time"
"github.com/BurntSushi/toml"
"github.com/vbauerster/mpb/v4"
@ -192,6 +192,7 @@ func (in *Index) Refresh() error {
}
start := time.Now()
defer func() {
// TODO: this is stupid, traverse the file tree first *then* read all the files
if progressCurrent >= progressLength {
progressLength++
progress.SetTotal(int64(progressLength), false)
@ -272,7 +273,7 @@ func (in *Index) RefreshFileWithHash(path, format, hash string, mod bool) error
func (in Index) FindMod(modName string) (string, bool) {
for _, v := range in.Files {
if v.MetaFile {
_, file := filepath.Split(v.File);
_, file := filepath.Split(v.File)
fileTrimmed := strings.TrimSuffix(file, ModExtension)
if fileTrimmed == modName {
return v.File, true
@ -280,4 +281,4 @@ func (in Index) FindMod(modName string) (string, bool) {
}
}
return "", false
}
}

View File

@ -1,16 +1,43 @@
package core
// UpdateParsers stores all the update parsers that packwiz can use. Add your own update systems to this map.
var UpdateParsers = make(map[string]UpdateParser)
// UpdateParser takes an unparsed interface{} (as a map[string]interface{}), and returns an Updater for a mod file.
// This can be done using the mapstructure library or your own parsing methods.
type UpdateParser interface {
ParseUpdate(map[string]interface{}) (Updater, error)
}
// Updaters stores all the updaters that packwiz can use. Add your own update systems to this map, keyed by the configuration name.
var Updaters = make(map[string]Updater)
// Updater checks for and does updates on a mod
// Updater is used to process updates on mods
type Updater interface {
// DoUpdate returns true if an update was done, false otherwise
DoUpdate(Mod) (bool, error)
// ParseUpdate takes an unparsed interface{} (as a map[string]interface{}), and returns an Updater for a mod file.
// This can be done using the mapstructure library or your own parsing methods.
ParseUpdate(map[string]interface{}) (interface{}, error)
// CheckUpdate checks whether there is an update for each of the mods in the given slice,
// called for all of the mods that this updater handles
CheckUpdate([]Mod) ([]UpdateCheck, error)
// DoUpdate carries out the update previously queried in CheckUpdate, on each Mod's metadata,
// given pointers to Mods and the value of CachedState for each mod
DoUpdate([]*Mod, []interface{}) error
}
// UpdateCheck represents the data returned from CheckUpdate for each mod
type UpdateCheck struct {
// UpdateAvailable is true if an update is available for this mod
UpdateAvailable bool
// UpdateString is a string that details the update in some way to the user. Usually this will be in the form of
// a version change (1.0.0 -> 1.0.1), or a file name change (thanos-skin-1.0.0.jar -> thanos-skin-1.0.1.jar).
UpdateString string
// CachedState can be used to preserve per-mod state between CheckUpdate and DoUpdate (e.g. file metadata)
CachedState interface{}
// Error stores an error for this specific mod
// Errors can also be returned from CheckUpdate directly, if the whole operation failed completely (so only 1 error is printed)
// If an error is returned for a mod, or from CheckUpdate, DoUpdate is not called on that mod / at all
Error error
}
// TODO: new docs
// to carry out updating:
// go through all metafiles in index
// make a []Mod for each updater, so map[string][]Mod
// for each Mod, check the "first" updater, then give the Mod to the map
// go through the map, call CheckUpdate with the []Mod
// print to user, if interactive mode
// call doupdate with the mods and interfaces!!

View File

@ -19,8 +19,8 @@ type Mod struct {
Optional bool `toml:"optional,omitempty"`
Download ModDownload `toml:"download"`
// Update is a map of map of stuff, so you can store arbitrary values on string keys to define updating
Update map[string]map[string]interface{} `toml:"update"`
updaters map[string]Updater
Update map[string]map[string]interface{} `toml:"update"`
updateData map[string]interface{}
}
// ModDownload specifies how to download the mod file
@ -43,16 +43,16 @@ func LoadMod(modFile string) (Mod, error) {
if _, err := toml.DecodeFile(modFile, &mod); err != nil {
return Mod{}, err
}
mod.updaters = make(map[string]Updater)
// Horrible reflection library to convert to Updaters
mod.updateData = make(map[string]interface{})
// Horrible reflection library to convert map[string]interface to proper struct
for k, v := range mod.Update {
updateParser, ok := UpdateParsers[k]
updater, ok := Updaters[k]
if ok {
updater, err := updateParser.ParseUpdate(v)
updateData, err := updater.ParseUpdate(v)
if err != nil {
return mod, err
}
mod.updaters[k] = updater
mod.updateData[k] = updateData
} else {
return mod, errors.New("Update plugin " + k + " not found!")
}
@ -86,8 +86,8 @@ func (m Mod) Write() (string, string, error) {
return "sha256", hashString, err
}
// GetParsedUpdater can be used to retrieve updater-specific information after parsing a mod file
func (m Mod) GetParsedUpdater(updaterName string) (Updater, bool) {
upd, ok := m.updaters[updaterName]
// GetParsedUpdateData can be used to retrieve updater-specific information after parsing a mod file
func (m Mod) GetParsedUpdateData(updaterName string) (interface{}, bool) {
upd, ok := m.updateData[updaterName]
return upd, ok
}

View File

@ -38,7 +38,7 @@ func init() {
},
}},
})
core.UpdateParsers["curseforge"] = cfUpdateParser{}
core.Updaters["curseforge"] = cfUpdater{}
}
var fileIDRegexes = [...]*regexp.Regexp{
@ -100,7 +100,7 @@ func createModFile(flags core.Flags, modInfo modInfo, fileInfo modFileInfo, inde
updateMap := make(map[string]map[string]interface{})
var err error
updateMap["curseforge"], err = cfUpdater{
updateMap["curseforge"], err = cfUpdateData{
ProjectID: modInfo.ID,
FileID: fileInfo.ID,
// TODO: determine update channel
@ -162,11 +162,11 @@ func cmdDoc(flags core.Flags, mod string) error {
if err != nil {
return cli.NewExitError(err, 1)
}
updateData, ok := modData.GetParsedUpdater("curseforge")
updateData, ok := modData.GetParsedUpdateData("curseforge")
if !ok {
return cli.NewExitError("This mod doesn't seem to be a curseforge mod!", 1)
}
cfUpdateData := updateData.(cfUpdater)
cfUpdateData := updateData.(cfUpdateData)
fmt.Println("Opening browser...")
url := "https://minecraft.curseforge.com/projects/" + strconv.Itoa(cfUpdateData.ProjectID)
err = open.Start(url)
@ -178,32 +178,36 @@ func cmdDoc(flags core.Flags, mod string) error {
return nil
}
type cfUpdateParser struct{}
func (u cfUpdateParser) ParseUpdate(updateUnparsed map[string]interface{}) (core.Updater, error) {
var updater cfUpdater
err := mapstructure.Decode(updateUnparsed, &updater)
return updater, err
}
type cfUpdater struct {
type cfUpdateData struct {
ProjectID int `mapstructure:"project-id"`
FileID int `mapstructure:"file-id"`
ReleaseChannel string `mapstructure:"release-channel"`
}
func (u cfUpdater) DoUpdate(mod core.Mod) (bool, error) {
func (u cfUpdateData) ToMap() (map[string]interface{}, error) {
newMap := make(map[string]interface{})
err := mapstructure.Decode(u, &newMap)
return newMap, err
}
type cfUpdater struct{}
func (u cfUpdater) ParseUpdate(updateUnparsed map[string]interface{}) (interface{}, error) {
var updateData cfUpdateData
err := mapstructure.Decode(updateUnparsed, &updateData)
return updateData, err
}
func (u cfUpdater) CheckUpdate(mod []core.Mod) ([]core.UpdateCheck, error) {
return nil, nil
}
func (u cfUpdater) DoUpdate(mod []*core.Mod, cachedState []interface{}) error {
// TODO: implement updating
// modInfoData, err := getModInfo(u.ProjectID)
// if err != nil {
// return false, err
// }
return false, nil
}
func (u cfUpdater) ToMap() (map[string]interface{}, error) {
newMap := make(map[string]interface{})
err := mapstructure.Decode(u, &newMap)
return newMap, err
return nil
}