blob: 925e4e633a23360c4c691c077a8a61f66dee958b [file] [log] [blame]
// Copyright 2016 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package filter
import (
"strings"
"v.io/v23/glob"
wire "v.io/v23/services/syncbase"
pubutil "v.io/v23/syncbase/util"
"v.io/v23/verror"
)
var (
errGlobParseFailed = verror.Register(pkgPath+".errGlobParseFailed", verror.NoRetry, "{1:}{2:} glob parse failed{:_}")
)
// globFilter matches using a glob of the format <encodedCxId>/<decodedKey>
// with v23.Glob semantics. The key is decoded to allow matching name
// components within the key separated by slashes using '*'.
type globFilter struct {
cxId *glob.Element
rowKey *glob.Glob
}
var _ CollectionRowFilter = (*globFilter)(nil)
func NewGlobFilter(pattern string) (*globFilter, error) {
fullGlob, err := glob.Parse(pattern)
if err != nil {
return nil, verror.New(errGlobParseFailed, nil, err)
}
// TODO(ivanpi): Check that collection pattern can match a valid collection
// name, e.g. contains a comma or wildcard that could be a comma.
return &globFilter{
cxId: fullGlob.Head(),
rowKey: fullGlob.Tail(),
}, nil
}
func (g *globFilter) CollectionMatches(cxId wire.Id) bool {
return g.cxId.Match(pubutil.EncodeId(cxId))
}
func (g *globFilter) RowMatches(cxId wire.Id, key string) bool {
if !g.CollectionMatches(cxId) {
return false
}
gIt := g.rowKey
// Globs are matched by iterating over slash-separated key segments and
// matching each segment to the appropriate glob component. Globs are
// iterated over by removing the first component after it has been matched.
// Recursive globs allow indefinite iteration after glob components have
// been exhausted to match subsequent key components, while non-recursive
// globs fail.
for _, kPart := range strings.Split(key, "/") {
if !gIt.Head().Match(kPart) {
return false
}
gIt = gIt.Tail()
}
// All glob components must be matched, otherwise the key is just a prefix of
// a valid match.
return gIt.Len() == 0
}