mirror of
https://github.com/aptly-dev/aptly.git
synced 2026-05-07 22:20:24 +00:00
PkgQueries, concept of 'Searchable', rewrite Filter using PackageQueries.
This commit is contained in:
+14
-46
@@ -5,7 +5,6 @@ import (
|
||||
"github.com/smira/aptly/aptly"
|
||||
"github.com/smira/aptly/utils"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Dependency options
|
||||
@@ -333,11 +332,19 @@ func (l *PackageList) PrepareIndex() {
|
||||
l.indexed = true
|
||||
}
|
||||
|
||||
// Query searches package index using parsed query
|
||||
//func (l *PackageList) Query(, allMatches bool) (searchResults []*Package) {
|
||||
//}
|
||||
// Scan searches package index using full scan
|
||||
func (l *PackageList) Scan(q PackageQuery) (result *PackageList) {
|
||||
result = NewPackageList()
|
||||
for _, pkg := range l.packagesIndex {
|
||||
if q.Matches(pkg) {
|
||||
result.Add(pkg)
|
||||
}
|
||||
}
|
||||
|
||||
// Search searches package index for specified package(s)
|
||||
return
|
||||
}
|
||||
|
||||
// Search searches package index for specified package(s) using optimized queries
|
||||
func (l *PackageList) Search(dep Dependency, allMatches bool) (searchResults []*Package) {
|
||||
if !l.indexed {
|
||||
panic("list not indexed, can't search")
|
||||
@@ -377,7 +384,7 @@ func (l *PackageList) Search(dep Dependency, allMatches bool) (searchResults []*
|
||||
}
|
||||
|
||||
// Filter filters package index by specified queries (ORed together), possibly pulling dependencies
|
||||
func (l *PackageList) Filter(queries []string, withDependencies bool, source *PackageList, dependencyOptions int, architecturesList []string) (*PackageList, error) {
|
||||
func (l *PackageList) Filter(queries []PackageQuery, withDependencies bool, source *PackageList, dependencyOptions int, architecturesList []string) (*PackageList, error) {
|
||||
if !l.indexed {
|
||||
panic("list not indexed, can't filter")
|
||||
}
|
||||
@@ -385,46 +392,7 @@ func (l *PackageList) Filter(queries []string, withDependencies bool, source *Pa
|
||||
result := NewPackageList()
|
||||
|
||||
for _, query := range queries {
|
||||
isDepQuery := strings.IndexAny(query, " (){}=<>") != -1
|
||||
|
||||
if !isDepQuery {
|
||||
// try to interpret query as package string representation
|
||||
|
||||
// convert Package.String() to Package.Key()
|
||||
i := strings.Index(query, "_")
|
||||
if i != -1 {
|
||||
pkg, query := query[:i], query[i+1:]
|
||||
j := strings.LastIndex(query, "_")
|
||||
if j != -1 {
|
||||
version, arch := query[:j], query[j+1:]
|
||||
p := l.packages["P"+arch+" "+pkg+" "+version]
|
||||
if p != nil {
|
||||
result.Add(p)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// try as dependency
|
||||
dep, err := ParseDependency(query)
|
||||
if err != nil {
|
||||
if isDepQuery {
|
||||
return nil, err
|
||||
}
|
||||
// parsing failed, but probably that wasn't a dep query
|
||||
continue
|
||||
}
|
||||
|
||||
i := sort.Search(len(l.packagesIndex), func(j int) bool { return l.packagesIndex[j].Name >= dep.Pkg })
|
||||
|
||||
for i < len(l.packagesIndex) && l.packagesIndex[i].Name == dep.Pkg {
|
||||
p := l.packagesIndex[i]
|
||||
if p.MatchesDependency(dep) {
|
||||
result.Add(p)
|
||||
}
|
||||
i++
|
||||
}
|
||||
result.Append(query.Query(l))
|
||||
}
|
||||
|
||||
if withDependencies {
|
||||
|
||||
+26
-9
@@ -301,10 +301,7 @@ func (s *PackageListSuite) TestSearch(c *C) {
|
||||
}
|
||||
|
||||
func (s *PackageListSuite) TestFilter(c *C) {
|
||||
c.Check(func() { s.list.Filter([]string{"abcd_0.3_i386"}, false, nil, 0, nil) }, Panics, "list not indexed, can't filter")
|
||||
|
||||
_, err := s.il.Filter([]string{"app >3)"}, false, nil, 0, nil)
|
||||
c.Check(err, ErrorMatches, "unable to parse dependency.*")
|
||||
c.Check(func() { s.list.Filter([]PackageQuery{&PkgQuery{"abcd", "0.3", "i386"}}, false, nil, 0, nil) }, Panics, "list not indexed, can't filter")
|
||||
|
||||
plString := func(l *PackageList) string {
|
||||
list := make([]string, 0, l.Len())
|
||||
@@ -317,25 +314,45 @@ func (s *PackageListSuite) TestFilter(c *C) {
|
||||
return strings.Join(list, " ")
|
||||
}
|
||||
|
||||
result, err := s.il.Filter([]string{"app_1.1~bp1_i386"}, false, nil, 0, nil)
|
||||
result, err := s.il.Filter([]PackageQuery{&PkgQuery{"app", "1.1~bp1", "i386"}}, false, nil, 0, nil)
|
||||
c.Check(err, IsNil)
|
||||
c.Check(plString(result), Equals, "app_1.1~bp1_i386")
|
||||
|
||||
result, err = s.il.Filter([]string{"app_1.1~bp1_i386", "dpkg_1.7_source", "dpkg_1.8_amd64"}, false, nil, 0, nil)
|
||||
result, err = s.il.Filter([]PackageQuery{&PkgQuery{"app", "1.1~bp1", "i386"}, &PkgQuery{"dpkg", "1.7", "source"},
|
||||
&PkgQuery{"dpkg", "1.8", "amd64"}}, false, nil, 0, nil)
|
||||
c.Check(err, IsNil)
|
||||
c.Check(plString(result), Equals, "app_1.1~bp1_i386 dpkg_1.7_source")
|
||||
|
||||
result, err = s.il.Filter([]string{"app", "dpkg (>>1.6.1-3)", "app (>=1.0)", "xyz", "aa (>>3.0)"}, false, nil, 0, nil)
|
||||
result, err = s.il.Filter([]PackageQuery{
|
||||
&DependencyQuery{Dep: Dependency{Pkg: "app"}},
|
||||
&DependencyQuery{Dep: Dependency{Pkg: "dpkg", Relation: VersionGreater, Version: "1.6.1-3"}},
|
||||
&DependencyQuery{Dep: Dependency{Pkg: "app", Relation: VersionGreaterOrEqual, Version: "1.0"}},
|
||||
&DependencyQuery{Dep: Dependency{Pkg: "xyz"}},
|
||||
&DependencyQuery{Dep: Dependency{Pkg: "aa", Relation: VersionGreater, Version: "3.0"}}}, false, nil, 0, nil)
|
||||
c.Check(err, IsNil)
|
||||
c.Check(plString(result), Equals, "app_1.0_s390 app_1.1~bp1_amd64 app_1.1~bp1_arm app_1.1~bp1_i386 dpkg_1.7_i386 dpkg_1.7_source")
|
||||
|
||||
result, err = s.il.Filter([]string{"app {i386}"}, true, NewPackageList(), 0, []string{"i386"})
|
||||
result, err = s.il.Filter([]PackageQuery{&DependencyQuery{Dep: Dependency{Pkg: "app", Architecture: "i386"}}}, true, NewPackageList(), 0, []string{"i386"})
|
||||
c.Check(err, IsNil)
|
||||
c.Check(plString(result), Equals, "app_1.1~bp1_i386 data_1.1~bp1_all dpkg_1.7_i386 lib_1.0_i386 mailer_3.5.8_i386")
|
||||
|
||||
result, err = s.il.Filter([]string{"app (>=0.9)", "lib", "data"}, true, NewPackageList(), 0, []string{"i386", "amd64"})
|
||||
result, err = s.il.Filter([]PackageQuery{
|
||||
&DependencyQuery{Dep: Dependency{Pkg: "app", Relation: VersionGreaterOrEqual, Version: "0.9"}},
|
||||
&DependencyQuery{Dep: Dependency{Pkg: "lib"}},
|
||||
&DependencyQuery{Dep: Dependency{Pkg: "data"}}}, true, NewPackageList(), 0, []string{"i386", "amd64"})
|
||||
c.Check(err, IsNil)
|
||||
c.Check(plString(result), Equals, "app_1.0_s390 app_1.1~bp1_amd64 app_1.1~bp1_arm app_1.1~bp1_i386 data_1.1~bp1_all dpkg_1.6.1-3_amd64 dpkg_1.7_i386 lib_1.0_i386 mailer_3.5.8_i386")
|
||||
|
||||
result, err = s.il.Filter([]PackageQuery{&OrQuery{&PkgQuery{"app", "1.1~bp1", "i386"},
|
||||
&DependencyQuery{Dep: Dependency{Pkg: "dpkg", Relation: VersionGreater, Version: "1.6.1-3"}}}}, false, nil, 0, nil)
|
||||
c.Check(err, IsNil)
|
||||
c.Check(plString(result), Equals, "app_1.1~bp1_i386 dpkg_1.7_i386 dpkg_1.7_source")
|
||||
|
||||
result, err = s.il.Filter([]PackageQuery{&AndQuery{&PkgQuery{"app", "1.1~bp1", "i386"},
|
||||
&DependencyQuery{Dep: Dependency{Pkg: "dpkg", Relation: VersionGreater, Version: "1.6.1-3"}}}}, false, nil, 0, nil)
|
||||
c.Check(err, IsNil)
|
||||
c.Check(plString(result), Equals, "")
|
||||
|
||||
}
|
||||
|
||||
func (s *PackageListSuite) TestVerifyDependencies(c *C) {
|
||||
|
||||
+107
@@ -2,7 +2,12 @@ package deb
|
||||
|
||||
// PackageQuery is interface of predicate on Package
|
||||
type PackageQuery interface {
|
||||
// Matches calculates match of condition against package
|
||||
Matches(pkg *Package) bool
|
||||
// Searchable returns if search strategy is possible for this query
|
||||
Searchable() bool
|
||||
// Query performs search on package list
|
||||
Query(list *PackageList) *PackageList
|
||||
}
|
||||
|
||||
// OrQuery is L | R
|
||||
@@ -27,6 +32,13 @@ type FieldQuery struct {
|
||||
Value string
|
||||
}
|
||||
|
||||
// PkgQuery is search request against specific package
|
||||
type PkgQuery struct {
|
||||
Pkg string
|
||||
Version string
|
||||
Arch string
|
||||
}
|
||||
|
||||
// DependencyQuery is generic Debian-dependency like query
|
||||
type DependencyQuery struct {
|
||||
Dep Dependency
|
||||
@@ -37,22 +49,117 @@ func (q *OrQuery) Matches(pkg *Package) bool {
|
||||
return q.L.Matches(pkg) || q.R.Matches(pkg)
|
||||
}
|
||||
|
||||
// Searchable is true only if both parts are searchable
|
||||
func (q *OrQuery) Searchable() bool {
|
||||
return q.L.Searchable() && q.R.Searchable()
|
||||
}
|
||||
|
||||
// Query strategy depends on nodes
|
||||
func (q *OrQuery) Query(list *PackageList) (result *PackageList) {
|
||||
if q.Searchable() {
|
||||
result = q.L.Query(list)
|
||||
result.Append(q.R.Query(list))
|
||||
} else {
|
||||
result = list.Scan(q)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Matches if both of L, R matches
|
||||
func (q *AndQuery) Matches(pkg *Package) bool {
|
||||
return q.L.Matches(pkg) && q.R.Matches(pkg)
|
||||
}
|
||||
|
||||
// Searchable is true if any of the parts are searchable
|
||||
func (q *AndQuery) Searchable() bool {
|
||||
return q.L.Searchable() || q.R.Searchable()
|
||||
}
|
||||
|
||||
// Query strategy depends on nodes
|
||||
func (q *AndQuery) Query(list *PackageList) (result *PackageList) {
|
||||
if !q.Searchable() {
|
||||
result = list.Scan(q)
|
||||
} else {
|
||||
if q.L.Searchable() {
|
||||
result = q.L.Query(list)
|
||||
result = result.Scan(q.R)
|
||||
} else {
|
||||
result = q.R.Query(list)
|
||||
result = result.Scan(q.L)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Matches if not matches
|
||||
func (q *NotQuery) Matches(pkg *Package) bool {
|
||||
return !q.Q.Matches(pkg)
|
||||
}
|
||||
|
||||
// Searchable is false
|
||||
func (q *NotQuery) Searchable() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// NotQuery strategy is scan always
|
||||
func (q *NotQuery) Query(list *PackageList) (result *PackageList) {
|
||||
result = list.Scan(q)
|
||||
return
|
||||
}
|
||||
|
||||
// Matches on generic field
|
||||
func (q *FieldQuery) Matches(pkg *Package) bool {
|
||||
panic("not implemented yet")
|
||||
}
|
||||
|
||||
// Query runs iteration through list
|
||||
func (q *FieldQuery) Query(list *PackageList) (result *PackageList) {
|
||||
panic("not implemented yet")
|
||||
}
|
||||
|
||||
// Searchable depends on the query
|
||||
func (q *FieldQuery) Searchable() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Matches on dependency condition
|
||||
func (q *DependencyQuery) Matches(pkg *Package) bool {
|
||||
return pkg.MatchesDependency(q.Dep)
|
||||
}
|
||||
|
||||
// Searchable is always true for dependency query
|
||||
func (q *DependencyQuery) Searchable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Query runs PackageList.Search
|
||||
func (q *DependencyQuery) Query(list *PackageList) (result *PackageList) {
|
||||
result = NewPackageList()
|
||||
for _, pkg := range list.Search(q.Dep, true) {
|
||||
result.Add(pkg)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Matches on specific properties
|
||||
func (q *PkgQuery) Matches(pkg *Package) bool {
|
||||
return pkg.Name == q.Pkg && pkg.Version == q.Version && pkg.Architecture == q.Arch
|
||||
}
|
||||
|
||||
// Searchable is always true for package query
|
||||
func (q *PkgQuery) Searchable() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Query looks up specific package
|
||||
func (q *PkgQuery) Query(list *PackageList) (result *PackageList) {
|
||||
result = NewPackageList()
|
||||
|
||||
pkg := list.packages["P"+q.Arch+" "+q.Pkg+" "+q.Version]
|
||||
if pkg != nil {
|
||||
result.Add(pkg)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user