aptly/debian/package.go

196 lines
4.9 KiB
Go

package debian
import (
"bytes"
"fmt"
"github.com/smira/aptly/database"
"github.com/smira/aptly/utils"
"github.com/ugorji/go/codec"
"os"
"strconv"
"strings"
)
// Package is single instance of Debian package
//
// TODO: support source & binary
type Package struct {
Name string
Version string
Filename string
Filesize int64
Architecture string
Source string
// Various dependencies
Depends []string
PreDepends []string
Suggests []string
Recommends []string
// Hashsums of package contents
HashMD5 string
HashSHA1 string
HashSHA256 string
// Extra information from stanza
Extra Stanza
}
func parseDependencies(input Stanza, key string) []string {
value, ok := input[key]
if !ok {
return nil
}
delete(input, key)
return strings.Split(value, ", ")
}
// NewPackageFromControlFile creates Package from parsed Debian control file
func NewPackageFromControlFile(input Stanza) *Package {
result := &Package{
Name: input["Package"],
Version: input["Version"],
Filename: input["Filename"],
Architecture: input["Architecture"],
Source: input["Source"],
HashMD5: input["MD5sum"],
HashSHA1: input["SHA1"],
HashSHA256: input["SHA256"],
}
delete(input, "Package")
delete(input, "Version")
delete(input, "Filename")
delete(input, "Architecture")
delete(input, "Source")
delete(input, "MD5sum")
delete(input, "SHA1")
delete(input, "SHA256")
result.Filesize, _ = strconv.ParseInt(input["Size"], 10, 64)
delete(input, "Size")
result.Depends = parseDependencies(input, "Depends")
result.PreDepends = parseDependencies(input, "Pre-Depends")
result.Suggests = parseDependencies(input, "Suggests")
result.Recommends = parseDependencies(input, "Recommends")
result.Extra = input
return result
}
// Key returns unique key identifying package
func (p *Package) Key() []byte {
return []byte("P" + p.Name + " " + p.Version + " " + p.Architecture)
}
// Encode does msgpack encoding of Package
func (p *Package) Encode() []byte {
var buf bytes.Buffer
encoder := codec.NewEncoder(&buf, &codec.MsgpackHandle{})
encoder.Encode(p)
return buf.Bytes()
}
// Decode decodes msgpack representation into Package
func (p *Package) Decode(input []byte) error {
decoder := codec.NewDecoderBytes(input, &codec.MsgpackHandle{})
return decoder.Decode(p)
}
// String creates readable representation
func (p *Package) String() string {
return fmt.Sprintf("%s-%s_%s", p.Name, p.Version, p.Architecture)
}
// Stanza creates original stanza from package
func (p *Package) Stanza() (result Stanza) {
result = p.Extra.Copy()
result["Package"] = p.Name
result["Version"] = p.Version
result["Filename"] = p.Filename
result["Architecture"] = p.Architecture
result["Source"] = p.Source
if p.HashMD5 != "" {
result["MD5sum"] = p.HashMD5
}
if p.HashSHA1 != "" {
result["SHA1"] = p.HashSHA1
}
if p.HashSHA256 != "" {
result["SHA256"] = p.HashSHA256
}
if p.Depends != nil {
result["Depends"] = strings.Join(p.Depends, ", ")
}
if p.PreDepends != nil {
result["Pre-Depends"] = strings.Join(p.PreDepends, ", ")
}
if p.Suggests != nil {
result["Suggests"] = strings.Join(p.Suggests, ", ")
}
if p.Recommends != nil {
result["Recommends"] = strings.Join(p.Recommends, ", ")
}
result["Size"] = fmt.Sprintf("%d", p.Filesize)
return
}
// Equals compares two packages to be identical
func (p *Package) Equals(p2 *Package) bool {
return p.Name == p2.Name && p.Version == p2.Version && p.Filename == p2.Filename &&
p.Architecture == p2.Architecture && utils.StrSlicesEqual(p.Depends, p2.Depends) &&
utils.StrSlicesEqual(p.PreDepends, p2.PreDepends) && utils.StrSlicesEqual(p.Suggests, p2.Suggests) &&
utils.StrSlicesEqual(p.Recommends, p2.Recommends) && utils.StrMapsEqual(p.Extra, p2.Extra) &&
p.Filesize == p2.Filesize && p.HashMD5 == p2.HashMD5 && p.HashSHA1 == p2.HashSHA1 &&
p.HashSHA256 == p2.HashSHA256 && p.Source == p2.Source
}
// VerifyFile verifies integrity and existence of local files for the package
func (p *Package) VerifyFile(filepath string) bool {
st, err := os.Stat(filepath)
if err != nil {
return false
}
return st.Size() == p.Filesize
}
// PackageCollection does management of packages in DB
type PackageCollection struct {
db database.Storage
}
// NewPackageCollection creates new PackageCollection and binds it to database
func NewPackageCollection(db database.Storage) *PackageCollection {
return &PackageCollection{
db: db,
}
}
// ByKey find package in DB by its key
func (collection *PackageCollection) ByKey(key []byte) (*Package, error) {
encoded, err := collection.db.Get(key)
if err != nil {
return nil, err
}
p := &Package{}
err = p.Decode(encoded)
if err != nil {
return nil, err
}
return p, nil
}
// Update adds or updates information about package in DB
func (collection *PackageCollection) Update(p *Package) error {
return collection.db.Put(p.Key(), p.Encode())
}