273 lines
5.9 KiB
Go
273 lines
5.9 KiB
Go
|
package netmap
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"strconv"
|
||
|
|
||
|
"github.com/nspcc-dev/neofs-api-go/v2/netmap"
|
||
|
)
|
||
|
|
||
|
// Filter represents v2-compatible netmap filter.
|
||
|
type Filter netmap.Filter
|
||
|
|
||
|
// MainFilterName is a name of the filter
|
||
|
// which points to the whole netmap.
|
||
|
const MainFilterName = "*"
|
||
|
|
||
|
// applyFilter applies named filter to b.
|
||
|
func (c *Context) applyFilter(name string, b *Node) bool {
|
||
|
return name == MainFilterName || c.match(c.Filters[name], b)
|
||
|
}
|
||
|
|
||
|
// processFilters processes filters and returns error is any of them is invalid.
|
||
|
func (c *Context) processFilters(p *PlacementPolicy) error {
|
||
|
for _, f := range p.Filters() {
|
||
|
if err := c.processFilter(f, true); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (c *Context) processFilter(f *Filter, top bool) error {
|
||
|
if f == nil {
|
||
|
return fmt.Errorf("%w: FILTER", ErrMissingField)
|
||
|
}
|
||
|
|
||
|
if f.Name() == MainFilterName {
|
||
|
return fmt.Errorf("%w: '*' is reserved", ErrInvalidFilterName)
|
||
|
}
|
||
|
|
||
|
if top && f.Name() == "" {
|
||
|
return ErrUnnamedTopFilter
|
||
|
}
|
||
|
|
||
|
if !top && f.Name() != "" && c.Filters[f.Name()] == nil {
|
||
|
return fmt.Errorf("%w: '%s'", ErrFilterNotFound, f.Name())
|
||
|
}
|
||
|
|
||
|
switch f.Operation() {
|
||
|
case OpAND, OpOR:
|
||
|
for _, flt := range f.InnerFilters() {
|
||
|
if err := c.processFilter(flt, false); err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
default:
|
||
|
if len(f.InnerFilters()) != 0 {
|
||
|
return ErrNonEmptyFilters
|
||
|
} else if !top && f.Name() != "" { // named reference
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
switch f.Operation() {
|
||
|
case OpEQ, OpNE:
|
||
|
case OpGT, OpGE, OpLT, OpLE:
|
||
|
n, err := strconv.ParseUint(f.Value(), 10, 64)
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("%w: '%s'", ErrInvalidNumber, f.Value())
|
||
|
}
|
||
|
|
||
|
c.numCache[f] = n
|
||
|
default:
|
||
|
return fmt.Errorf("%w: %s", ErrInvalidFilterOp, f.Operation())
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if top {
|
||
|
c.Filters[f.Name()] = f
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// match matches f against b. It returns no errors because
|
||
|
// filter should have been parsed during context creation
|
||
|
// and missing node properties are considered as a regular fail.
|
||
|
func (c *Context) match(f *Filter, b *Node) bool {
|
||
|
switch f.Operation() {
|
||
|
case OpAND, OpOR:
|
||
|
for _, lf := range f.InnerFilters() {
|
||
|
if lf.Name() != "" {
|
||
|
lf = c.Filters[lf.Name()]
|
||
|
}
|
||
|
|
||
|
ok := c.match(lf, b)
|
||
|
if ok == (f.Operation() == OpOR) {
|
||
|
return ok
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return f.Operation() == OpAND
|
||
|
default:
|
||
|
return c.matchKeyValue(f, b)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (c *Context) matchKeyValue(f *Filter, b *Node) bool {
|
||
|
switch f.Operation() {
|
||
|
case OpEQ:
|
||
|
return b.Attribute(f.Key()) == f.Value()
|
||
|
case OpNE:
|
||
|
return b.Attribute(f.Key()) != f.Value()
|
||
|
default:
|
||
|
var attr uint64
|
||
|
|
||
|
switch f.Key() {
|
||
|
case AttrPrice:
|
||
|
attr = b.Price
|
||
|
case AttrCapacity:
|
||
|
attr = b.Capacity
|
||
|
default:
|
||
|
var err error
|
||
|
|
||
|
attr, err = strconv.ParseUint(b.Attribute(f.Key()), 10, 64)
|
||
|
if err != nil {
|
||
|
// Note: because filters are somewhat independent from nodes attributes,
|
||
|
// We don't report an error here, and fail filter instead.
|
||
|
return false
|
||
|
}
|
||
|
}
|
||
|
|
||
|
switch f.Operation() {
|
||
|
case OpGT:
|
||
|
return attr > c.numCache[f]
|
||
|
case OpGE:
|
||
|
return attr >= c.numCache[f]
|
||
|
case OpLT:
|
||
|
return attr < c.numCache[f]
|
||
|
case OpLE:
|
||
|
return attr <= c.numCache[f]
|
||
|
default:
|
||
|
// do nothing and return false
|
||
|
}
|
||
|
}
|
||
|
// will not happen if context was created from f (maybe panic?)
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
// NewFilter creates and returns new Filter instance.
|
||
|
//
|
||
|
// Defaults:
|
||
|
// - name: "";
|
||
|
// - key: "";
|
||
|
// - value: "";
|
||
|
// - operation: 0;
|
||
|
// - filters: nil.
|
||
|
func NewFilter() *Filter {
|
||
|
return NewFilterFromV2(new(netmap.Filter))
|
||
|
}
|
||
|
|
||
|
// NewFilterFromV2 converts v2 Filter to Filter.
|
||
|
//
|
||
|
// Nil netmap.Filter converts to nil.
|
||
|
func NewFilterFromV2(f *netmap.Filter) *Filter {
|
||
|
return (*Filter)(f)
|
||
|
}
|
||
|
|
||
|
// ToV2 converts Filter to v2 Filter.
|
||
|
//
|
||
|
// Nil Filter converts to nil.
|
||
|
func (f *Filter) ToV2() *netmap.Filter {
|
||
|
return (*netmap.Filter)(f)
|
||
|
}
|
||
|
|
||
|
// Key returns key to filter.
|
||
|
func (f *Filter) Key() string {
|
||
|
return (*netmap.Filter)(f).GetKey()
|
||
|
}
|
||
|
|
||
|
// SetKey sets key to filter.
|
||
|
func (f *Filter) SetKey(key string) {
|
||
|
(*netmap.Filter)(f).SetKey(key)
|
||
|
}
|
||
|
|
||
|
// Value returns value to match.
|
||
|
func (f *Filter) Value() string {
|
||
|
return (*netmap.Filter)(f).GetValue()
|
||
|
}
|
||
|
|
||
|
// SetValue sets value to match.
|
||
|
func (f *Filter) SetValue(val string) {
|
||
|
(*netmap.Filter)(f).SetValue(val)
|
||
|
}
|
||
|
|
||
|
// Name returns filter name.
|
||
|
func (f *Filter) Name() string {
|
||
|
return (*netmap.Filter)(f).GetName()
|
||
|
}
|
||
|
|
||
|
// SetName sets filter name.
|
||
|
func (f *Filter) SetName(name string) {
|
||
|
(*netmap.Filter)(f).SetName(name)
|
||
|
}
|
||
|
|
||
|
// Operation returns filtering operation.
|
||
|
func (f *Filter) Operation() Operation {
|
||
|
return OperationFromV2(
|
||
|
(*netmap.Filter)(f).GetOp())
|
||
|
}
|
||
|
|
||
|
// SetOperation sets filtering operation.
|
||
|
func (f *Filter) SetOperation(op Operation) {
|
||
|
(*netmap.Filter)(f).SetOp(op.ToV2())
|
||
|
}
|
||
|
|
||
|
func filtersFromV2(fs []*netmap.Filter) []*Filter {
|
||
|
if fs == nil {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
res := make([]*Filter, 0, len(fs))
|
||
|
|
||
|
for i := range fs {
|
||
|
res = append(res, NewFilterFromV2(fs[i]))
|
||
|
}
|
||
|
|
||
|
return res
|
||
|
}
|
||
|
|
||
|
// InnerFilters returns list of inner filters.
|
||
|
func (f *Filter) InnerFilters() []*Filter {
|
||
|
return filtersFromV2((*netmap.Filter)(f).GetFilters())
|
||
|
}
|
||
|
|
||
|
func filtersToV2(fs []*Filter) (fsV2 []*netmap.Filter) {
|
||
|
if fs != nil {
|
||
|
fsV2 = make([]*netmap.Filter, 0, len(fs))
|
||
|
|
||
|
for i := range fs {
|
||
|
fsV2 = append(fsV2, fs[i].ToV2())
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// SetInnerFilters sets list of inner filters.
|
||
|
func (f *Filter) SetInnerFilters(fs ...*Filter) {
|
||
|
(*netmap.Filter)(f).
|
||
|
SetFilters(filtersToV2(fs))
|
||
|
}
|
||
|
|
||
|
// Marshal marshals Filter into a protobuf binary form.
|
||
|
func (f *Filter) Marshal() ([]byte, error) {
|
||
|
return (*netmap.Filter)(f).StableMarshal(nil)
|
||
|
}
|
||
|
|
||
|
// Unmarshal unmarshals protobuf binary representation of Filter.
|
||
|
func (f *Filter) Unmarshal(data []byte) error {
|
||
|
return (*netmap.Filter)(f).Unmarshal(data)
|
||
|
}
|
||
|
|
||
|
// MarshalJSON encodes Filter to protobuf JSON format.
|
||
|
func (f *Filter) MarshalJSON() ([]byte, error) {
|
||
|
return (*netmap.Filter)(f).MarshalJSON()
|
||
|
}
|
||
|
|
||
|
// UnmarshalJSON decodes Filter from protobuf JSON format.
|
||
|
func (f *Filter) UnmarshalJSON(data []byte) error {
|
||
|
return (*netmap.Filter)(f).UnmarshalJSON(data)
|
||
|
}
|