109 lines
3.1 KiB
Go
109 lines
3.1 KiB
Go
package cli
|
|
|
|
import (
|
|
"context"
|
|
"sort"
|
|
|
|
"github.com/Khan/genqlient/graphql"
|
|
"github.com/Masterminds/semver/v3"
|
|
"github.com/pkg/errors"
|
|
"github.com/satisfactorymodding/ficsit-cli/ficsit"
|
|
)
|
|
|
|
type DependencyResolver struct {
|
|
apiClient graphql.Client
|
|
}
|
|
|
|
func NewDependencyResolver(apiClient graphql.Client) DependencyResolver {
|
|
return DependencyResolver{apiClient: apiClient}
|
|
}
|
|
|
|
func (d DependencyResolver) ResolveModDependencies(constraints map[string]string) (map[string]ModVersion, error) {
|
|
results := make(map[string]ModVersion)
|
|
|
|
toResolve := constraints
|
|
|
|
for len(toResolve) > 0 {
|
|
converted := make([]ficsit.ModVersionConstraint, 0)
|
|
for id, constraint := range toResolve {
|
|
converted = append(converted, ficsit.ModVersionConstraint{
|
|
ModIdOrReference: id,
|
|
Version: constraint,
|
|
})
|
|
}
|
|
|
|
toResolve = make(map[string]string)
|
|
|
|
dependencies, err := ficsit.ResolveModDependencies(context.TODO(), d.apiClient, converted)
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "failed resolving mod dependencies")
|
|
}
|
|
|
|
for _, mod := range dependencies.Mods {
|
|
modVersions := make([]ModVersion, len(mod.Versions))
|
|
for i, version := range mod.Versions {
|
|
versionDependencies := make([]VersionDependency, len(version.Dependencies))
|
|
|
|
for j, dependency := range version.Dependencies {
|
|
versionDependencies[j] = VersionDependency{
|
|
ModReference: dependency.Mod_id,
|
|
Constraint: dependency.Condition,
|
|
Optional: dependency.Optional,
|
|
}
|
|
}
|
|
|
|
modVersions[i] = ModVersion{
|
|
ID: version.Id,
|
|
Version: version.Version,
|
|
Dependencies: versionDependencies,
|
|
}
|
|
}
|
|
|
|
sort.Slice(modVersions, func(i, j int) bool {
|
|
a := semver.MustParse(modVersions[i].Version)
|
|
b := semver.MustParse(modVersions[j].Version)
|
|
return b.LessThan(a)
|
|
})
|
|
|
|
// Pick latest version
|
|
selectedVersion := modVersions[0]
|
|
|
|
if _, ok := results[mod.Mod_reference]; ok {
|
|
if results[mod.Mod_reference].Version != selectedVersion.Version {
|
|
return nil, errors.New("failed resolving dependencies. requires different versions of " + mod.Mod_reference)
|
|
}
|
|
}
|
|
|
|
results[mod.Mod_reference] = selectedVersion
|
|
|
|
for _, dependency := range selectedVersion.Dependencies {
|
|
if previousSelectedVersion, ok := results[dependency.ModReference]; ok {
|
|
constraint, _ := semver.NewConstraint(dependency.Constraint)
|
|
if !constraint.Check(semver.MustParse(previousSelectedVersion.Version)) {
|
|
return nil, errors.Errorf("mod %s version %s does not match constraint %s",
|
|
dependency.ModReference,
|
|
previousSelectedVersion.Version,
|
|
dependency.Constraint,
|
|
)
|
|
}
|
|
}
|
|
|
|
// TODO If already exists, verify which constraint is newer and use that
|
|
toResolve[dependency.ModReference] = dependency.Constraint
|
|
}
|
|
}
|
|
|
|
for _, constraint := range converted {
|
|
// Ignore SML
|
|
if constraint.ModIdOrReference == "SML" {
|
|
continue
|
|
}
|
|
|
|
if _, ok := results[constraint.ModIdOrReference]; !ok {
|
|
return nil, errors.New("failed resolving dependency: " + constraint.ModIdOrReference)
|
|
}
|
|
}
|
|
}
|
|
|
|
return results, nil
|
|
}
|