From 14a456f006d0dad0de0678d773a1c963cc67d075 Mon Sep 17 00:00:00 2001 From: stephen mcquay Date: Sat, 3 Mar 2018 22:22:50 -0800 Subject: [PATCH] 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. --- available.go | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++ meta.go | 3 ++ 2 files changed, 96 insertions(+) diff --git a/available.go b/available.go index a34d8ae..6cd400e 100644 --- a/available.go +++ b/available.go @@ -1,8 +1,10 @@ package pm import ( + "fmt" "net/url" "sort" + "strings" "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) 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 // that can be installed. 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. func (a Available) Add(m Meta) error { if _, err := m.Valid(); err != nil { @@ -92,3 +136,52 @@ func (a Available) Traverse() <-chan Meta { }() 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 +} diff --git a/meta.go b/meta.go index a4d9ace..1ba591a 100644 --- a/meta.go +++ b/meta.go @@ -27,3 +27,6 @@ func (m *Meta) Valid() (bool, error) { } return true, nil } + +// Metas is a slice of Meta +type Metas []Meta