mirror of
https://github.com/aptly-dev/aptly.git
synced 2026-07-26 13:47:40 +00:00
Introduce regexp query matching.
This commit is contained in:
@@ -78,9 +78,9 @@ func aptlySnapshotPull(cmd *commander.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build architecture query: (arch == "i386" | arch == "amd64" | ...)
|
// Build architecture query: (arch == "i386" | arch == "amd64" | ...)
|
||||||
var archQuery deb.PackageQuery = &deb.FieldQuery{"$Architecture", deb.VersionEqual, ""}
|
var archQuery deb.PackageQuery = &deb.FieldQuery{Field: "$Architecture", Relation: deb.VersionEqual, Value: ""}
|
||||||
for _, arch := range architecturesList {
|
for _, arch := range architecturesList {
|
||||||
archQuery = &deb.OrQuery{L: &deb.FieldQuery{"$Architecture", deb.VersionEqual, arch}, R: archQuery}
|
archQuery = &deb.OrQuery{L: &deb.FieldQuery{Field: "$Architecture", Relation: deb.VersionEqual, Value: arch}, R: archQuery}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initial queries out of arguments
|
// Initial queries out of arguments
|
||||||
|
|||||||
+9
-3
@@ -3,6 +3,7 @@ package deb
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
. "launchpad.net/gocheck"
|
. "launchpad.net/gocheck"
|
||||||
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@@ -358,20 +359,25 @@ func (s *PackageListSuite) TestFilter(c *C) {
|
|||||||
c.Check(err, IsNil)
|
c.Check(err, IsNil)
|
||||||
c.Check(plString(result), Equals, "app_1.0_s390 app_1.1~bp1_i386 data_1.1~bp1_all")
|
c.Check(plString(result), Equals, "app_1.0_s390 app_1.1~bp1_i386 data_1.1~bp1_all")
|
||||||
|
|
||||||
result, err = s.il.Filter([]PackageQuery{&AndQuery{&FieldQuery{"Version", VersionGreaterOrEqual, "1.0"},
|
result, err = s.il.Filter([]PackageQuery{&AndQuery{&FieldQuery{Field: "Version", Relation: VersionGreaterOrEqual, Value: "1.0"},
|
||||||
&FieldQuery{Field: "$Architecture", Relation: VersionEqual, Value: "s390"}}}, false, nil, 0, nil)
|
&FieldQuery{Field: "$Architecture", Relation: VersionEqual, Value: "s390"}}}, false, nil, 0, nil)
|
||||||
c.Check(err, IsNil)
|
c.Check(err, IsNil)
|
||||||
c.Check(plString(result), Equals, "app_1.0_s390 data_1.1~bp1_all")
|
c.Check(plString(result), Equals, "app_1.0_s390 data_1.1~bp1_all")
|
||||||
|
|
||||||
result, err = s.il.Filter([]PackageQuery{&AndQuery{
|
result, err = s.il.Filter([]PackageQuery{&AndQuery{
|
||||||
&FieldQuery{"$Architecture", VersionPatternMatch, "i*6"}, &PkgQuery{"app", "1.1~bp1", "i386"}}}, false, nil, 0, nil)
|
&FieldQuery{Field: "$Architecture", Relation: VersionPatternMatch, Value: "i*6"}, &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([]PackageQuery{&NotQuery{
|
result, err = s.il.Filter([]PackageQuery{&NotQuery{
|
||||||
&FieldQuery{"$Architecture", VersionPatternMatch, "i*6"}}}, false, nil, 0, nil)
|
&FieldQuery{Field: "$Architecture", Relation: VersionPatternMatch, Value: "i*6"}}}, 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 data_1.1~bp1_all dpkg_1.6.1-3_amd64 dpkg_1.6.1-3_arm dpkg_1.6.1-3_source dpkg_1.7_source libx_1.5_arm")
|
c.Check(plString(result), Equals, "app_1.0_s390 app_1.1~bp1_amd64 app_1.1~bp1_arm data_1.1~bp1_all dpkg_1.6.1-3_amd64 dpkg_1.6.1-3_arm dpkg_1.6.1-3_source dpkg_1.7_source libx_1.5_arm")
|
||||||
|
|
||||||
|
result, err = s.il.Filter([]PackageQuery{&AndQuery{
|
||||||
|
&FieldQuery{Field: "$Architecture", Relation: VersionRegexp, Value: "i.*6", Regexp: regexp.MustCompile("i.*6")}, &PkgQuery{"app", "1.1~bp1", "i386"}}}, false, nil, 0, nil)
|
||||||
|
c.Check(err, IsNil)
|
||||||
|
c.Check(plString(result), Equals, "app_1.1~bp1_i386")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *PackageListSuite) TestVerifyDependencies(c *C) {
|
func (s *PackageListSuite) TestVerifyDependencies(c *C) {
|
||||||
|
|||||||
+1
-1
@@ -295,7 +295,7 @@ func (p *Package) MatchesDependency(dep Dependency) bool {
|
|||||||
matched, err := filepath.Match(dep.Version, p.Version)
|
matched, err := filepath.Match(dep.Version, p.Version)
|
||||||
return err == nil && matched
|
return err == nil && matched
|
||||||
case VersionRegexp:
|
case VersionRegexp:
|
||||||
panic("regexp matching not implemented yet")
|
return dep.Regexp.FindStringIndex(p.Version) != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
panic("unknown relation")
|
panic("unknown relation")
|
||||||
|
|||||||
+11
-4
@@ -7,6 +7,7 @@ import (
|
|||||||
. "launchpad.net/gocheck"
|
. "launchpad.net/gocheck"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PackageSuite struct {
|
type PackageSuite struct {
|
||||||
@@ -266,10 +267,16 @@ func (s *PackageSuite) TestMatchesDependency(c *C) {
|
|||||||
c.Check(p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionPatternMatch, Version: "7.40-[2"}), Equals, false)
|
c.Check(p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionPatternMatch, Version: "7.40-[2"}), Equals, false)
|
||||||
c.Check(p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionPatternMatch, Version: "7.40-[34]"}), Equals, false)
|
c.Check(p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionPatternMatch, Version: "7.40-[34]"}), Equals, false)
|
||||||
|
|
||||||
// %
|
// ~
|
||||||
c.Check(func() {
|
c.Check(
|
||||||
p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionRegexp, Version: "7\\.40-.*"})
|
p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionRegexp, Version: "7\\.40-.*",
|
||||||
}, Panics, "regexp matching not implemented yet")
|
Regexp: regexp.MustCompile("7\\.40-.*")}), Equals, true)
|
||||||
|
c.Check(
|
||||||
|
p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionRegexp, Version: "7\\.40-.*",
|
||||||
|
Regexp: regexp.MustCompile("40")}), Equals, true)
|
||||||
|
c.Check(
|
||||||
|
p.MatchesDependency(Dependency{Pkg: "alien-arena-common", Architecture: "i386", Relation: VersionRegexp, Version: "7\\.40-.*",
|
||||||
|
Regexp: regexp.MustCompile("39-.*")}), Equals, false)
|
||||||
|
|
||||||
// Provides
|
// Provides
|
||||||
c.Check(p.MatchesDependency(Dependency{Pkg: "game", Relation: VersionDontCare}), Equals, false)
|
c.Check(p.MatchesDependency(Dependency{Pkg: "game", Relation: VersionDontCare}), Equals, false)
|
||||||
|
|||||||
+4
-2
@@ -2,6 +2,7 @@ package deb
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PackageQuery is interface of predicate on Package
|
// PackageQuery is interface of predicate on Package
|
||||||
@@ -34,6 +35,7 @@ type FieldQuery struct {
|
|||||||
Field string
|
Field string
|
||||||
Relation int
|
Relation int
|
||||||
Value string
|
Value string
|
||||||
|
Regexp *regexp.Regexp
|
||||||
}
|
}
|
||||||
|
|
||||||
// PkgQuery is search request against specific package
|
// PkgQuery is search request against specific package
|
||||||
@@ -114,7 +116,7 @@ func (q *NotQuery) Query(list *PackageList) (result *PackageList) {
|
|||||||
// Matches on generic field
|
// Matches on generic field
|
||||||
func (q *FieldQuery) Matches(pkg *Package) bool {
|
func (q *FieldQuery) Matches(pkg *Package) bool {
|
||||||
if q.Field == "$Version" {
|
if q.Field == "$Version" {
|
||||||
return pkg.MatchesDependency(Dependency{Pkg: pkg.Name, Relation: q.Relation, Version: q.Value})
|
return pkg.MatchesDependency(Dependency{Pkg: pkg.Name, Relation: q.Relation, Version: q.Value, Regexp: q.Regexp})
|
||||||
}
|
}
|
||||||
if q.Field == "$Architecture" && q.Relation == VersionEqual {
|
if q.Field == "$Architecture" && q.Relation == VersionEqual {
|
||||||
return pkg.MatchesArchitecture(q.Value)
|
return pkg.MatchesArchitecture(q.Value)
|
||||||
@@ -139,7 +141,7 @@ func (q *FieldQuery) Matches(pkg *Package) bool {
|
|||||||
matched, err := filepath.Match(q.Value, field)
|
matched, err := filepath.Match(q.Value, field)
|
||||||
return err == nil && matched
|
return err == nil && matched
|
||||||
case VersionRegexp:
|
case VersionRegexp:
|
||||||
panic("regexp matching not implemented yet")
|
return q.Regexp.FindStringIndex(field) != nil
|
||||||
|
|
||||||
}
|
}
|
||||||
panic("unknown relation")
|
panic("unknown relation")
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package deb
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
@@ -188,6 +189,7 @@ type Dependency struct {
|
|||||||
Relation int
|
Relation int
|
||||||
Version string
|
Version string
|
||||||
Architecture string
|
Architecture string
|
||||||
|
Regexp *regexp.Regexp
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hash calculates some predefined unique ID of Dependency
|
// Hash calculates some predefined unique ID of Dependency
|
||||||
|
|||||||
+19
-2
@@ -3,6 +3,7 @@ package query
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/smira/aptly/deb"
|
"github.com/smira/aptly/deb"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
@@ -135,7 +136,15 @@ func (p *parser) D() deb.PackageQuery {
|
|||||||
r, _ := utf8.DecodeRuneInString(field)
|
r, _ := utf8.DecodeRuneInString(field)
|
||||||
if strings.HasPrefix(field, "$") || unicode.IsUpper(r) {
|
if strings.HasPrefix(field, "$") || unicode.IsUpper(r) {
|
||||||
// special field or regular field
|
// special field or regular field
|
||||||
return &deb.FieldQuery{Field: field, Relation: operatorToRelation(operator), Value: value}
|
q := &deb.FieldQuery{Field: field, Relation: operatorToRelation(operator), Value: value}
|
||||||
|
if q.Relation == deb.VersionRegexp {
|
||||||
|
var err error
|
||||||
|
q.Regexp, err = regexp.Compile(q.Value)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("regexp compile failed: %s", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return q
|
||||||
} else if operator == 0 && value == "" {
|
} else if operator == 0 && value == "" {
|
||||||
if pkg, version, arch, ok := parsePackageRef(field); ok {
|
if pkg, version, arch, ok := parsePackageRef(field); ok {
|
||||||
// query for specific package
|
// query for specific package
|
||||||
@@ -144,11 +153,19 @@ func (p *parser) D() deb.PackageQuery {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// regular dependency-like query
|
// regular dependency-like query
|
||||||
return &deb.DependencyQuery{Dep: deb.Dependency{
|
q := &deb.DependencyQuery{Dep: deb.Dependency{
|
||||||
Pkg: field,
|
Pkg: field,
|
||||||
Relation: operatorToRelation(operator),
|
Relation: operatorToRelation(operator),
|
||||||
Version: value,
|
Version: value,
|
||||||
Architecture: p.ArchCondition()}}
|
Architecture: p.ArchCondition()}}
|
||||||
|
if q.Dep.Relation == deb.VersionRegexp {
|
||||||
|
var err error
|
||||||
|
q.Dep.Regexp, err = regexp.Compile(q.Dep.Version)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("regexp compile failed: %s", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return q
|
||||||
}
|
}
|
||||||
|
|
||||||
// condition := '(' <operator> value ')' |
|
// condition := '(' <operator> value ')' |
|
||||||
|
|||||||
+18
-1
@@ -3,6 +3,7 @@ package query
|
|||||||
import (
|
import (
|
||||||
"github.com/smira/aptly/deb"
|
"github.com/smira/aptly/deb"
|
||||||
. "launchpad.net/gocheck"
|
. "launchpad.net/gocheck"
|
||||||
|
"regexp"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SyntaxSuite struct {
|
type SyntaxSuite struct {
|
||||||
@@ -32,7 +33,8 @@ func (s *SyntaxSuite) TestParsing(c *C) {
|
|||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
c.Check(q.(*deb.AndQuery).L, DeepEquals, &deb.DependencyQuery{Dep: deb.Dependency{Pkg: "package", Relation: deb.VersionDontCare}})
|
c.Check(q.(*deb.AndQuery).L, DeepEquals, &deb.DependencyQuery{Dep: deb.Dependency{Pkg: "package", Relation: deb.VersionDontCare}})
|
||||||
c.Check(q.(*deb.AndQuery).R.(*deb.NotQuery).Q.(*deb.OrQuery).L, DeepEquals, &deb.FieldQuery{Field: "Name", Relation: deb.VersionDontCare})
|
c.Check(q.(*deb.AndQuery).R.(*deb.NotQuery).Q.(*deb.OrQuery).L, DeepEquals, &deb.FieldQuery{Field: "Name", Relation: deb.VersionDontCare})
|
||||||
c.Check(q.(*deb.AndQuery).R.(*deb.NotQuery).Q.(*deb.OrQuery).R, DeepEquals, &deb.FieldQuery{Field: "$Source", Relation: deb.VersionRegexp, Value: "a.*"})
|
c.Check(q.(*deb.AndQuery).R.(*deb.NotQuery).Q.(*deb.OrQuery).R, DeepEquals, &deb.FieldQuery{Field: "$Source", Relation: deb.VersionRegexp, Value: "a.*",
|
||||||
|
Regexp: regexp.MustCompile("a.*")})
|
||||||
|
|
||||||
l, _ = lex("query", "package (> 5.3.7)")
|
l, _ = lex("query", "package (> 5.3.7)")
|
||||||
q, err = parse(l)
|
q, err = parse(l)
|
||||||
@@ -40,6 +42,13 @@ func (s *SyntaxSuite) TestParsing(c *C) {
|
|||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
c.Check(q, DeepEquals, &deb.DependencyQuery{Dep: deb.Dependency{Pkg: "package", Relation: deb.VersionGreaterOrEqual, Version: "5.3.7"}})
|
c.Check(q, DeepEquals, &deb.DependencyQuery{Dep: deb.Dependency{Pkg: "package", Relation: deb.VersionGreaterOrEqual, Version: "5.3.7"}})
|
||||||
|
|
||||||
|
l, _ = lex("query", "package (~ 5\\.3.*~dev)")
|
||||||
|
q, err = parse(l)
|
||||||
|
|
||||||
|
c.Assert(err, IsNil)
|
||||||
|
c.Check(q, DeepEquals, &deb.DependencyQuery{Dep: deb.Dependency{Pkg: "package", Relation: deb.VersionRegexp, Version: "5\\.3.*~dev",
|
||||||
|
Regexp: regexp.MustCompile("5\\.3.*~dev")}})
|
||||||
|
|
||||||
l, _ = lex("query", "alien-data_1.3.4~dev_i386")
|
l, _ = lex("query", "alien-data_1.3.4~dev_i386")
|
||||||
q, err = parse(l)
|
q, err = parse(l)
|
||||||
|
|
||||||
@@ -78,4 +87,12 @@ func (s *SyntaxSuite) TestParsingErrors(c *C) {
|
|||||||
l, _ = lex("query", "'package )")
|
l, _ = lex("query", "'package )")
|
||||||
_, err = parse(l)
|
_, err = parse(l)
|
||||||
c.Check(err, ErrorMatches, "parsing failed: unexpected token error: unexpected eof in quoted string: expecting field or package name")
|
c.Check(err, ErrorMatches, "parsing failed: unexpected token error: unexpected eof in quoted string: expecting field or package name")
|
||||||
|
|
||||||
|
l, _ = lex("query", "package (~ 1.2[34)")
|
||||||
|
_, err = parse(l)
|
||||||
|
c.Check(err, ErrorMatches, "parsing failed: regexp compile failed: error parsing regexp: missing closing \\]: `\\[34`")
|
||||||
|
|
||||||
|
l, _ = lex("query", "$Name (~ 1.2[34)")
|
||||||
|
_, err = parse(l)
|
||||||
|
c.Check(err, ErrorMatches, "parsing failed: regexp compile failed: error parsing regexp: missing closing \\]: `\\[34`")
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user