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 { filters := p.Filters() for i := range filters { if err := c.processFilter(&filters[i], 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.Value()] = 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.Value()] case OpGE: return attr >= c.numCache[f.Value()] case OpLT: return attr < c.numCache[f.Value()] case OpLE: return attr <= c.numCache[f.Value()] 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, len(fs)) for i := range fs { res[i] = *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, len(fs)) for i := range fs { fsV2[i] = *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) }