@@ -34,6 +34,7 @@ import (
|
|||||||
type Interface interface {
|
type Interface interface {
|
||||||
ConstraintsConvertable
|
ConstraintsConvertable
|
||||||
fmt.GoStringer
|
fmt.GoStringer
|
||||||
|
Evaluate(v map[string]bool) bool
|
||||||
Validate() error
|
Validate() error
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,6 +67,15 @@ func (cs Constraints) Validate() error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cs Constraints) Evaluate(v map[string]bool) bool {
|
||||||
|
r := true
|
||||||
|
for _, c := range cs {
|
||||||
|
r = r && c.Evaluate(v)
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
func (cs Constraints) GoString() string {
|
func (cs Constraints) GoString() string {
|
||||||
s := ""
|
s := ""
|
||||||
for _, c := range cs {
|
for _, c := range cs {
|
||||||
@@ -86,6 +96,14 @@ func (c Constraint) Validate() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c Constraint) Evaluate(v map[string]bool) bool {
|
||||||
|
r := false
|
||||||
|
for _, o := range c {
|
||||||
|
r = r || o.Evaluate(v)
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
func (c Constraint) GoString() string {
|
func (c Constraint) GoString() string {
|
||||||
s := "// +build"
|
s := "// +build"
|
||||||
for _, o := range c {
|
for _, o := range c {
|
||||||
@@ -107,6 +125,14 @@ func (o Option) Validate() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o Option) Evaluate(v map[string]bool) bool {
|
||||||
|
r := true
|
||||||
|
for _, t := range o {
|
||||||
|
r = r && t.Evaluate(v)
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
func (o Option) GoString() string {
|
func (o Option) GoString() string {
|
||||||
var ts []string
|
var ts []string
|
||||||
for _, t := range o {
|
for _, t := range o {
|
||||||
@@ -119,22 +145,24 @@ func (t Term) ToConstraints() Constraints { return t.ToOption().ToConstraints()
|
|||||||
func (t Term) ToConstraint() Constraint { return t.ToOption().ToConstraint() }
|
func (t Term) ToConstraint() Constraint { return t.ToOption().ToConstraint() }
|
||||||
func (t Term) ToOption() Option { return Option{t} }
|
func (t Term) ToOption() Option { return Option{t} }
|
||||||
|
|
||||||
func (t Term) Validate() error {
|
func (t Term) IsNegated() bool { return strings.HasPrefix(string(t), "!") }
|
||||||
name := string(t)
|
|
||||||
|
|
||||||
|
func (t Term) Name() string {
|
||||||
|
return strings.TrimPrefix(string(t), "!")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t Term) Validate() error {
|
||||||
// Reference: https://github.com/golang/go/blob/204a8f55dc2e0ac8d27a781dab0da609b98560da/src/cmd/go/internal/imports/build.go#L110-L112
|
// Reference: https://github.com/golang/go/blob/204a8f55dc2e0ac8d27a781dab0da609b98560da/src/cmd/go/internal/imports/build.go#L110-L112
|
||||||
//
|
//
|
||||||
// if strings.HasPrefix(name, "!!") { // bad syntax, reject always
|
// if strings.HasPrefix(name, "!!") { // bad syntax, reject always
|
||||||
// return false
|
// return false
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
if strings.HasPrefix(name, "!!") {
|
if strings.HasPrefix(string(t), "!!") {
|
||||||
return errors.New("at most one '!' allowed")
|
return errors.New("at most one '!' allowed")
|
||||||
}
|
}
|
||||||
|
|
||||||
name = strings.TrimPrefix(name, "!")
|
if len(t.Name()) == 0 {
|
||||||
|
|
||||||
if len(name) == 0 {
|
|
||||||
return errors.New("empty tag name")
|
return errors.New("empty tag name")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,7 +176,7 @@ func (t Term) Validate() error {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
for _, c := range name {
|
for _, c := range t.Name() {
|
||||||
if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' {
|
if !unicode.IsLetter(c) && !unicode.IsDigit(c) && c != '_' && c != '.' {
|
||||||
return fmt.Errorf("character '%c' disallowed in tags", c)
|
return fmt.Errorf("character '%c' disallowed in tags", c)
|
||||||
}
|
}
|
||||||
@@ -157,6 +185,10 @@ func (t Term) Validate() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t Term) Evaluate(v map[string]bool) bool {
|
||||||
|
return (t.Validate() == nil) && (v[t.Name()] == !t.IsNegated())
|
||||||
|
}
|
||||||
|
|
||||||
func (t Term) GoString() string { return string(t) }
|
func (t Term) GoString() string { return string(t) }
|
||||||
|
|
||||||
func Not(ident string) Term {
|
func Not(ident string) Term {
|
||||||
@@ -202,3 +234,11 @@ func ParseConstraint(expr string) (Constraint, error) {
|
|||||||
}
|
}
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SetTags(names ...string) map[string]bool {
|
||||||
|
v := map[string]bool{}
|
||||||
|
for _, n := range names {
|
||||||
|
v[n] = true
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|||||||
@@ -65,10 +65,7 @@ func TestParseConstraintRoundTrip(t *testing.T) {
|
|||||||
"linux,386 darwin,!cgo",
|
"linux,386 darwin,!cgo",
|
||||||
}
|
}
|
||||||
for _, expr := range exprs {
|
for _, expr := range exprs {
|
||||||
c, err := ParseConstraint(expr)
|
c := AssertParseConstraint(t, expr)
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("error parsing expression %q: %q", expr, err)
|
|
||||||
}
|
|
||||||
got := c.GoString()
|
got := c.GoString()
|
||||||
expect := "// +build " + expr + "\n"
|
expect := "// +build " + expr + "\n"
|
||||||
if got != expect {
|
if got != expect {
|
||||||
@@ -84,3 +81,62 @@ func TestParseConstraintError(t *testing.T) {
|
|||||||
t.Fatalf("expected error parsing %q", expr)
|
t.Fatalf("expected error parsing %q", expr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEvaluate(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
Constraint Interface
|
||||||
|
Values map[string]bool
|
||||||
|
Expect bool
|
||||||
|
}{
|
||||||
|
{Term("a"), SetTags("a"), true},
|
||||||
|
{Term("!a"), SetTags("a"), false},
|
||||||
|
{Term("!a"), SetTags(), true},
|
||||||
|
{Term("inval-id"), SetTags("inval-id"), false},
|
||||||
|
|
||||||
|
{Opt(Term("a"), Term("b")), SetTags(), false},
|
||||||
|
{Opt(Term("a"), Term("b")), SetTags("a"), false},
|
||||||
|
{Opt(Term("a"), Term("b")), SetTags("b"), false},
|
||||||
|
{Opt(Term("a"), Term("b")), SetTags("a", "b"), true},
|
||||||
|
{Opt(Term("a"), Term("b-a-d")), SetTags("a", "b-a-d"), false},
|
||||||
|
|
||||||
|
{
|
||||||
|
Any(Opt(Term("linux"), Term("386")), Opt("darwin", Not("cgo"))),
|
||||||
|
SetTags("linux", "386"),
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Any(Opt(Term("linux"), Term("386")), Opt("darwin", Not("cgo"))),
|
||||||
|
SetTags("darwin"),
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Any(Opt(Term("linux"), Term("386")), Opt("darwin", Not("cgo"))),
|
||||||
|
SetTags("linux", "darwin", "cgo"),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
And(Any(Term("linux"), Term("darwin")), Term("386")),
|
||||||
|
SetTags("darwin", "386"),
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, c := range cases {
|
||||||
|
got := c.Constraint.Evaluate(c.Values)
|
||||||
|
if c.Constraint.Validate() != nil && got {
|
||||||
|
t.Fatal("invalid expressions must evaluate false")
|
||||||
|
}
|
||||||
|
if got != c.Expect {
|
||||||
|
t.Errorf("%#v evaluated with %#v got %v expect %v", c.Constraint, c.Values, got, c.Expect)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func AssertParseConstraint(t *testing.T, expr string) Constraint {
|
||||||
|
t.Helper()
|
||||||
|
c, err := ParseConstraint(expr)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error parsing expression %q: %q", expr, err)
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user