diff --git a/pkg/labels/labels.go b/pkg/labels/labels.go index 86971d3f841..156e18561c3 100644 --- a/pkg/labels/labels.go +++ b/pkg/labels/labels.go @@ -29,7 +29,7 @@ type Labels interface { // A map of label:value. Implements Labels. type Set map[string]string -// All labels listed as a human readable string. Conveiently, exactly the format +// All labels listed as a human readable string. Conveniently, exactly the format // that ParseQuery takes. func (ls Set) String() string { query := make([]string, 0, len(ls)) diff --git a/pkg/labels/query.go b/pkg/labels/query.go index 00f01bcb8b7..5e563924a3c 100644 --- a/pkg/labels/query.go +++ b/pkg/labels/query.go @@ -32,53 +32,50 @@ type Query interface { // Everything returns a query that matches all labels. func Everything() Query { - return &queryTerm{} + return andTerm{} } -// A single term of a label query. -type queryTerm struct { - // Not inverts the meaning of the items in this term. - not bool - - // Exactly one of the below three items should be used. - - // If non-nil, we match Set l iff l[*label] == *value. - label, value *string - - // A list of terms which must all match for this query term to return true. - and []queryTerm - - // A list of terms, any one of which will cause this query term to return true. - // Parsing/printing not implemented. - or []queryTerm +type hasTerm struct { + label, value string } -func (l *queryTerm) Matches(ls Labels) bool { - matches := !l.not - switch { - case l.label != nil && l.value != nil: - if ls.Get(*l.label) == *l.value { - return matches +func (t *hasTerm) Matches(ls Labels) bool { + return ls.Get(t.label) == t.value +} + +func (t *hasTerm) String() string { + return fmt.Sprintf("%v=%v", t.label, t.value) +} + +type notHasTerm struct { + label, value string +} + +func (t *notHasTerm) Matches(ls Labels) bool { + return ls.Get(t.label) != t.value +} + +func (t *notHasTerm) String() string { + return fmt.Sprintf("%v!=%v", t.label, t.value) +} + +type andTerm []Query + +func (t andTerm) Matches(ls Labels) bool { + for _, q := range t { + if !q.Matches(ls) { + return false } - return !matches - case len(l.and) > 0: - for i := range l.and { - if !l.and[i].Matches(ls) { - return !matches - } - } - return matches - case len(l.or) > 0: - for i := range l.or { - if l.or[i].Matches(ls) { - return matches - } - } - return !matches } + return true +} - // Empty queries match everything - return matches +func (t andTerm) String() string { + var terms []string + for _, q := range t { + terms = append(terms, q.String()) + } + return strings.Join(terms, ",") } func try(queryPiece, op string) (lhs, rhs string, ok bool) { @@ -91,55 +88,36 @@ func try(queryPiece, op string) (lhs, rhs string, ok bool) { // Given a Set, return a Query which will match exactly that Set. func QueryFromSet(ls Set) Query { - var query queryTerm - for l, v := range ls { - // Make a copy, because we're taking the address below - label, value := l, v - query.and = append(query.and, queryTerm{label: &label, value: &value}) + var items []Query + for label, value := range ls { + items = append(items, &hasTerm{label: label, value: value}) } - return &query + if len(items) == 1 { + return items[0] + } + return andTerm(items) } // Takes a string repsenting a label query and returns an object suitable for matching, or an error. func ParseQuery(query string) (Query, error) { parts := strings.Split(query, ",") - var items []queryTerm + var items []Query for _, part := range parts { if part == "" { continue } if lhs, rhs, ok := try(part, "!="); ok { - items = append(items, queryTerm{not: true, label: &lhs, value: &rhs}) + items = append(items, ¬HasTerm{label: lhs, value: rhs}) } else if lhs, rhs, ok := try(part, "=="); ok { - items = append(items, queryTerm{label: &lhs, value: &rhs}) + items = append(items, &hasTerm{label: lhs, value: rhs}) } else if lhs, rhs, ok := try(part, "="); ok { - items = append(items, queryTerm{label: &lhs, value: &rhs}) + items = append(items, &hasTerm{label: lhs, value: rhs}) } else { return nil, fmt.Errorf("invalid label query: '%s'; can't understand '%s'", query, part) } } if len(items) == 1 { - return &items[0], nil + return items[0], nil } - return &queryTerm{and: items}, nil -} - -// Returns this query as a string in a form that ParseQuery can parse. -func (l *queryTerm) String() (out string) { - if len(l.and) > 0 { - for _, part := range l.and { - if out != "" { - out += "," - } - out += part.String() - } - return - } else if l.label != nil && l.value != nil { - op := "=" - if l.not { - op = "!=" - } - return fmt.Sprintf("%v%v%v", *l.label, op, *l.value) - } - return "" + return andTerm(items), nil }