ficsit-cli-flake/cli/provider/local.go
mircearoata e4b02a792d
feat: offline mode (#14)
* chore: move mod downloading to cli/cache

* feat: data providers, ficsit and local

* feat: keep cache in memory, load on init

* feat: log invalid cache files instead of returning error

* chore: make linter happy

* feat: fill cached mod Authors field from CreatedBy

* chore: make linter happy again

* feat: add icon and size to cached mods

* feat: cache the cached file hashes

* fix: change to new provider access style

---------

Co-authored-by: Vilsol <me@vil.so>
2023-12-06 06:47:41 +02:00

289 lines
7.5 KiB
Go

package provider
import (
"context"
"strings"
"time"
"github.com/Masterminds/semver/v3"
"github.com/pkg/errors"
"github.com/satisfactorymodding/ficsit-cli/cli/cache"
"github.com/satisfactorymodding/ficsit-cli/ficsit"
)
type localProvider struct{}
func initLocalProvider() localProvider {
return localProvider{}
}
func (p localProvider) Mods(_ context.Context, filter ficsit.ModFilter) (*ficsit.ModsResponse, error) {
cachedMods, err := cache.GetCache()
if err != nil {
return nil, errors.Wrap(err, "failed to get cache")
}
mods := make([]ficsit.ModsModsGetModsModsMod, 0)
for modReference, files := range cachedMods {
if modReference == "SML" {
continue
}
if len(filter.References) > 0 {
skip := true
for _, a := range filter.References {
if a == modReference {
skip = false
break
}
}
if skip {
continue
}
}
mods = append(mods, ficsit.ModsModsGetModsModsMod{
Id: modReference,
Name: files[0].Plugin.FriendlyName,
Mod_reference: modReference,
Last_version_date: time.Now(),
Created_at: time.Now(),
Downloads: 0,
Popularity: 0,
Hotness: 0,
})
}
if filter.Limit == 0 {
filter.Limit = 25
}
low := filter.Offset
high := filter.Offset + filter.Limit
if low > len(mods) {
return &ficsit.ModsResponse{
Mods: ficsit.ModsModsGetMods{
Count: 0,
Mods: []ficsit.ModsModsGetModsModsMod{},
},
}, nil
}
if high > len(mods) {
high = len(mods)
}
mods = mods[low:high]
return &ficsit.ModsResponse{
Mods: ficsit.ModsModsGetMods{
Count: len(mods),
Mods: mods,
},
}, nil
}
func (p localProvider) GetMod(_ context.Context, modReference string) (*ficsit.GetModResponse, error) {
cachedModFiles, err := cache.GetCacheMod(modReference)
if err != nil {
return nil, errors.Wrap(err, "failed to get cache")
}
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, ",") {
authors = append(authors, ficsit.GetModModAuthorsUserMod{
Role: "Unknown",
User: ficsit.GetModModAuthorsUserModUser{
Username: author,
},
})
}
return &ficsit.GetModResponse{
Mod: ficsit.GetModMod{
Id: modReference,
Name: cachedModFiles[0].Plugin.FriendlyName,
Mod_reference: modReference,
Created_at: time.Now(),
Views: 0,
Downloads: 0,
Authors: authors,
Full_description: "",
Source_url: "",
},
}, nil
}
func (p localProvider) ModVersions(_ context.Context, modReference string, filter ficsit.VersionFilter) (*ficsit.ModVersionsResponse, error) {
cachedModFiles, err := cache.GetCacheMod(modReference)
if err != nil {
return nil, errors.Wrap(err, "failed to get cache")
}
if filter.Limit == 0 {
filter.Limit = 25
}
versions := make([]ficsit.ModVersionsModVersionsVersion, 0)
for _, modFile := range cachedModFiles[filter.Offset : filter.Offset+filter.Limit] {
versions = append(versions, ficsit.ModVersionsModVersionsVersion{
Id: modReference + ":" + modFile.Plugin.SemVersion,
Version: modFile.Plugin.SemVersion,
})
}
return &ficsit.ModVersionsResponse{
Mod: ficsit.ModVersionsMod{
Id: modReference,
Versions: versions,
},
}, nil
}
func (p localProvider) SMLVersions(_ context.Context) (*ficsit.SMLVersionsResponse, error) {
cachedSMLFiles, err := cache.GetCacheMod("SML")
if err != nil {
return nil, errors.Wrap(err, "failed to get cache")
}
smlVersions := make([]ficsit.SMLVersionsSmlVersionsGetSMLVersionsSml_versionsSMLVersion, 0)
for _, smlFile := range cachedSMLFiles {
smlVersions = append(smlVersions, ficsit.SMLVersionsSmlVersionsGetSMLVersionsSml_versionsSMLVersion{
Id: "SML:" + smlFile.Plugin.SemVersion,
Version: smlFile.Plugin.SemVersion,
Satisfactory_version: 0, // TODO: where can this be obtained from?
})
}
return &ficsit.SMLVersionsResponse{
SmlVersions: ficsit.SMLVersionsSmlVersionsGetSMLVersions{
Count: len(smlVersions),
Sml_versions: smlVersions,
},
}, nil
}
func (p localProvider) ResolveModDependencies(_ context.Context, filter []ficsit.ModVersionConstraint) (*ficsit.ResolveModDependenciesResponse, error) {
cachedMods, err := cache.GetCache()
if err != nil {
return nil, errors.Wrap(err, "failed to get cache")
}
mods := make([]ficsit.ResolveModDependenciesModsModVersion, 0)
constraintMap := make(map[string]string)
for _, constraint := range filter {
constraintMap[constraint.ModIdOrReference] = constraint.Version
}
for modReference, modFiles := range cachedMods {
constraint, ok := constraintMap[modReference]
if !ok {
continue
}
semverConstraint, err := semver.NewConstraint(constraint)
if err != nil {
return nil, errors.Wrapf(err, "failed to parse constraint for %s", modReference)
}
versions := make([]ficsit.ResolveModDependenciesModsModVersionVersionsVersion, 0)
for _, modFile := range modFiles {
semverVersion, err := semver.NewVersion(modFile.Plugin.SemVersion)
if err != nil {
return nil, errors.Wrapf(err, "failed to parse version for %s", modReference)
}
if !semverConstraint.Check(semverVersion) {
continue
}
dependencies := make([]ficsit.ResolveModDependenciesModsModVersionVersionsVersionDependenciesVersionDependency, 0)
for _, dependency := range modFile.Plugin.Plugins {
if dependency.BasePlugin {
continue
}
dependencies = append(dependencies, ficsit.ResolveModDependenciesModsModVersionVersionsVersionDependenciesVersionDependency{
Mod_id: dependency.Name,
Condition: dependency.SemVersion,
Optional: dependency.Optional,
})
}
versions = append(versions, ficsit.ResolveModDependenciesModsModVersionVersionsVersion{
Id: modReference + ":" + modFile.Plugin.SemVersion,
Version: modFile.Plugin.SemVersion,
Link: "",
Hash: modFile.Hash,
Dependencies: dependencies,
})
}
mods = append(mods, ficsit.ResolveModDependenciesModsModVersion{
Id: modReference,
Mod_reference: modReference,
Versions: versions,
})
}
return &ficsit.ResolveModDependenciesResponse{
Mods: mods,
}, nil
}
func (p localProvider) ModVersionsWithDependencies(_ context.Context, modID string) (*ficsit.ModVersionsWithDependenciesResponse, error) {
cachedModFiles, err := cache.GetCacheMod(modID)
if err != nil {
return nil, errors.Wrap(err, "failed to get cache")
}
versions := make([]ficsit.ModVersionsWithDependenciesModVersionsVersion, 0)
for _, modFile := range cachedModFiles {
versions = append(versions, ficsit.ModVersionsWithDependenciesModVersionsVersion{
Id: modID + ":" + modFile.Plugin.SemVersion,
Version: modFile.Plugin.SemVersion,
})
}
return &ficsit.ModVersionsWithDependenciesResponse{
Mod: ficsit.ModVersionsWithDependenciesMod{
Id: modID,
Versions: versions,
},
}, nil
}
func (p localProvider) GetModName(_ context.Context, modReference string) (*ficsit.GetModNameResponse, error) {
cachedModFiles, err := cache.GetCacheMod(modReference)
if err != nil {
return nil, errors.Wrap(err, "failed to get cache")
}
if len(cachedModFiles) == 0 {
return nil, errors.New("mod not found")
}
return &ficsit.GetModNameResponse{
Mod: ficsit.GetModNameMod{
Id: modReference,
Name: cachedModFiles[0].Plugin.FriendlyName,
Mod_reference: modReference,
},
}, nil
}