From 382278f826afe48eecbb552683ae4e08b9916566 Mon Sep 17 00:00:00 2001 From: Mircea Roata Date: Thu, 15 Aug 2024 18:52:35 +0300 Subject: [PATCH] chore: remove .integrity file, cache only latest mod details --- cli/cache/integrity.go | 118 ------------------------- cli/cache/{cache.go => mod_details.go} | 81 +++++++++-------- cli/context.go | 2 +- cli/provider/local.go | 25 ++---- go.mod | 2 +- 5 files changed, 55 insertions(+), 173 deletions(-) delete mode 100644 cli/cache/integrity.go rename cli/cache/{cache.go => mod_details.go} (64%) diff --git a/cli/cache/integrity.go b/cli/cache/integrity.go deleted file mode 100644 index 42212c1..0000000 --- a/cli/cache/integrity.go +++ /dev/null @@ -1,118 +0,0 @@ -package cache - -import ( - "encoding/json" - "fmt" - "io" - "log/slog" - "os" - "path/filepath" - "time" - - "github.com/puzpuzpuz/xsync/v3" - "github.com/spf13/viper" - - "github.com/satisfactorymodding/ficsit-cli/utils" -) - -type hashInfo struct { - Modified time.Time - Hash string - Size int64 -} - -var hashCache *xsync.MapOf[string, hashInfo] - -var integrityFilename = ".integrity" - -func getFileHash(file string) (string, error) { - if hashCache == nil { - loadHashCache() - } - cachedHash, ok := hashCache.Load(file) - if !ok { - return cacheFileHash(file) - } - downloadCache := filepath.Join(viper.GetString("cache-dir"), "downloadCache") - stat, err := os.Stat(filepath.Join(downloadCache, file)) - if err != nil { - return "", fmt.Errorf("failed to stat file: %w", err) - } - if stat.Size() != cachedHash.Size || stat.ModTime() != cachedHash.Modified { - return cacheFileHash(file) - } - return cachedHash.Hash, nil -} - -func cacheFileHash(file string) (string, error) { - downloadCache := filepath.Join(viper.GetString("cache-dir"), "downloadCache") - stat, err := os.Stat(filepath.Join(downloadCache, file)) - if err != nil { - return "", fmt.Errorf("failed to stat file: %w", err) - } - f, err := os.Open(filepath.Join(downloadCache, file)) - if err != nil { - return "", fmt.Errorf("failed to open file: %w", err) - } - defer f.Close() - hash, err := utils.SHA256Data(f) - if err != nil { - return "", fmt.Errorf("failed to hash file: %w", err) - } - hashCache.Store(file, hashInfo{ - Hash: hash, - Size: stat.Size(), - Modified: stat.ModTime(), - }) - saveHashCache() - return hash, nil -} - -func loadHashCache() { - hashCache = xsync.NewMapOf[string, hashInfo]() - cacheFile := filepath.Join(viper.GetString("cache-dir"), "downloadCache", integrityFilename) - if _, err := os.Stat(cacheFile); os.IsNotExist(err) { - return - } - f, err := os.Open(cacheFile) - if err != nil { - slog.Warn("failed to open hash cache, recreating", slog.Any("err", err)) - return - } - defer f.Close() - - hashCacheJSON, err := io.ReadAll(f) - if err != nil { - slog.Warn("failed to read hash cache, recreating", slog.Any("err", err)) - return - } - - var plainCache map[string]hashInfo - if err := json.Unmarshal(hashCacheJSON, &plainCache); err != nil { - slog.Warn("failed to unmarshal hash cache, recreating", slog.Any("err", err)) - return - } - - for k, v := range plainCache { - hashCache.Store(k, v) - } -} - -func saveHashCache() { - cacheFile := filepath.Join(viper.GetString("cache-dir"), "downloadCache", integrityFilename) - plainCache := make(map[string]hashInfo, hashCache.Size()) - hashCache.Range(func(k string, v hashInfo) bool { - plainCache[k] = v - return true - }) - hashCacheJSON, err := json.Marshal(plainCache) - if err != nil { - slog.Warn("failed to marshal hash cache", slog.Any("err", err)) - return - } - - if err := os.WriteFile(cacheFile, hashCacheJSON, 0o755); err != nil { - slog.Warn("failed to write hash cache", slog.Any("err", err)) - return - } -} diff --git a/cli/cache/cache.go b/cli/cache/mod_details.go similarity index 64% rename from cli/cache/cache.go rename to cli/cache/mod_details.go index 9400662..ab104b6 100644 --- a/cli/cache/cache.go +++ b/cli/cache/mod_details.go @@ -12,43 +12,44 @@ import ( "path/filepath" "strings" + "github.com/mircearoata/pubgrub-go/pubgrub/semver" "github.com/puzpuzpuz/xsync/v3" "github.com/spf13/viper" ) const IconFilename = "Resources/Icon128.png" // This is the path UE expects for the icon -type File struct { - Icon *string - ModReference string - Hash string - Plugin UPlugin - Size int64 +type Mod struct { + ModReference string + Name string + Author string + Icon *string + LatestVersion string } -var loadedCache *xsync.MapOf[string, []File] +var loadedMods *xsync.MapOf[string, Mod] -func GetCache() (*xsync.MapOf[string, []File], error) { - if loadedCache != nil { - return loadedCache, nil +func GetCacheMods() (*xsync.MapOf[string, Mod], error) { + if loadedMods != nil { + return loadedMods, nil } - return LoadCache() + return LoadCacheMods() } -func GetCacheMod(mod string) ([]File, error) { - cache, err := GetCache() +func GetCacheMod(mod string) (Mod, error) { + cache, err := GetCacheMods() if err != nil { - return nil, err + return Mod{}, err } value, _ := cache.Load(mod) return value, nil } -func LoadCache() (*xsync.MapOf[string, []File], error) { - loadedCache = xsync.NewMapOf[string, []File]() +func LoadCacheMods() (*xsync.MapOf[string, Mod], error) { + loadedMods = xsync.NewMapOf[string, Mod]() downloadCache := filepath.Join(viper.GetString("cache-dir"), "downloadCache") if _, err := os.Stat(downloadCache); os.IsNotExist(err) { - return loadedCache, nil + return loadedMods, nil } items, err := os.ReadDir(downloadCache) @@ -60,32 +61,45 @@ func LoadCache() (*xsync.MapOf[string, []File], error) { if item.IsDir() { continue } - if item.Name() == integrityFilename { - continue - } _, err = addFileToCache(item.Name()) if err != nil { slog.Error("failed to add file to cache", slog.String("file", item.Name()), slog.Any("err", err)) } } - return loadedCache, nil + return loadedMods, nil } -func addFileToCache(filename string) (*File, error) { +func addFileToCache(filename string) (*Mod, error) { cacheFile, err := readCacheFile(filename) if err != nil { return nil, fmt.Errorf("failed to read cache file: %w", err) } - loadedCache.Compute(cacheFile.ModReference, func(oldValue []File, _ bool) ([]File, bool) { - return append(oldValue, *cacheFile), false + loadedMods.Compute(cacheFile.ModReference, func(oldValue Mod, loaded bool) (Mod, bool) { + if !loaded { + return *cacheFile, false + } + oldVersion, err := semver.NewVersion(oldValue.LatestVersion) + if err != nil { + slog.Error("failed to parse version", slog.String("version", oldValue.LatestVersion), slog.Any("err", err)) + return *cacheFile, false + } + newVersion, err := semver.NewVersion(cacheFile.LatestVersion) + if err != nil { + slog.Error("failed to parse version", slog.String("version", cacheFile.LatestVersion), slog.Any("err", err)) + return oldValue, false + } + if newVersion.Compare(oldVersion) > 0 { + return *cacheFile, false + } + return oldValue, false }) return cacheFile, nil } -func readCacheFile(filename string) (*File, error) { +func readCacheFile(filename string) (*Mod, error) { downloadCache := filepath.Join(viper.GetString("cache-dir"), "downloadCache") path := filepath.Join(downloadCache, filename) stat, err := os.Stat(path) @@ -132,11 +146,6 @@ func readCacheFile(filename string) (*File, error) { modReference := strings.TrimSuffix(upluginFile.Name, ".uplugin") - hash, err := getFileHash(filename) - if err != nil { - return nil, fmt.Errorf("failed to get file hash: %w", err) - } - var iconFile *zip.File for _, file := range reader.File { if file.Name == IconFilename { @@ -160,11 +169,11 @@ func readCacheFile(filename string) (*File, error) { icon = &iconData } - return &File{ - ModReference: modReference, - Hash: hash, - Size: size, - Icon: icon, - Plugin: uplugin, + return &Mod{ + ModReference: modReference, + Name: uplugin.FriendlyName, + Author: uplugin.CreatedBy, + Icon: icon, + LatestVersion: uplugin.SemVersion, }, nil } diff --git a/cli/context.go b/cli/context.go index c6687dd..3d00a62 100644 --- a/cli/context.go +++ b/cli/context.go @@ -46,7 +46,7 @@ func InitCLI(apiOnly bool) (*GlobalContext, error) { return nil, fmt.Errorf("failed to initialize installations: %w", err) } - _, err = cache.LoadCache() + _, err = cache.LoadCacheMods() if err != nil { return nil, fmt.Errorf("failed to load cache: %w", err) } diff --git a/cli/provider/local.go b/cli/provider/local.go index 57b1484..b5eabc9 100644 --- a/cli/provider/local.go +++ b/cli/provider/local.go @@ -2,7 +2,6 @@ package provider import ( "context" - "errors" "fmt" "strings" "time" @@ -21,14 +20,14 @@ func NewLocalProvider() LocalProvider { } func (p LocalProvider) Mods(_ context.Context, filter ficsit.ModFilter) (*ficsit.ModsResponse, error) { - cachedMods, err := cache.GetCache() + cachedMods, err := cache.GetCacheMods() if err != nil { return nil, fmt.Errorf("failed to get cache: %w", err) } mods := make([]ficsit.ModsModsGetModsModsMod, 0) - cachedMods.Range(func(modReference string, files []cache.File) bool { + cachedMods.Range(func(modReference string, cachedMod cache.Mod) bool { if len(filter.References) > 0 { skip := true @@ -46,7 +45,7 @@ func (p LocalProvider) Mods(_ context.Context, filter ficsit.ModFilter) (*ficsit mods = append(mods, ficsit.ModsModsGetModsModsMod{ Id: modReference, - Name: files[0].Plugin.FriendlyName, + Name: cachedMod.Name, Mod_reference: modReference, Last_version_date: time.Now(), Created_at: time.Now(), @@ -89,18 +88,14 @@ func (p LocalProvider) Mods(_ context.Context, filter ficsit.ModFilter) (*ficsit } func (p LocalProvider) GetMod(_ context.Context, modReference string) (*ficsit.GetModResponse, error) { - cachedModFiles, err := cache.GetCacheMod(modReference) + cachedMod, err := cache.GetCacheMod(modReference) if err != nil { return nil, fmt.Errorf("failed to get cache: %w", err) } - if len(cachedModFiles) == 0 { - return nil, errors.New("mod not found") - } - authors := make([]ficsit.GetModModAuthorsUserMod, 0) - for _, author := range strings.Split(cachedModFiles[0].Plugin.CreatedBy, ",") { + for _, author := range strings.Split(cachedMod.Author, ",") { authors = append(authors, ficsit.GetModModAuthorsUserMod{ Role: "Unknown", User: ficsit.GetModModAuthorsUserModUser{ @@ -112,7 +107,7 @@ func (p LocalProvider) GetMod(_ context.Context, modReference string) (*ficsit.G return &ficsit.GetModResponse{ Mod: ficsit.GetModMod{ Id: modReference, - Name: cachedModFiles[0].Plugin.FriendlyName, + Name: cachedMod.Name, Mod_reference: modReference, Created_at: time.Now(), Views: 0, @@ -136,18 +131,14 @@ func (p LocalProvider) ModVersionsWithDependencies(_ context.Context, modID stri } func (p LocalProvider) GetModName(_ context.Context, modReference string) (*resolver.ModName, error) { - cachedModFiles, err := cache.GetCacheMod(modReference) + cachedMod, err := cache.GetCacheMod(modReference) if err != nil { return nil, fmt.Errorf("failed to get cache: %w", err) } - if len(cachedModFiles) == 0 { - return nil, errors.New("mod not found") - } - return &resolver.ModName{ ID: modReference, - Name: cachedModFiles[0].Plugin.FriendlyName, + Name: cachedMod.Name, ModReference: modReference, }, nil } diff --git a/go.mod b/go.mod index 1c8fcd5..8f7f4de 100644 --- a/go.mod +++ b/go.mod @@ -19,6 +19,7 @@ require ( github.com/jackc/puddle/v2 v2.2.1 github.com/jlaffaye/ftp v0.2.0 github.com/lmittmann/tint v1.0.3 + github.com/mircearoata/pubgrub-go v0.3.3 github.com/muesli/reflow v0.3.0 github.com/pkg/sftp v1.13.6 github.com/pterm/pterm v0.12.71 @@ -72,7 +73,6 @@ require ( github.com/mattn/go-localereader v0.0.1 // indirect github.com/mattn/go-runewidth v0.0.15 // indirect github.com/microcosm-cc/bluemonday v1.0.26 // indirect - github.com/mircearoata/pubgrub-go v0.3.3 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect