Files
aptly/debian/list.go
T
2014-01-09 13:12:32 +04:00

286 lines
6.3 KiB
Go

package debian
import (
"bytes"
"fmt"
"github.com/ugorji/go/codec"
"sort"
)
// PackageList is list of unique (by key) packages
//
// It could be seen as repo snapshot, repo contents, result of filtering,
// merge, etc.
type PackageList struct {
packages map[string]*Package
}
// NewPackageList creates empty package list
func NewPackageList() *PackageList {
return &PackageList{packages: make(map[string]*Package, 1000)}
}
// Add appends package to package list, additionally checking for uniqueness
func (l *PackageList) Add(p *Package) error {
key := string(p.Key())
existing, ok := l.packages[key]
if ok {
if !existing.Equals(p) {
return fmt.Errorf("conflict in package %s: %#v != %#v", p, existing, p)
}
return nil
}
l.packages[key] = p
return nil
}
// ForEach calls handler for each package in list
func (l *PackageList) ForEach(handler func(*Package) error) error {
var err error
for _, p := range l.packages {
err = handler(p)
if err != nil {
return err
}
}
return err
}
// Len returns number of packages in the list
func (l *PackageList) Len() int {
return len(l.packages)
}
// Dependency options
const (
// DepFollowSource pulls source packages when required
DepFollowSource = 1 << iota
// DepFollowSuggests pulls from suggests
DepFollowSuggests
// DepFollowRecommends pulls from recommends
DepFollowRecommends
// DepFollowAllVariants follows all variants if depends on "a | b"
DepFollowAllVariants
)
// VerifyDependencies looks for missing dependencies in package list.
//
// Analysis would be peformed for each architecture, in specified sources
func (l *PackageList) VerifyDependencies(options int, architectures []string, sources *PackageIndexedList) ([]Dependency, error) {
missing := make([]Dependency, 0, 128)
for _, arch := range architectures {
cache := make(map[string]bool, 2048)
for _, p := range l.packages {
if !p.MatchesArchitecture(arch) {
continue
}
for _, dep := range p.GetDependencies(options) {
dep, err := parseDependency(dep)
if err != nil {
return nil, fmt.Errorf("unable to process package %s: %s", p, err)
}
dep.Architecture = arch
hash := dep.Hash()
_, ok := cache[hash]
if ok {
continue
}
if sources.Search(dep) == nil {
missing = append(missing, dep)
cache[hash] = false
} else {
cache[hash] = true
}
}
}
}
return missing, nil
}
// PackageIndexedList is a list of packages optimized for satisfying searches
type PackageIndexedList struct {
// List of packages, sorted by name internally
packages []*Package
// Map of packages for each virtual package
providesList map[string][]*Package
}
// Verify interface
var (
_ sort.Interface = &PackageIndexedList{}
)
// NewPackageIndexedList creates empty PackageIndexedList
func NewPackageIndexedList() *PackageIndexedList {
return &PackageIndexedList{
packages: make([]*Package, 0, 1024),
}
}
// Len returns number of refs
func (l *PackageIndexedList) Len() int {
return len(l.packages)
}
// Swap swaps two refs
func (l *PackageIndexedList) Swap(i, j int) {
l.packages[i], l.packages[j] = l.packages[j], l.packages[i]
}
// Compare compares two refs in lexographical order
func (l *PackageIndexedList) Less(i, j int) bool {
return l.packages[i].Name < l.packages[j].Name
}
// PrepareIndex prepares list for indexing
func (l *PackageIndexedList) PrepareIndex() {
sort.Sort(l)
l.providesList = make(map[string][]*Package, 128)
for _, p := range l.packages {
if p.Provides != "" {
l.providesList[p.Provides] = append(l.providesList[p.Provides], p)
}
}
}
// Append adds more packages to the index
func (l *PackageIndexedList) Append(pl *PackageList) {
pp := make([]*Package, pl.Len())
i := 0
for _, p := range pl.packages {
pp[i] = p
i++
}
l.packages = append(l.packages, pp...)
}
// Search searches package index for specified package
func (l *PackageIndexedList) Search(dep Dependency) *Package {
if dep.Relation == VersionDontCare {
for _, p := range l.providesList[dep.Pkg] {
if p.MatchesArchitecture(dep.Architecture) {
return p
}
}
}
i := sort.Search(len(l.packages), func(j int) bool { return l.packages[j].Name >= dep.Pkg })
for i < len(l.packages) && l.packages[i].Name == dep.Pkg {
p := l.packages[i]
if p.MatchesArchitecture(dep.Architecture) {
if dep.Relation == VersionDontCare {
return p
}
r := CompareVersions(p.Version, dep.Version)
switch dep.Relation {
case VersionEqual:
if r == 0 {
return p
}
case VersionLess:
if r < 0 {
return p
}
case VersionGreater:
if r > 0 {
return p
}
case VersionLessOrEqual:
if r <= 0 {
return p
}
case VersionGreaterOrEqual:
if r >= 0 {
return p
}
}
}
i++
}
return nil
}
// PackageRefList is a list of keys of packages, this is basis for snapshot
// and similar stuff
//
// Refs are sorted in lexicographical order
type PackageRefList struct {
// List of package keys
Refs [][]byte
}
// Verify interface
var (
_ sort.Interface = &PackageRefList{}
)
// NewPackageRefListFromPackageList creates PackageRefList from PackageList
func NewPackageRefListFromPackageList(list *PackageList) *PackageRefList {
reflist := &PackageRefList{}
reflist.Refs = make([][]byte, list.Len())
i := 0
for _, p := range list.packages {
reflist.Refs[i] = p.Key()
i++
}
sort.Sort(reflist)
return reflist
}
// Len returns number of refs
func (l *PackageRefList) Len() int {
return len(l.Refs)
}
// Swap swaps two refs
func (l *PackageRefList) Swap(i, j int) {
l.Refs[i], l.Refs[j] = l.Refs[j], l.Refs[i]
}
// Compare compares two refs in lexographical order
func (l *PackageRefList) Less(i, j int) bool {
return bytes.Compare(l.Refs[i], l.Refs[j]) < 0
}
// Encode does msgpack encoding of PackageRefList
func (l *PackageRefList) Encode() []byte {
var buf bytes.Buffer
encoder := codec.NewEncoder(&buf, &codec.MsgpackHandle{})
encoder.Encode(l)
return buf.Bytes()
}
// Decode decodes msgpack representation into PackageRefLit
func (l *PackageRefList) Decode(input []byte) error {
decoder := codec.NewDecoderBytes(input, &codec.MsgpackHandle{})
return decoder.Decode(l)
}
// ForEach calls handler for each package ref in list
func (l *PackageRefList) ForEach(handler func([]byte) error) error {
var err error
for _, p := range l.Refs {
err = handler(p)
if err != nil {
return err
}
}
return err
}