From bf6d6b0850c03257fcfed4d7702bcfe28be991d1 Mon Sep 17 00:00:00 2001 From: mircearoata Date: Wed, 2 Oct 2024 10:59:56 +0200 Subject: [PATCH] feat: store API responses in a local registry for offline mode (#68) --- cli/cache/integrity.go | 118 -------- cli/cache/{cache.go => mod_details.go} | 81 ++--- cli/context.go | 8 +- cli/localregistry/registry.go | 197 ++++++++++++ cli/provider/ficsit.go | 7 +- cli/provider/local.go | 42 +-- ficsit/api_test.go | 9 - ficsit/queries/mod_versions.graphql | 13 - .../mod_versions_with_dependencies.graphql | 24 -- ficsit/types.go | 285 ------------------ go.mod | 27 +- go.sum | 64 +++- 12 files changed, 334 insertions(+), 541 deletions(-) delete mode 100644 cli/cache/integrity.go rename cli/cache/{cache.go => mod_details.go} (64%) create mode 100644 cli/localregistry/registry.go delete mode 100644 ficsit/queries/mod_versions.graphql delete mode 100644 ficsit/queries/mod_versions_with_dependencies.graphql 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 35395ee..3d00a62 100644 --- a/cli/context.go +++ b/cli/context.go @@ -8,6 +8,7 @@ import ( "github.com/spf13/viper" "github.com/satisfactorymodding/ficsit-cli/cli/cache" + "github.com/satisfactorymodding/ficsit-cli/cli/localregistry" "github.com/satisfactorymodding/ficsit-cli/cli/provider" "github.com/satisfactorymodding/ficsit-cli/ficsit" ) @@ -45,11 +46,16 @@ 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) } + err = localregistry.Init() + if err != nil { + return nil, fmt.Errorf("failed to initialize local registry: %w", err) + } + globalContext = &GlobalContext{ Installations: installations, Profiles: profiles, diff --git a/cli/localregistry/registry.go b/cli/localregistry/registry.go new file mode 100644 index 0000000..d170d43 --- /dev/null +++ b/cli/localregistry/registry.go @@ -0,0 +1,197 @@ +package localregistry + +import ( + "database/sql" + "fmt" + "log/slog" + "os" + "path/filepath" + "sync" + + resolver "github.com/satisfactorymodding/ficsit-resolver" + "github.com/spf13/viper" + + // sqlite driver + _ "modernc.org/sqlite" +) + +var db *sql.DB +var dbWriteMutex = sync.Mutex{} + +func Init() error { + dbPath := filepath.Join(viper.GetString("cache-dir"), "registry.db") + + err := os.MkdirAll(filepath.Dir(dbPath), 0o777) + if err != nil { + return fmt.Errorf("failed to create local registry directory: %w", err) + } + + db, err = sql.Open("sqlite", dbPath) + if err != nil { + return fmt.Errorf("failed to open database: %w", err) + } + + // Set pragmas here because modernc.org/sqlite does not support them in the connection string + _, err = db.Exec(` + PRAGMA journal_mode = WAL; + PRAGMA foreign_keys = ON; + PRAGMA busy_timeout = 5000; + + CREATE TABLE IF NOT EXISTS "versions" ( + "id" TEXT NOT NULL PRIMARY KEY, + "mod_reference" TEXT NOT NULL, + "version" TEXT NOT NULL, + "game_version" TEXT NOT NULL + ); + CREATE INDEX IF NOT EXISTS "mod_reference" ON "versions" ("mod_reference"); + CREATE UNIQUE INDEX IF NOT EXISTS "mod_version" ON "versions" ("mod_reference", "version"); + + CREATE TABLE IF NOT EXISTS "dependencies" ( + "version_id" TEXT NOT NULL, + "dependency" TEXT NOT NULL, + "condition" TEXT NOT NULL, + "optional" INT NOT NULL, + FOREIGN KEY ("version_id") REFERENCES "versions" ("id") ON DELETE CASCADE, + PRIMARY KEY ("version_id", "dependency") + ); + + CREATE TABLE IF NOT EXISTS "targets" ( + "version_id" TEXT NOT NULL, + "target_name" TEXT NOT NULL, + "link" TEXT NOT NULL, + "hash" TEXT NOT NULL, + "size" INT NOT NULL, + FOREIGN KEY ("version_id") REFERENCES "versions" ("id") ON DELETE CASCADE, + PRIMARY KEY ("version_id", "target_name") + ); + `) + if err != nil { + return fmt.Errorf("failed to setup tables: %w", err) + } + + return nil +} + +func Add(modReference string, modVersions []resolver.ModVersion) { + dbWriteMutex.Lock() + defer dbWriteMutex.Unlock() + + tx, err := db.Begin() + if err != nil { + slog.Error("failed to start local registry transaction", slog.Any("err", err)) + return + } + // In case the transaction is not committed, revert and release + defer tx.Rollback() //nolint:errcheck + + _, err = tx.Exec("DELETE FROM versions WHERE mod_reference = ?", modReference) + if err != nil { + slog.Error("failed to delete existing mod versions from local registry", slog.Any("err", err)) + return + } + + for _, modVersion := range modVersions { + l := slog.With(slog.String("mod", modReference), slog.String("version", modVersion.Version)) + + _, err = tx.Exec("INSERT INTO versions (id, mod_reference, version, game_version) VALUES (?, ?, ?, ?)", modVersion.ID, modReference, modVersion.Version, modVersion.GameVersion) + if err != nil { + l.Error("failed to insert mod version into local registry", slog.Any("err", err)) + return + } + for _, dependency := range modVersion.Dependencies { + _, err = tx.Exec("INSERT INTO dependencies (version_id, dependency, condition, optional) VALUES (?, ?, ?, ?)", modVersion.ID, dependency.ModID, dependency.Condition, dependency.Optional) + if err != nil { + l.Error("failed to insert dependency into local registry", slog.String("dependency", dependency.ModID), slog.Any("err", err)) + return + } + } + for _, target := range modVersion.Targets { + _, err = tx.Exec("INSERT INTO targets (version_id, target_name, link, hash, size) VALUES (?, ?, ?, ?, ?)", modVersion.ID, target.TargetName, target.Link, target.Hash, target.Size) + if err != nil { + l.Error("failed to insert target into local registry", slog.Any("target", target.TargetName), slog.Any("err", err)) + return + } + } + } + + err = tx.Commit() + if err != nil { + slog.Error("failed to commit local registry transaction", slog.Any("err", err)) + return + } +} + +func GetModVersions(modReference string) ([]resolver.ModVersion, error) { + versionRows, err := db.Query("SELECT id, version, game_version FROM versions WHERE mod_reference = ?", modReference) + if err != nil { + return nil, fmt.Errorf("failed to fetch mod versions from local registry: %w", err) + } + defer versionRows.Close() + + var versions []resolver.ModVersion + for versionRows.Next() { + var version resolver.ModVersion + err = versionRows.Scan(&version.ID, &version.Version, &version.GameVersion) + if err != nil { + return nil, fmt.Errorf("failed to scan version row: %w", err) + } + + dependencies, err := getVersionDependencies(version.ID) + if err != nil { + return nil, err + } + + version.Dependencies = dependencies + + targets, err := getVersionTargets(version.ID) + if err != nil { + return nil, err + } + + version.Targets = targets + + versions = append(versions, version) + } + + return versions, nil +} + +func getVersionDependencies(versionID string) ([]resolver.Dependency, error) { + var dependencies []resolver.Dependency + dependencyRows, err := db.Query("SELECT dependency, condition, optional FROM dependencies WHERE version_id = ?", versionID) + if err != nil { + return nil, fmt.Errorf("failed to fetch dependencies from local registry: %w", err) + } + defer dependencyRows.Close() + + for dependencyRows.Next() { + var dependency resolver.Dependency + err = dependencyRows.Scan(&dependency.ModID, &dependency.Condition, &dependency.Optional) + if err != nil { + return nil, fmt.Errorf("failed to scan dependency row: %w", err) + } + dependencies = append(dependencies, dependency) + } + + return dependencies, nil +} + +func getVersionTargets(versionID string) ([]resolver.Target, error) { + var targets []resolver.Target + targetRows, err := db.Query("SELECT target_name, link, hash, size FROM targets WHERE version_id = ?", versionID) + if err != nil { + return nil, fmt.Errorf("failed to fetch targets from local registry: %w", err) + } + defer targetRows.Close() + + for targetRows.Next() { + var target resolver.Target + err = targetRows.Scan(&target.TargetName, &target.Link, &target.Hash, &target.Size) + if err != nil { + return nil, fmt.Errorf("failed to scan target row: %w", err) + } + targets = append(targets, target) + } + + return targets, nil +} diff --git a/cli/provider/ficsit.go b/cli/provider/ficsit.go index 24147f7..9c8a8cc 100644 --- a/cli/provider/ficsit.go +++ b/cli/provider/ficsit.go @@ -8,6 +8,7 @@ import ( resolver "github.com/satisfactorymodding/ficsit-resolver" "github.com/spf13/viper" + "github.com/satisfactorymodding/ficsit-cli/cli/localregistry" "github.com/satisfactorymodding/ficsit-cli/ficsit" ) @@ -29,10 +30,6 @@ func (p FicsitProvider) GetMod(context context.Context, modReference string) (*f return ficsit.GetMod(context, p.client, modReference) } -func (p FicsitProvider) ModVersions(context context.Context, modReference string, filter ficsit.VersionFilter) (*ficsit.ModVersionsResponse, error) { - return ficsit.ModVersions(context, p.client, modReference, filter) -} - func (p FicsitProvider) ModVersionsWithDependencies(_ context.Context, modID string) ([]resolver.ModVersion, error) { response, err := ficsit.GetAllModVersions(modID) if err != nil { @@ -73,6 +70,8 @@ func (p FicsitProvider) ModVersionsWithDependencies(_ context.Context, modID str } } + localregistry.Add(modID, modVersions) + return modVersions, err } diff --git a/cli/provider/local.go b/cli/provider/local.go index 35058b7..b5eabc9 100644 --- a/cli/provider/local.go +++ b/cli/provider/local.go @@ -2,7 +2,6 @@ package provider import ( "context" - "errors" "fmt" "strings" "time" @@ -10,6 +9,7 @@ import ( resolver "github.com/satisfactorymodding/ficsit-resolver" "github.com/satisfactorymodding/ficsit-cli/cli/cache" + "github.com/satisfactorymodding/ficsit-cli/cli/localregistry" "github.com/satisfactorymodding/ficsit-cli/ficsit" ) @@ -20,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 @@ -45,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(), @@ -88,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{ @@ -111,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, @@ -124,37 +120,25 @@ func (p LocalProvider) GetMod(_ context.Context, modReference string) (*ficsit.G } func (p LocalProvider) ModVersionsWithDependencies(_ context.Context, modID string) ([]resolver.ModVersion, error) { - cachedModFiles, err := cache.GetCacheMod(modID) + modVersions, err := localregistry.GetModVersions(modID) if err != nil { - return nil, fmt.Errorf("failed to get cache: %w", err) + return nil, fmt.Errorf("failed to get local mod versions: %w", err) } - versions := make([]resolver.ModVersion, 0) + // TODO: only list as available the versions that have at least one target cached - for _, modFile := range cachedModFiles { - versions = append(versions, resolver.ModVersion{ - ID: modID + ":" + modFile.Plugin.SemVersion, - Version: modFile.Plugin.SemVersion, - GameVersion: modFile.Plugin.GameVersion, - }) - } - - return versions, nil + return modVersions, nil } 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/ficsit/api_test.go b/ficsit/api_test.go index 332e08b..6d0fe0b 100644 --- a/ficsit/api_test.go +++ b/ficsit/api_test.go @@ -17,15 +17,6 @@ func init() { client = InitAPI() } -func TestModVersions(t *testing.T) { - response, err := ModVersions(context.Background(), client, "SmartFoundations", VersionFilter{}) - testza.AssertNoError(t, err) - testza.AssertNotNil(t, response) - testza.AssertNotNil(t, response.Mod) - testza.AssertNotNil(t, response.Mod.Versions) - testza.AssertNotZero(t, len(response.Mod.Versions)) -} - func TestMods(t *testing.T) { response, err := Mods(context.Background(), client, ModFilter{}) testza.AssertNoError(t, err) diff --git a/ficsit/queries/mod_versions.graphql b/ficsit/queries/mod_versions.graphql deleted file mode 100644 index 4b0dde9..0000000 --- a/ficsit/queries/mod_versions.graphql +++ /dev/null @@ -1,13 +0,0 @@ -# @genqlient(omitempty: true) -query ModVersions ( - $modId: String!, - $filter: VersionFilter -) { - mod: getModByIdOrReference(modIdOrReference: $modId) { - id - versions (filter: $filter) { - id - version - } - } -} \ No newline at end of file diff --git a/ficsit/queries/mod_versions_with_dependencies.graphql b/ficsit/queries/mod_versions_with_dependencies.graphql deleted file mode 100644 index 34ef967..0000000 --- a/ficsit/queries/mod_versions_with_dependencies.graphql +++ /dev/null @@ -1,24 +0,0 @@ -# @genqlient(omitempty: true) -query ModVersionsWithDependencies ( - $modId: String!, -) { - mod: getModByIdOrReference(modIdOrReference: $modId) { - id - versions (filter: { limit: 100 }) { - id - version - link - hash - dependencies { - mod_id - condition - optional - } - targets { - targetName - link - hash - } - } - } -} \ No newline at end of file diff --git a/ficsit/types.go b/ficsit/types.go index d2af747..55be747 100644 --- a/ficsit/types.go +++ b/ficsit/types.go @@ -355,136 +355,6 @@ func (v *ModFilter) GetHidden() bool { return v.Hidden } // GetTagIDs returns ModFilter.TagIDs, and is useful for accessing the field via an interface. func (v *ModFilter) GetTagIDs() []string { return v.TagIDs } -// ModVersionsMod includes the requested fields of the GraphQL type Mod. -type ModVersionsMod struct { - Id string `json:"id"` - Versions []ModVersionsModVersionsVersion `json:"versions"` -} - -// GetId returns ModVersionsMod.Id, and is useful for accessing the field via an interface. -func (v *ModVersionsMod) GetId() string { return v.Id } - -// GetVersions returns ModVersionsMod.Versions, and is useful for accessing the field via an interface. -func (v *ModVersionsMod) GetVersions() []ModVersionsModVersionsVersion { return v.Versions } - -// ModVersionsModVersionsVersion includes the requested fields of the GraphQL type Version. -type ModVersionsModVersionsVersion struct { - Id string `json:"id"` - Version string `json:"version"` -} - -// GetId returns ModVersionsModVersionsVersion.Id, and is useful for accessing the field via an interface. -func (v *ModVersionsModVersionsVersion) GetId() string { return v.Id } - -// GetVersion returns ModVersionsModVersionsVersion.Version, and is useful for accessing the field via an interface. -func (v *ModVersionsModVersionsVersion) GetVersion() string { return v.Version } - -// ModVersionsResponse is returned by ModVersions on success. -type ModVersionsResponse struct { - Mod ModVersionsMod `json:"mod"` -} - -// GetMod returns ModVersionsResponse.Mod, and is useful for accessing the field via an interface. -func (v *ModVersionsResponse) GetMod() ModVersionsMod { return v.Mod } - -// ModVersionsWithDependenciesMod includes the requested fields of the GraphQL type Mod. -type ModVersionsWithDependenciesMod struct { - Id string `json:"id"` - Versions []ModVersionsWithDependenciesModVersionsVersion `json:"versions"` -} - -// GetId returns ModVersionsWithDependenciesMod.Id, and is useful for accessing the field via an interface. -func (v *ModVersionsWithDependenciesMod) GetId() string { return v.Id } - -// GetVersions returns ModVersionsWithDependenciesMod.Versions, and is useful for accessing the field via an interface. -func (v *ModVersionsWithDependenciesMod) GetVersions() []ModVersionsWithDependenciesModVersionsVersion { - return v.Versions -} - -// ModVersionsWithDependenciesModVersionsVersion includes the requested fields of the GraphQL type Version. -type ModVersionsWithDependenciesModVersionsVersion struct { - Id string `json:"id"` - Version string `json:"version"` - Link string `json:"link"` - Hash string `json:"hash"` - Dependencies []ModVersionsWithDependenciesModVersionsVersionDependenciesVersionDependency `json:"dependencies"` - Targets []ModVersionsWithDependenciesModVersionsVersionTargetsVersionTarget `json:"targets"` -} - -// GetId returns ModVersionsWithDependenciesModVersionsVersion.Id, and is useful for accessing the field via an interface. -func (v *ModVersionsWithDependenciesModVersionsVersion) GetId() string { return v.Id } - -// GetVersion returns ModVersionsWithDependenciesModVersionsVersion.Version, and is useful for accessing the field via an interface. -func (v *ModVersionsWithDependenciesModVersionsVersion) GetVersion() string { return v.Version } - -// GetLink returns ModVersionsWithDependenciesModVersionsVersion.Link, and is useful for accessing the field via an interface. -func (v *ModVersionsWithDependenciesModVersionsVersion) GetLink() string { return v.Link } - -// GetHash returns ModVersionsWithDependenciesModVersionsVersion.Hash, and is useful for accessing the field via an interface. -func (v *ModVersionsWithDependenciesModVersionsVersion) GetHash() string { return v.Hash } - -// GetDependencies returns ModVersionsWithDependenciesModVersionsVersion.Dependencies, and is useful for accessing the field via an interface. -func (v *ModVersionsWithDependenciesModVersionsVersion) GetDependencies() []ModVersionsWithDependenciesModVersionsVersionDependenciesVersionDependency { - return v.Dependencies -} - -// GetTargets returns ModVersionsWithDependenciesModVersionsVersion.Targets, and is useful for accessing the field via an interface. -func (v *ModVersionsWithDependenciesModVersionsVersion) GetTargets() []ModVersionsWithDependenciesModVersionsVersionTargetsVersionTarget { - return v.Targets -} - -// ModVersionsWithDependenciesModVersionsVersionDependenciesVersionDependency includes the requested fields of the GraphQL type VersionDependency. -type ModVersionsWithDependenciesModVersionsVersionDependenciesVersionDependency struct { - Mod_id string `json:"mod_id"` - Condition string `json:"condition"` - Optional bool `json:"optional"` -} - -// GetMod_id returns ModVersionsWithDependenciesModVersionsVersionDependenciesVersionDependency.Mod_id, and is useful for accessing the field via an interface. -func (v *ModVersionsWithDependenciesModVersionsVersionDependenciesVersionDependency) GetMod_id() string { - return v.Mod_id -} - -// GetCondition returns ModVersionsWithDependenciesModVersionsVersionDependenciesVersionDependency.Condition, and is useful for accessing the field via an interface. -func (v *ModVersionsWithDependenciesModVersionsVersionDependenciesVersionDependency) GetCondition() string { - return v.Condition -} - -// GetOptional returns ModVersionsWithDependenciesModVersionsVersionDependenciesVersionDependency.Optional, and is useful for accessing the field via an interface. -func (v *ModVersionsWithDependenciesModVersionsVersionDependenciesVersionDependency) GetOptional() bool { - return v.Optional -} - -// ModVersionsWithDependenciesModVersionsVersionTargetsVersionTarget includes the requested fields of the GraphQL type VersionTarget. -type ModVersionsWithDependenciesModVersionsVersionTargetsVersionTarget struct { - TargetName TargetName `json:"targetName"` - Link string `json:"link"` - Hash string `json:"hash"` -} - -// GetTargetName returns ModVersionsWithDependenciesModVersionsVersionTargetsVersionTarget.TargetName, and is useful for accessing the field via an interface. -func (v *ModVersionsWithDependenciesModVersionsVersionTargetsVersionTarget) GetTargetName() TargetName { - return v.TargetName -} - -// GetLink returns ModVersionsWithDependenciesModVersionsVersionTargetsVersionTarget.Link, and is useful for accessing the field via an interface. -func (v *ModVersionsWithDependenciesModVersionsVersionTargetsVersionTarget) GetLink() string { - return v.Link -} - -// GetHash returns ModVersionsWithDependenciesModVersionsVersionTargetsVersionTarget.Hash, and is useful for accessing the field via an interface. -func (v *ModVersionsWithDependenciesModVersionsVersionTargetsVersionTarget) GetHash() string { - return v.Hash -} - -// ModVersionsWithDependenciesResponse is returned by ModVersionsWithDependencies on success. -type ModVersionsWithDependenciesResponse struct { - Mod ModVersionsWithDependenciesMod `json:"mod"` -} - -// GetMod returns ModVersionsWithDependenciesResponse.Mod, and is useful for accessing the field via an interface. -func (v *ModVersionsWithDependenciesResponse) GetMod() ModVersionsWithDependenciesMod { return v.Mod } - // ModsModsGetMods includes the requested fields of the GraphQL type GetMods. type ModsModsGetMods struct { Count int `json:"count"` @@ -675,49 +545,6 @@ const ( OrderDesc Order = "desc" ) -type TargetName string - -const ( - TargetNameWindows TargetName = "Windows" - TargetNameWindowsserver TargetName = "WindowsServer" - TargetNameLinuxserver TargetName = "LinuxServer" -) - -type VersionFields string - -const ( - VersionFieldsCreatedAt VersionFields = "created_at" - VersionFieldsUpdatedAt VersionFields = "updated_at" - VersionFieldsDownloads VersionFields = "downloads" -) - -type VersionFilter struct { - Limit int `json:"limit,omitempty"` - Offset int `json:"offset,omitempty"` - Order_by VersionFields `json:"order_by,omitempty"` - Order Order `json:"order,omitempty"` - Search string `json:"search,omitempty"` - Ids []string `json:"ids,omitempty"` -} - -// GetLimit returns VersionFilter.Limit, and is useful for accessing the field via an interface. -func (v *VersionFilter) GetLimit() int { return v.Limit } - -// GetOffset returns VersionFilter.Offset, and is useful for accessing the field via an interface. -func (v *VersionFilter) GetOffset() int { return v.Offset } - -// GetOrder_by returns VersionFilter.Order_by, and is useful for accessing the field via an interface. -func (v *VersionFilter) GetOrder_by() VersionFields { return v.Order_by } - -// GetOrder returns VersionFilter.Order, and is useful for accessing the field via an interface. -func (v *VersionFilter) GetOrder() Order { return v.Order } - -// GetSearch returns VersionFilter.Search, and is useful for accessing the field via an interface. -func (v *VersionFilter) GetSearch() string { return v.Search } - -// GetIds returns VersionFilter.Ids, and is useful for accessing the field via an interface. -func (v *VersionFilter) GetIds() []string { return v.Ids } - // VersionMod includes the requested fields of the GraphQL type Mod. type VersionMod struct { Id string `json:"id"` @@ -818,26 +645,6 @@ type __GetModNameInput struct { // GetModId returns __GetModNameInput.ModId, and is useful for accessing the field via an interface. func (v *__GetModNameInput) GetModId() string { return v.ModId } -// __ModVersionsInput is used internally by genqlient -type __ModVersionsInput struct { - ModId string `json:"modId,omitempty"` - Filter VersionFilter `json:"filter,omitempty"` -} - -// GetModId returns __ModVersionsInput.ModId, and is useful for accessing the field via an interface. -func (v *__ModVersionsInput) GetModId() string { return v.ModId } - -// GetFilter returns __ModVersionsInput.Filter, and is useful for accessing the field via an interface. -func (v *__ModVersionsInput) GetFilter() VersionFilter { return v.Filter } - -// __ModVersionsWithDependenciesInput is used internally by genqlient -type __ModVersionsWithDependenciesInput struct { - ModId string `json:"modId,omitempty"` -} - -// GetModId returns __ModVersionsWithDependenciesInput.ModId, and is useful for accessing the field via an interface. -func (v *__ModVersionsWithDependenciesInput) GetModId() string { return v.ModId } - // __ModsInput is used internally by genqlient type __ModsInput struct { Filter ModFilter `json:"filter,omitempty"` @@ -1063,98 +870,6 @@ func GetModName( return &data, err } -// The query or mutation executed by ModVersions. -const ModVersions_Operation = ` -query ModVersions ($modId: String!, $filter: VersionFilter) { - mod: getModByIdOrReference(modIdOrReference: $modId) { - id - versions(filter: $filter) { - id - version - } - } -} -` - -func ModVersions( - ctx context.Context, - client graphql.Client, - modId string, - filter VersionFilter, -) (*ModVersionsResponse, error) { - req := &graphql.Request{ - OpName: "ModVersions", - Query: ModVersions_Operation, - Variables: &__ModVersionsInput{ - ModId: modId, - Filter: filter, - }, - } - var err error - - var data ModVersionsResponse - resp := &graphql.Response{Data: &data} - - err = client.MakeRequest( - ctx, - req, - resp, - ) - - return &data, err -} - -// The query or mutation executed by ModVersionsWithDependencies. -const ModVersionsWithDependencies_Operation = ` -query ModVersionsWithDependencies ($modId: String!) { - mod: getModByIdOrReference(modIdOrReference: $modId) { - id - versions(filter: {limit:100}) { - id - version - link - hash - dependencies { - mod_id - condition - optional - } - targets { - targetName - link - hash - } - } - } -} -` - -func ModVersionsWithDependencies( - ctx context.Context, - client graphql.Client, - modId string, -) (*ModVersionsWithDependenciesResponse, error) { - req := &graphql.Request{ - OpName: "ModVersionsWithDependencies", - Query: ModVersionsWithDependencies_Operation, - Variables: &__ModVersionsWithDependenciesInput{ - ModId: modId, - }, - } - var err error - - var data ModVersionsWithDependenciesResponse - resp := &graphql.Response{Data: &data} - - err = client.MakeRequest( - ctx, - req, - resp, - ) - - return &data, err -} - // The query or mutation executed by Mods. const Mods_Operation = ` query Mods ($filter: ModFilter) { diff --git a/go.mod b/go.mod index 5da4534..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 @@ -29,8 +30,9 @@ require ( github.com/spf13/cobra v1.8.0 github.com/spf13/viper v1.18.1 goftp.io/server/v2 v2.0.1 - golang.org/x/crypto v0.16.0 - golang.org/x/sync v0.5.0 + golang.org/x/crypto v0.21.0 + golang.org/x/sync v0.6.0 + modernc.org/sqlite v1.32.0 ) require ( @@ -54,10 +56,12 @@ require ( github.com/dlclark/regexp2 v1.10.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/google/go-cmp v0.6.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/gookit/color v1.5.4 // indirect github.com/gorilla/css v1.0.1 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/klauspost/cpuid/v2 v2.2.6 // indirect @@ -69,14 +73,15 @@ 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 github.com/muesli/termenv v0.15.2 // indirect + github.com/ncruces/go-strftime v0.1.9 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect @@ -94,13 +99,19 @@ require ( github.com/yuin/goldmark-emoji v1.0.2 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb // indirect - golang.org/x/mod v0.14.0 // indirect - golang.org/x/net v0.19.0 // indirect - golang.org/x/sys v0.15.0 // indirect - golang.org/x/term v0.15.0 // indirect + golang.org/x/mod v0.16.0 // indirect + golang.org/x/net v0.22.0 // indirect + golang.org/x/sys v0.22.0 // indirect + golang.org/x/term v0.18.0 // indirect golang.org/x/text v0.14.0 // indirect - golang.org/x/tools v0.16.0 // indirect + golang.org/x/tools v0.19.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect + modernc.org/libc v1.55.3 // indirect + modernc.org/mathutil v1.6.0 // indirect + modernc.org/memory v1.8.0 // indirect + modernc.org/strutil v1.2.0 // indirect + modernc.org/token v1.1.0 // indirect ) diff --git a/go.sum b/go.sum index f31afc1..384ba54 100644 --- a/go.sum +++ b/go.sum @@ -86,6 +86,10 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo= +github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ= github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo= github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= @@ -99,6 +103,8 @@ github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= @@ -164,6 +170,8 @@ github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKt github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc= github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= +github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= +github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= @@ -187,6 +195,8 @@ github.com/pterm/pterm v0.12.71 h1:KcEJ98EiVCbzDkFbktJ2gMlr4pn8IzyGb9bwK6ffkuA= github.com/pterm/pterm v0.12.71/go.mod h1:SUAcoZjRt+yjPWlWba+/Fd8zJJ2lSXBQWf0Z0HbFiIQ= github.com/puzpuzpuz/xsync/v3 v3.0.2 h1:3yESHrRFYr6xzkz61LLkvNiPFXxJEAABanTQpKbAaew= github.com/puzpuzpuz/xsync/v3 v3.0.2/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= @@ -267,14 +277,14 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= -golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= -golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb h1:c0vyKkb6yr3KR7jEfJaOSv4lG7xPkbN6r52aJz1d8a8= golang.org/x/exp v0.0.0-20231206192017-f3f8817b8deb/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= -golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic= +golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= @@ -290,13 +300,13 @@ golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= -golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= -golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -318,8 +328,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -329,8 +339,8 @@ golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww= -golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= -golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -346,8 +356,8 @@ golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM= -golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= +golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= +golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= @@ -363,3 +373,29 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ= +modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ= +modernc.org/ccgo/v4 v4.19.2 h1:lwQZgvboKD0jBwdaeVCTouxhxAyN6iawF3STraAal8Y= +modernc.org/ccgo/v4 v4.19.2/go.mod h1:ysS3mxiMV38XGRTTcgo0DQTeTmAO4oCmJl1nX9VFI3s= +modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= +modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= +modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw= +modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU= +modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI= +modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4= +modernc.org/libc v1.55.3 h1:AzcW1mhlPNrRtjS5sS+eW2ISCgSOLLNyFzRh/V3Qj/U= +modernc.org/libc v1.55.3/go.mod h1:qFXepLhz+JjFThQ4kzwzOjA/y/artDeg+pcYnY+Q83w= +modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= +modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= +modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= +modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU= +modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= +modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc= +modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss= +modernc.org/sqlite v1.32.0 h1:6BM4uGza7bWypsw4fdLRsLxut6bHe4c58VeqjRgST8s= +modernc.org/sqlite v1.32.0/go.mod h1:UqoylwmTb9F+IqXERT8bW9zzOWN8qwAIcLdzeBZs4hA= +modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= +modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=