Adds EXTRAORDINARILY Installable implementation

We want the simplest satisfiability solver we can have, but one that
chases transitive dependencies. This one only checks the requested
packages exist in the availability database.
This commit is contained in:
Stephen McQuay 2018-03-03 22:22:50 -08:00
parent b869bd250c
commit 14a456f006
Signed by: sm
GPG Key ID: 4E4B72F479BA3CE5
2 changed files with 96 additions and 0 deletions

View File

@ -1,8 +1,10 @@
package pm package pm
import ( import (
"fmt"
"net/url" "net/url"
"sort" "sort"
"strings"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -28,10 +30,52 @@ func (v Versions) Len() int { return len(v) }
func (v Versions) Swap(a, b int) { v[a], v[b] = v[b], v[a] } func (v Versions) Swap(a, b int) { v[a], v[b] = v[b], v[a] }
func (v Versions) Less(a, b int) bool { return v[a] < v[b] } func (v Versions) Less(a, b int) bool { return v[a] < v[b] }
type label struct {
n Name
v Version
}
type labels []label
// TODO (sm): make this semver sort?
func (n labels) Len() int { return len(n) }
func (n labels) Swap(a, b int) { n[a], n[b] = n[b], n[a] }
func (n labels) Less(a, b int) bool {
if n[a].n != n[b].n {
return n[a].n < n[b].n
}
return n[a].v < n[b].v
}
// Available is the structure used to represent the collection of all packages // Available is the structure used to represent the collection of all packages
// that can be installed. // that can be installed.
type Available map[Name]map[Version]Meta type Available map[Name]map[Version]Meta
// Get returns the meta stored at a[n][v] or an error explaining why it could
// not be Get.
func (a Available) Get(n Name, v Version) (Meta, error) {
if _, ok := a[n]; !ok {
return Meta{}, errors.Errorf("could not find package named %q", n)
}
if v == "" {
if len(a[n]) == 0 {
return Meta{}, errors.Errorf("no configured versions for %q", n)
}
vers := Versions{}
for ver := range a[n] {
vers = append(vers, ver)
}
sort.Sort(vers)
v = vers[len(vers)-1]
}
if _, ok := a[n][v]; !ok {
return Meta{}, errors.Errorf("could not find %v@%v in database", n, v)
}
return a[n][v], nil
}
// Add inserts m into a. // Add inserts m into a.
func (a Available) Add(m Meta) error { func (a Available) Add(m Meta) error {
if _, err := m.Valid(); err != nil { if _, err := m.Valid(); err != nil {
@ -92,3 +136,52 @@ func (a Available) Traverse() <-chan Meta {
}() }()
return r return r
} }
func labelForString(s string) (label, error) {
r := label{}
c := strings.Count(s, "@")
switch c {
case 0:
r.n = Name(s)
case 1:
sp := strings.Split(s, "@")
r.n, r.v = Name(sp[0]), Version(sp[1])
default:
return r, fmt.Errorf("unexpected number of '@' found, got %v, want 1", c)
}
if r.n == "" {
return r, fmt.Errorf("name cannot be empty")
}
return r, nil
}
// Installable calculates if the packages requested in "in" can be installed.
func (a Available) Installable(in []string) (Metas, error) {
ls := labels{}
for _, i := range in {
l, err := labelForString(i)
if err != nil {
return nil, errors.Wrap(err, "parsing name/version")
}
ls = append(ls, l)
}
seen := map[Name]bool{}
for _, l := range ls {
if _, ok := seen[l.n]; ok {
return nil, fmt.Errorf("can only ask to install %q once", l.n)
}
seen[l.n] = true
}
ms := Metas{}
for _, l := range ls {
m, err := a.Get(l.n, l.v)
if err != nil {
return ms, errors.Wrapf(err, "getting %v", l)
}
ms = append(ms, m)
}
return ms, nil
}

View File

@ -27,3 +27,6 @@ func (m *Meta) Valid() (bool, error) {
} }
return true, nil return true, nil
} }
// Metas is a slice of Meta
type Metas []Meta