mirror of
https://github.com/aptly-dev/aptly.git
synced 2026-05-08 22:30:41 +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/aptly"
|
||||||
"github.com/smira/aptly/utils"
|
"github.com/smira/aptly/utils"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Dependency options
|
// Dependency options
|
||||||
@@ -333,11 +332,19 @@ func (l *PackageList) PrepareIndex() {
|
|||||||
l.indexed = true
|
l.indexed = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query searches package index using parsed query
|
// Scan searches package index using full scan
|
||||||
//func (l *PackageList) Query(, allMatches bool) (searchResults []*Package) {
|
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) {
|
func (l *PackageList) Search(dep Dependency, allMatches bool) (searchResults []*Package) {
|
||||||
if !l.indexed {
|
if !l.indexed {
|
||||||
panic("list not indexed, can't search")
|
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
|
// 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 {
|
if !l.indexed {
|
||||||
panic("list not indexed, can't filter")
|
panic("list not indexed, can't filter")
|
||||||
}
|
}
|
||||||
@@ -385,46 +392,7 @@ func (l *PackageList) Filter(queries []string, withDependencies bool, source *Pa
|
|||||||
result := NewPackageList()
|
result := NewPackageList()
|
||||||
|
|
||||||
for _, query := range queries {
|
for _, query := range queries {
|
||||||
isDepQuery := strings.IndexAny(query, " (){}=<>") != -1
|
result.Append(query.Query(l))
|
||||||
|
|
||||||
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++
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if withDependencies {
|
if withDependencies {
|
||||||
|
|||||||
+26
-9
@@ -301,10 +301,7 @@ func (s *PackageListSuite) TestSearch(c *C) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *PackageListSuite) TestFilter(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")
|
c.Check(func() { s.list.Filter([]PackageQuery{&PkgQuery{"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.*")
|
|
||||||
|
|
||||||
plString := func(l *PackageList) string {
|
plString := func(l *PackageList) string {
|
||||||
list := make([]string, 0, l.Len())
|
list := make([]string, 0, l.Len())
|
||||||
@@ -317,25 +314,45 @@ func (s *PackageListSuite) TestFilter(c *C) {
|
|||||||
return strings.Join(list, " ")
|
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(err, IsNil)
|
||||||
c.Check(plString(result), Equals, "app_1.1~bp1_i386")
|
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(err, IsNil)
|
||||||
c.Check(plString(result), Equals, "app_1.1~bp1_i386 dpkg_1.7_source")
|
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(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")
|
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(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")
|
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(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")
|
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) {
|
func (s *PackageListSuite) TestVerifyDependencies(c *C) {
|
||||||
|
|||||||
+107
@@ -2,7 +2,12 @@ package deb
|
|||||||
|
|
||||||
// PackageQuery is interface of predicate on Package
|
// PackageQuery is interface of predicate on Package
|
||||||
type PackageQuery interface {
|
type PackageQuery interface {
|
||||||
|
// Matches calculates match of condition against package
|
||||||
Matches(pkg *Package) bool
|
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
|
// OrQuery is L | R
|
||||||
@@ -27,6 +32,13 @@ type FieldQuery struct {
|
|||||||
Value string
|
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
|
// DependencyQuery is generic Debian-dependency like query
|
||||||
type DependencyQuery struct {
|
type DependencyQuery struct {
|
||||||
Dep Dependency
|
Dep Dependency
|
||||||
@@ -37,22 +49,117 @@ func (q *OrQuery) Matches(pkg *Package) bool {
|
|||||||
return q.L.Matches(pkg) || q.R.Matches(pkg)
|
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
|
// Matches if both of L, R matches
|
||||||
func (q *AndQuery) Matches(pkg *Package) bool {
|
func (q *AndQuery) Matches(pkg *Package) bool {
|
||||||
return q.L.Matches(pkg) && q.R.Matches(pkg)
|
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
|
// Matches if not matches
|
||||||
func (q *NotQuery) Matches(pkg *Package) bool {
|
func (q *NotQuery) Matches(pkg *Package) bool {
|
||||||
return !q.Q.Matches(pkg)
|
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
|
// Matches on generic field
|
||||||
func (q *FieldQuery) Matches(pkg *Package) bool {
|
func (q *FieldQuery) Matches(pkg *Package) bool {
|
||||||
panic("not implemented yet")
|
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
|
// Matches on dependency condition
|
||||||
func (q *DependencyQuery) Matches(pkg *Package) bool {
|
func (q *DependencyQuery) Matches(pkg *Package) bool {
|
||||||
return pkg.MatchesDependency(q.Dep)
|
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