commit af3ad670813ec4e60de61baeb2a27bbcedabf009 Author: Adphi Date: Tue Jun 1 20:15:36 2021 +0200 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1fb3c29 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea +test.* diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..752b572 --- /dev/null +++ b/Makefile @@ -0,0 +1,11 @@ + + + +.PHONY: proto +proto: + @protoc -I. --go_out=paths=source_relative:. ./*.proto + + +.PHONY: proto-test +proto-test: + @protoc -I. --go_out=paths=source_relative:. tests/pb/*.proto diff --git a/field_filter.extensions.go b/field_filter.extensions.go new file mode 100644 index 0000000..40477f4 --- /dev/null +++ b/field_filter.extensions.go @@ -0,0 +1,189 @@ +package protofilters + + + +func New(filters ...*FieldFilter) *FieldsFilter { + out := make(map[string]*Filter) + for _, v := range filters { + if v == nil { + continue + } + out[v.Field] = v.Filter + } + return &FieldsFilter{Filters: out} +} + +func StringEquals(s string) *Filter { + return newStringFilter(&StringFilter{ + Condition: &StringFilter_Equals{ + Equals: s, + }, + }) +} + +func StringNotEquals(s string) *Filter { + return newStringFilter(&StringFilter{ + Condition: &StringFilter_Equals{ + Equals: s, + }, + Not: true, + }) +} + +func StringNotIEquals(s string) *Filter { + return newStringFilter(&StringFilter{ + Condition: &StringFilter_Equals{ + Equals: s, + }, + CaseInsensitive: true, + Not: true, + }) +} + +func StringIEquals(s string) *Filter { + return newStringFilter(&StringFilter{ + Condition: &StringFilter_Equals{ + Equals: s, + }, + CaseInsensitive: true, + }) +} + +func StringRegex(s string) *Filter { + return newStringFilter( + &StringFilter{ + Condition: &StringFilter_Regex{ + Regex: s, + }, + }, + ) +} + +func StringNotRegex(s string) *Filter { + return newStringFilter( + &StringFilter{ + Condition: &StringFilter_Regex{ + Regex: s, + }, + Not: true, + }, + ) +} + +func newStringFilter(f *StringFilter) *Filter { + return &Filter{ + Match: &Filter_String_{ + String_: f, + }, + } +} + +func StringIN(s ...string) *Filter { + return newStringFilter( + &StringFilter{ + Condition: &StringFilter_In_{ + In: &StringFilter_In{ + Values: s, + }, + }, + }) +} + +func StringNotIN(s ...string) *Filter { + return newStringFilter( + &StringFilter{ + Condition: &StringFilter_In_{ + In: &StringFilter_In{ + Values: s, + }, + }, + Not: true, + }) +} + +func newNumberFilter(f *NumberFilter) *Filter { + return &Filter{ + Match: &Filter_Number{ + Number: f, + }, + } +} + +func NumberEquals(n float64) *Filter { + return newNumberFilter( + &NumberFilter{ + Condition: &NumberFilter_Equals{ + Equals: n, + }, + }, + ) +} + +func NumberNotEquals(n float64) *Filter { + return newNumberFilter( + &NumberFilter{ + Condition: &NumberFilter_Equals{ + Equals: n, + }, + Not: true, + }, + ) +} + +func NumberIN(n ...float64) *Filter { + return newNumberFilter( + &NumberFilter{ + Condition: &NumberFilter_In_{ + In: &NumberFilter_In{ + Values: n, + }, + }, + }, + ) +} + +func NumberNotIN(n ...float64) *Filter { + return newNumberFilter( + &NumberFilter{ + Condition: &NumberFilter_In_{ + In: &NumberFilter_In{ + Values: n, + }, + }, + Not: true, + }, + ) +} + + +func True() *Filter { + return NewBoolFilter(&BoolFilter{Equals: true}) +} + +func False() *Filter { + return NewBoolFilter(&BoolFilter{Equals: false}) +} + +func NewBoolFilter(f *BoolFilter) *Filter { + return &Filter{ + Match: &Filter_Bool{ + Bool: f, + }, + } +} + +func Null() *Filter { + return NewNullFilter(&NullFilter{Not: false}) +} + +func NotNull() *Filter { + return NewNullFilter(&NullFilter{Not: true}) +} + +func NewNullFilter(f *NullFilter) *Filter { + return &Filter{ + Match: &Filter_Null{ + Null: f, + }, + } +} diff --git a/field_filter.pb.go b/field_filter.pb.go new file mode 100644 index 0000000..50f107e --- /dev/null +++ b/field_filter.pb.go @@ -0,0 +1,915 @@ +// Copyright 2021 Linka Cloud All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Linka Cloud nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.26.0 +// protoc v3.17.1 +// source: field_filter.proto + +package protofilters + +import ( + reflect "reflect" + sync "sync" + + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type FieldsFilter struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The set of field mask paths. + Filters map[string]*Filter `protobuf:"bytes,1,rep,name=filters,proto3" json:"filters,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` +} + +func (x *FieldsFilter) Reset() { + *x = FieldsFilter{} + if protoimpl.UnsafeEnabled { + mi := &file_field_filter_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FieldsFilter) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FieldsFilter) ProtoMessage() {} + +func (x *FieldsFilter) ProtoReflect() protoreflect.Message { + mi := &file_field_filter_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FieldsFilter.ProtoReflect.Descriptor instead. +func (*FieldsFilter) Descriptor() ([]byte, []int) { + return file_field_filter_proto_rawDescGZIP(), []int{0} +} + +func (x *FieldsFilter) GetFilters() map[string]*Filter { + if x != nil { + return x.Filters + } + return nil +} + +type FieldFilter struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Field string `protobuf:"bytes,1,opt,name=field,proto3" json:"field,omitempty"` + Filter *Filter `protobuf:"bytes,2,opt,name=filter,proto3" json:"filter,omitempty"` +} + +func (x *FieldFilter) Reset() { + *x = FieldFilter{} + if protoimpl.UnsafeEnabled { + mi := &file_field_filter_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FieldFilter) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FieldFilter) ProtoMessage() {} + +func (x *FieldFilter) ProtoReflect() protoreflect.Message { + mi := &file_field_filter_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FieldFilter.ProtoReflect.Descriptor instead. +func (*FieldFilter) Descriptor() ([]byte, []int) { + return file_field_filter_proto_rawDescGZIP(), []int{1} +} + +func (x *FieldFilter) GetField() string { + if x != nil { + return x.Field + } + return "" +} + +func (x *FieldFilter) GetFilter() *Filter { + if x != nil { + return x.Filter + } + return nil +} + +type Filter struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Match: + // *Filter_String_ + // *Filter_Number + // *Filter_Bool + // *Filter_Null + Match isFilter_Match `protobuf_oneof:"match"` +} + +func (x *Filter) Reset() { + *x = Filter{} + if protoimpl.UnsafeEnabled { + mi := &file_field_filter_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Filter) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Filter) ProtoMessage() {} + +func (x *Filter) ProtoReflect() protoreflect.Message { + mi := &file_field_filter_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Filter.ProtoReflect.Descriptor instead. +func (*Filter) Descriptor() ([]byte, []int) { + return file_field_filter_proto_rawDescGZIP(), []int{2} +} + +func (m *Filter) GetMatch() isFilter_Match { + if m != nil { + return m.Match + } + return nil +} + +func (x *Filter) GetString_() *StringFilter { + if x, ok := x.GetMatch().(*Filter_String_); ok { + return x.String_ + } + return nil +} + +func (x *Filter) GetNumber() *NumberFilter { + if x, ok := x.GetMatch().(*Filter_Number); ok { + return x.Number + } + return nil +} + +func (x *Filter) GetBool() *BoolFilter { + if x, ok := x.GetMatch().(*Filter_Bool); ok { + return x.Bool + } + return nil +} + +func (x *Filter) GetNull() *NullFilter { + if x, ok := x.GetMatch().(*Filter_Null); ok { + return x.Null + } + return nil +} + +type isFilter_Match interface { + isFilter_Match() +} + +type Filter_String_ struct { + String_ *StringFilter `protobuf:"bytes,1,opt,name=string,proto3,oneof"` +} + +type Filter_Number struct { + Number *NumberFilter `protobuf:"bytes,2,opt,name=number,proto3,oneof"` +} + +type Filter_Bool struct { + Bool *BoolFilter `protobuf:"bytes,3,opt,name=bool,proto3,oneof"` +} + +type Filter_Null struct { + Null *NullFilter `protobuf:"bytes,4,opt,name=null,proto3,oneof"` +} + +func (*Filter_String_) isFilter_Match() {} + +func (*Filter_Number) isFilter_Match() {} + +func (*Filter_Bool) isFilter_Match() {} + +func (*Filter_Null) isFilter_Match() {} + +type StringFilter struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Condition: + // *StringFilter_Equals + // *StringFilter_Regex + // *StringFilter_In_ + Condition isStringFilter_Condition `protobuf_oneof:"condition"` + Not bool `protobuf:"varint,4,opt,name=not,proto3" json:"not,omitempty"` + CaseInsensitive bool `protobuf:"varint,5,opt,name=case_insensitive,json=caseInsensitive,proto3" json:"case_insensitive,omitempty"` +} + +func (x *StringFilter) Reset() { + *x = StringFilter{} + if protoimpl.UnsafeEnabled { + mi := &file_field_filter_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StringFilter) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StringFilter) ProtoMessage() {} + +func (x *StringFilter) ProtoReflect() protoreflect.Message { + mi := &file_field_filter_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StringFilter.ProtoReflect.Descriptor instead. +func (*StringFilter) Descriptor() ([]byte, []int) { + return file_field_filter_proto_rawDescGZIP(), []int{3} +} + +func (m *StringFilter) GetCondition() isStringFilter_Condition { + if m != nil { + return m.Condition + } + return nil +} + +func (x *StringFilter) GetEquals() string { + if x, ok := x.GetCondition().(*StringFilter_Equals); ok { + return x.Equals + } + return "" +} + +func (x *StringFilter) GetRegex() string { + if x, ok := x.GetCondition().(*StringFilter_Regex); ok { + return x.Regex + } + return "" +} + +func (x *StringFilter) GetIn() *StringFilter_In { + if x, ok := x.GetCondition().(*StringFilter_In_); ok { + return x.In + } + return nil +} + +func (x *StringFilter) GetNot() bool { + if x != nil { + return x.Not + } + return false +} + +func (x *StringFilter) GetCaseInsensitive() bool { + if x != nil { + return x.CaseInsensitive + } + return false +} + +type isStringFilter_Condition interface { + isStringFilter_Condition() +} + +type StringFilter_Equals struct { + Equals string `protobuf:"bytes,1,opt,name=equals,proto3,oneof"` +} + +type StringFilter_Regex struct { + Regex string `protobuf:"bytes,2,opt,name=regex,proto3,oneof"` +} + +type StringFilter_In_ struct { + In *StringFilter_In `protobuf:"bytes,3,opt,name=in,proto3,oneof"` +} + +func (*StringFilter_Equals) isStringFilter_Condition() {} + +func (*StringFilter_Regex) isStringFilter_Condition() {} + +func (*StringFilter_In_) isStringFilter_Condition() {} + +type NumberFilter struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Condition: + // *NumberFilter_Equals + // *NumberFilter_In_ + Condition isNumberFilter_Condition `protobuf_oneof:"condition"` + Not bool `protobuf:"varint,4,opt,name=not,proto3" json:"not,omitempty"` +} + +func (x *NumberFilter) Reset() { + *x = NumberFilter{} + if protoimpl.UnsafeEnabled { + mi := &file_field_filter_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NumberFilter) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NumberFilter) ProtoMessage() {} + +func (x *NumberFilter) ProtoReflect() protoreflect.Message { + mi := &file_field_filter_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NumberFilter.ProtoReflect.Descriptor instead. +func (*NumberFilter) Descriptor() ([]byte, []int) { + return file_field_filter_proto_rawDescGZIP(), []int{4} +} + +func (m *NumberFilter) GetCondition() isNumberFilter_Condition { + if m != nil { + return m.Condition + } + return nil +} + +func (x *NumberFilter) GetEquals() float64 { + if x, ok := x.GetCondition().(*NumberFilter_Equals); ok { + return x.Equals + } + return 0 +} + +func (x *NumberFilter) GetIn() *NumberFilter_In { + if x, ok := x.GetCondition().(*NumberFilter_In_); ok { + return x.In + } + return nil +} + +func (x *NumberFilter) GetNot() bool { + if x != nil { + return x.Not + } + return false +} + +type isNumberFilter_Condition interface { + isNumberFilter_Condition() +} + +type NumberFilter_Equals struct { + Equals float64 `protobuf:"fixed64,1,opt,name=equals,proto3,oneof"` +} + +type NumberFilter_In_ struct { + In *NumberFilter_In `protobuf:"bytes,2,opt,name=in,proto3,oneof"` +} + +func (*NumberFilter_Equals) isNumberFilter_Condition() {} + +func (*NumberFilter_In_) isNumberFilter_Condition() {} + +type NullFilter struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Not bool `protobuf:"varint,1,opt,name=not,proto3" json:"not,omitempty"` +} + +func (x *NullFilter) Reset() { + *x = NullFilter{} + if protoimpl.UnsafeEnabled { + mi := &file_field_filter_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NullFilter) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NullFilter) ProtoMessage() {} + +func (x *NullFilter) ProtoReflect() protoreflect.Message { + mi := &file_field_filter_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NullFilter.ProtoReflect.Descriptor instead. +func (*NullFilter) Descriptor() ([]byte, []int) { + return file_field_filter_proto_rawDescGZIP(), []int{5} +} + +func (x *NullFilter) GetNot() bool { + if x != nil { + return x.Not + } + return false +} + +type BoolFilter struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Equals bool `protobuf:"varint,1,opt,name=equals,proto3" json:"equals,omitempty"` +} + +func (x *BoolFilter) Reset() { + *x = BoolFilter{} + if protoimpl.UnsafeEnabled { + mi := &file_field_filter_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BoolFilter) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BoolFilter) ProtoMessage() {} + +func (x *BoolFilter) ProtoReflect() protoreflect.Message { + mi := &file_field_filter_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BoolFilter.ProtoReflect.Descriptor instead. +func (*BoolFilter) Descriptor() ([]byte, []int) { + return file_field_filter_proto_rawDescGZIP(), []int{6} +} + +func (x *BoolFilter) GetEquals() bool { + if x != nil { + return x.Equals + } + return false +} + +type StringFilter_In struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Values []string `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty"` +} + +func (x *StringFilter_In) Reset() { + *x = StringFilter_In{} + if protoimpl.UnsafeEnabled { + mi := &file_field_filter_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *StringFilter_In) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*StringFilter_In) ProtoMessage() {} + +func (x *StringFilter_In) ProtoReflect() protoreflect.Message { + mi := &file_field_filter_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use StringFilter_In.ProtoReflect.Descriptor instead. +func (*StringFilter_In) Descriptor() ([]byte, []int) { + return file_field_filter_proto_rawDescGZIP(), []int{3, 0} +} + +func (x *StringFilter_In) GetValues() []string { + if x != nil { + return x.Values + } + return nil +} + +type NumberFilter_In struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Values []float64 `protobuf:"fixed64,1,rep,packed,name=values,proto3" json:"values,omitempty"` +} + +func (x *NumberFilter_In) Reset() { + *x = NumberFilter_In{} + if protoimpl.UnsafeEnabled { + mi := &file_field_filter_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NumberFilter_In) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NumberFilter_In) ProtoMessage() {} + +func (x *NumberFilter_In) ProtoReflect() protoreflect.Message { + mi := &file_field_filter_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use NumberFilter_In.ProtoReflect.Descriptor instead. +func (*NumberFilter_In) Descriptor() ([]byte, []int) { + return file_field_filter_proto_rawDescGZIP(), []int{4, 0} +} + +func (x *NumberFilter_In) GetValues() []float64 { + if x != nil { + return x.Values + } + return nil +} + +var File_field_filter_proto protoreflect.FileDescriptor + +var file_field_filter_proto_rawDesc = []byte{ + 0x0a, 0x12, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x18, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x2e, 0x63, 0x6c, 0x6f, 0x75, + 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x22, 0xbb, + 0x01, 0x0a, 0x0c, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, + 0x4d, 0x0a, 0x07, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x33, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x2e, 0x46, 0x69, 0x65, 0x6c, + 0x64, 0x73, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x1a, 0x5c, + 0x0a, 0x0c, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x36, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x20, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x2e, 0x46, 0x69, 0x6c, 0x74, 0x65, + 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x5d, 0x0a, 0x0b, + 0x46, 0x69, 0x65, 0x6c, 0x64, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x66, + 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, + 0x64, 0x12, 0x38, 0x0a, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x20, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x2e, 0x46, 0x69, 0x6c, + 0x74, 0x65, 0x72, 0x52, 0x06, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x22, 0x8d, 0x02, 0x0a, 0x06, + 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x40, 0x0a, 0x06, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x2e, 0x63, + 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, + 0x73, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x48, 0x00, + 0x52, 0x06, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x40, 0x0a, 0x06, 0x6e, 0x75, 0x6d, 0x62, + 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, + 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x66, 0x69, 0x6c, 0x74, + 0x65, 0x72, 0x73, 0x2e, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, + 0x48, 0x00, 0x52, 0x06, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x3a, 0x0a, 0x04, 0x62, 0x6f, + 0x6f, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, + 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x66, 0x69, 0x6c, 0x74, + 0x65, 0x72, 0x73, 0x2e, 0x42, 0x6f, 0x6f, 0x6c, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x48, 0x00, + 0x52, 0x04, 0x62, 0x6f, 0x6f, 0x6c, 0x12, 0x3a, 0x0a, 0x04, 0x6e, 0x75, 0x6c, 0x6c, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x2e, 0x63, 0x6c, 0x6f, + 0x75, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x2e, + 0x4e, 0x75, 0x6c, 0x6c, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x48, 0x00, 0x52, 0x04, 0x6e, 0x75, + 0x6c, 0x6c, 0x42, 0x07, 0x0a, 0x05, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x22, 0xe5, 0x01, 0x0a, 0x0c, + 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x06, + 0x65, 0x71, 0x75, 0x61, 0x6c, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x06, + 0x65, 0x71, 0x75, 0x61, 0x6c, 0x73, 0x12, 0x16, 0x0a, 0x05, 0x72, 0x65, 0x67, 0x65, 0x78, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x05, 0x72, 0x65, 0x67, 0x65, 0x78, 0x12, 0x3b, + 0x0a, 0x02, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x6c, 0x69, 0x6e, + 0x6b, 0x61, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x66, 0x69, + 0x6c, 0x74, 0x65, 0x72, 0x73, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x46, 0x69, 0x6c, 0x74, + 0x65, 0x72, 0x2e, 0x49, 0x6e, 0x48, 0x00, 0x52, 0x02, 0x69, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x6e, + 0x6f, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x6e, 0x6f, 0x74, 0x12, 0x29, 0x0a, + 0x10, 0x63, 0x61, 0x73, 0x65, 0x5f, 0x69, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, + 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x63, 0x61, 0x73, 0x65, 0x49, 0x6e, 0x73, + 0x65, 0x6e, 0x73, 0x69, 0x74, 0x69, 0x76, 0x65, 0x1a, 0x1c, 0x0a, 0x02, 0x49, 0x6e, 0x12, 0x16, + 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x42, 0x0b, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x64, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x22, 0xa2, 0x01, 0x0a, 0x0c, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x46, 0x69, + 0x6c, 0x74, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x06, 0x65, 0x71, 0x75, 0x61, 0x6c, 0x73, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x01, 0x48, 0x00, 0x52, 0x06, 0x65, 0x71, 0x75, 0x61, 0x6c, 0x73, 0x12, 0x3b, + 0x0a, 0x02, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x6c, 0x69, 0x6e, + 0x6b, 0x61, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x66, 0x69, + 0x6c, 0x74, 0x65, 0x72, 0x73, 0x2e, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x46, 0x69, 0x6c, 0x74, + 0x65, 0x72, 0x2e, 0x49, 0x6e, 0x48, 0x00, 0x52, 0x02, 0x69, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x6e, + 0x6f, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x6e, 0x6f, 0x74, 0x1a, 0x1c, 0x0a, + 0x02, 0x49, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x01, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x42, 0x0b, 0x0a, 0x09, 0x63, + 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x1e, 0x0a, 0x0a, 0x4e, 0x75, 0x6c, 0x6c, + 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x6e, 0x6f, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x03, 0x6e, 0x6f, 0x74, 0x22, 0x24, 0x0a, 0x0a, 0x42, 0x6f, 0x6f, 0x6c, + 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x65, 0x71, 0x75, 0x61, 0x6c, 0x73, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x65, 0x71, 0x75, 0x61, 0x6c, 0x73, 0x42, 0x2f, + 0x50, 0x01, 0x5a, 0x28, 0x67, 0x6f, 0x2e, 0x6c, 0x69, 0x6e, 0x6b, 0x61, 0x2e, 0x63, 0x6c, 0x6f, + 0x75, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0x3b, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x73, 0xf8, 0x01, 0x01, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_field_filter_proto_rawDescOnce sync.Once + file_field_filter_proto_rawDescData = file_field_filter_proto_rawDesc +) + +func file_field_filter_proto_rawDescGZIP() []byte { + file_field_filter_proto_rawDescOnce.Do(func() { + file_field_filter_proto_rawDescData = protoimpl.X.CompressGZIP(file_field_filter_proto_rawDescData) + }) + return file_field_filter_proto_rawDescData +} + +var file_field_filter_proto_msgTypes = make([]protoimpl.MessageInfo, 10) +var file_field_filter_proto_goTypes = []interface{}{ + (*FieldsFilter)(nil), // 0: linka.cloud.protofilters.FieldsFilter + (*FieldFilter)(nil), // 1: linka.cloud.protofilters.FieldFilter + (*Filter)(nil), // 2: linka.cloud.protofilters.Filter + (*StringFilter)(nil), // 3: linka.cloud.protofilters.StringFilter + (*NumberFilter)(nil), // 4: linka.cloud.protofilters.NumberFilter + (*NullFilter)(nil), // 5: linka.cloud.protofilters.NullFilter + (*BoolFilter)(nil), // 6: linka.cloud.protofilters.BoolFilter + nil, // 7: linka.cloud.protofilters.FieldsFilter.FiltersEntry + (*StringFilter_In)(nil), // 8: linka.cloud.protofilters.StringFilter.In + (*NumberFilter_In)(nil), // 9: linka.cloud.protofilters.NumberFilter.In +} +var file_field_filter_proto_depIdxs = []int32{ + 7, // 0: linka.cloud.protofilters.FieldsFilter.filters:type_name -> linka.cloud.protofilters.FieldsFilter.FiltersEntry + 2, // 1: linka.cloud.protofilters.FieldFilter.filter:type_name -> linka.cloud.protofilters.Filter + 3, // 2: linka.cloud.protofilters.Filter.string:type_name -> linka.cloud.protofilters.StringFilter + 4, // 3: linka.cloud.protofilters.Filter.number:type_name -> linka.cloud.protofilters.NumberFilter + 6, // 4: linka.cloud.protofilters.Filter.bool:type_name -> linka.cloud.protofilters.BoolFilter + 5, // 5: linka.cloud.protofilters.Filter.null:type_name -> linka.cloud.protofilters.NullFilter + 8, // 6: linka.cloud.protofilters.StringFilter.in:type_name -> linka.cloud.protofilters.StringFilter.In + 9, // 7: linka.cloud.protofilters.NumberFilter.in:type_name -> linka.cloud.protofilters.NumberFilter.In + 2, // 8: linka.cloud.protofilters.FieldsFilter.FiltersEntry.value:type_name -> linka.cloud.protofilters.Filter + 9, // [9:9] is the sub-list for method output_type + 9, // [9:9] is the sub-list for method input_type + 9, // [9:9] is the sub-list for extension type_name + 9, // [9:9] is the sub-list for extension extendee + 0, // [0:9] is the sub-list for field type_name +} + +func init() { file_field_filter_proto_init() } +func file_field_filter_proto_init() { + if File_field_filter_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_field_filter_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FieldsFilter); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_field_filter_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FieldFilter); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_field_filter_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Filter); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_field_filter_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StringFilter); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_field_filter_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NumberFilter); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_field_filter_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NullFilter); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_field_filter_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BoolFilter); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_field_filter_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*StringFilter_In); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_field_filter_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NumberFilter_In); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_field_filter_proto_msgTypes[2].OneofWrappers = []interface{}{ + (*Filter_String_)(nil), + (*Filter_Number)(nil), + (*Filter_Bool)(nil), + (*Filter_Null)(nil), + } + file_field_filter_proto_msgTypes[3].OneofWrappers = []interface{}{ + (*StringFilter_Equals)(nil), + (*StringFilter_Regex)(nil), + (*StringFilter_In_)(nil), + } + file_field_filter_proto_msgTypes[4].OneofWrappers = []interface{}{ + (*NumberFilter_Equals)(nil), + (*NumberFilter_In_)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_field_filter_proto_rawDesc, + NumEnums: 0, + NumMessages: 10, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_field_filter_proto_goTypes, + DependencyIndexes: file_field_filter_proto_depIdxs, + MessageInfos: file_field_filter_proto_msgTypes, + }.Build() + File_field_filter_proto = out.File + file_field_filter_proto_rawDesc = nil + file_field_filter_proto_goTypes = nil + file_field_filter_proto_depIdxs = nil +} diff --git a/field_filter.proto b/field_filter.proto new file mode 100644 index 0000000..4492b8c --- /dev/null +++ b/field_filter.proto @@ -0,0 +1,88 @@ +// Copyright 2021 Linka Cloud All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Linka Cloud nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package linka.cloud.protofilters; + +//option csharp_namespace = "LinkaCloud.ProtoDynamic.WellKnownTypes"; +//option java_package = "cloud.linka.protodynamic"; +//option java_outer_classname = "FieldFilterProto"; +option java_multiple_files = true; +//option objc_class_prefix = "GPB"; +option go_package = "go.linka.cloud/protofilters;protofilters"; +option cc_enable_arenas = true; + + +message FieldsFilter { + // The set of field mask paths. + map filters = 1; +} + +message FieldFilter { + string field = 1; + Filter filter = 2; +} + +message Filter { + oneof match { + StringFilter string = 1; + NumberFilter number = 2; + BoolFilter bool = 3; + NullFilter null = 4; + } +} + +message StringFilter { + message In { + repeated string values = 1; + } + oneof condition { + string equals = 1; + string regex = 2; + In in = 3; + } + bool not = 4; + bool case_insensitive = 5; +} +message NumberFilter { + message In { + repeated double values = 1; + } + oneof condition { + double equals = 1; + In in = 2; + } + bool not = 4; +} +message NullFilter { + bool not = 1; +} +message BoolFilter { + bool equals = 1; +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..60a0142 --- /dev/null +++ b/go.mod @@ -0,0 +1,9 @@ +module go.linka.cloud/protofilters + +go 1.16 + +require ( + github.com/sirupsen/logrus v1.8.1 + github.com/stretchr/testify v1.3.0 + google.golang.org/protobuf v1.26.0 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..3871fca --- /dev/null +++ b/go.sum @@ -0,0 +1,21 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= diff --git a/matcher/matcher.go b/matcher/matcher.go new file mode 100644 index 0000000..4eb3cc9 --- /dev/null +++ b/matcher/matcher.go @@ -0,0 +1,268 @@ +package matcher + +import ( + "errors" + "fmt" + "regexp" + "strings" + "sync" + + "google.golang.org/protobuf/proto" + pref "google.golang.org/protobuf/reflect/protoreflect" + + pf "go.linka.cloud/protofilters" +) + +type Matcher interface { + Match(m proto.Message, f *pf.FieldsFilter) (bool, error) + MatchFilters(m proto.Message, fs ...*pf.FieldFilter) (bool, error) +} + +type CachingMatcher interface { + Matcher + ResetCache() +} + +var defaultMatcher CachingMatcher = &matcher{cache: make(map[string]pref.FieldDescriptor)} + +func Match(m proto.Message, f *pf.FieldsFilter) (bool, error) { + return defaultMatcher.Match(m, f) +} + +func MatchFilters(m proto.Message, fs ...*pf.FieldFilter) (bool, error) { + return defaultMatcher.MatchFilters(m, fs...) +} + +type matcher struct { + mu sync.RWMutex + cache map[string]pref.FieldDescriptor +} + +func (x *matcher) Match(m proto.Message, f *pf.FieldsFilter) (bool, error) { + if m == nil { + return false, errors.New("message is null") + } + if f == nil { + return true, nil + } + for path, filter := range f.Filters { + fd, err := x.lookup(m, path) + if err != nil { + return false, err + } + ok, err := match(m, fd, filter) + if err != nil { + return false, err + } + if !ok { + return false, nil + } + } + return true, nil +} + +func (x *matcher) MatchFilters(m proto.Message, fs ...*pf.FieldFilter) (bool, error) { + f := pf.New(fs...) + return x.Match(m, f) +} + +func (x *matcher) ResetCache() { + x.mu.Lock() + x.cache = make(map[string]pref.FieldDescriptor) + x.mu.Unlock() +} + +func (x *matcher) lookup(m proto.Message, path string) (pref.FieldDescriptor, error) { + if x.cache == nil { + x.mu.Lock() + x.cache = make(map[string]pref.FieldDescriptor) + x.mu.Unlock() + } + key := fmt.Sprintf("%s.%s", m.ProtoReflect().Descriptor().FullName(), path) + x.mu.RLock() + fd, ok := x.cache[key] + x.mu.RUnlock() + if ok { + return fd, nil + } + md0 := m.ProtoReflect().Descriptor() + md := md0 + fd, ok = rangeFields(path, func(field string) (pref.FieldDescriptor, bool) { + // Search the field within the message. + if md == nil { + return nil, false // not within a message + } + fd := md.Fields().ByName(pref.Name(field)) + // The real field name of a group is the message name. + if fd == nil { + gd := md.Fields().ByName(pref.Name(strings.ToLower(field))) + if gd != nil && gd.Kind() == pref.GroupKind && string(gd.Message().Name()) == field { + fd = gd + } + } else if fd.Kind() == pref.GroupKind && string(fd.Message().Name()) != field { + fd = nil + } + if fd == nil { + return nil, false // message does not have this field + } + // Identify the next message to search within. + md = fd.Message() // may be nil + + // Repeated fields are only allowed at the last postion. + if fd.IsList() || fd.IsMap() { + md = nil + } + + return fd, true + }) + if !ok { + return nil, fmt.Errorf("%s does not contain '%s'", md0.FullName(), path) + } + x.mu.Lock() + x.cache[key] = fd + x.mu.Unlock() + return fd, nil +} + +func match(m proto.Message, fd pref.FieldDescriptor, f *pf.Filter) (bool, error) { + switch f.GetMatch().(type) { + case *pf.Filter_String_: + return matchString(m, fd, f) + case *pf.Filter_Number: + return matchNumber(m, fd, f) + case *pf.Filter_Bool: + return matchBool(m, fd, f) + case *pf.Filter_Null: + return matchNull(m, fd, f) + } + return false, nil +} + +func matchNull(m proto.Message, fd pref.FieldDescriptor, f *pf.Filter) (bool, error) { + var match bool + switch fd.Kind() { + case pref.MessageKind: + match = !m.ProtoReflect().Has(fd) + case pref.GroupKind: + match = m.ProtoReflect().Get(fd).List().Len() == 0 + default: + return false, fmt.Errorf("cannot use null filter on %s", fd.Kind().String()) + } + if f.GetNull().GetNot() { + return !match, nil + } + return match, nil +} + +func matchBool(m proto.Message, fd pref.FieldDescriptor, f *pf.Filter) (bool, error) { + if fd.Kind() != pref.BoolKind { + return false, fmt.Errorf("cannot use bool filter on %s", fd.Kind().String()) + } + return m.ProtoReflect().Get(fd).Bool() == f.GetBool().GetEquals(), nil +} + +func matchString(m proto.Message, fd pref.FieldDescriptor, f *pf.Filter) (bool, error) { + if fd.Kind() != pref.StringKind && fd.Kind() != pref.EnumKind { + return false, fmt.Errorf("cannot use string filter on %s", fd.Kind().String()) + } + insensitive := f.GetString_().GetCaseInsensitive() + rval := m.ProtoReflect().Get(fd) + value := rval.String() + if fd.Kind() == pref.EnumKind { + e := fd.Enum().Values().ByNumber(rval.Enum()) + if e == nil { + return false, nil + } + value = string(e.Name()) + } + var match bool + switch f.GetString_().GetCondition().(type) { + case *pf.StringFilter_Equals: + if insensitive { + match = strings.ToLower(f.GetString_().GetEquals()) == strings.ToLower(value) + } else { + match = value == f.GetString_().GetEquals() + } + case *pf.StringFilter_Regex: + reg, err := regexp.Compile(f.GetString_().GetRegex()) + if err != nil { + return false, err + } + match = reg.MatchString(value) + case *pf.StringFilter_In_: + lookup: + for _, v := range f.GetString_().GetIn().GetValues() { + if (insensitive && strings.ToLower(v) == strings.ToLower(value)) || v == value { + match = true + break lookup + } + } + } + if f.GetString_().GetNot() { + return !match, nil + } + return match, nil +} + +func matchNumber(m proto.Message, fd pref.FieldDescriptor, f *pf.Filter) (bool, error) { + rval := m.ProtoReflect().Get(fd) + var val float64 + switch fd.Kind() { + case pref.Int32Kind, + pref.Sint32Kind, + pref.Int64Kind, + pref.Sint64Kind, + pref.Sfixed32Kind, + pref.Fixed32Kind, + pref.Sfixed64Kind, + pref.Fixed64Kind: + val = float64(rval.Int()) + case pref.Uint32Kind, pref.Uint64Kind: + val = float64(rval.Uint()) + case pref.FloatKind, pref.DoubleKind: + val = rval.Float() + case pref.EnumKind: + val = float64(rval.Enum()) + default: + return false, fmt.Errorf("cannot use number filter on %s", fd.Kind().String()) + } + var match bool + switch f.GetNumber().GetCondition().(type) { + case *pf.NumberFilter_Equals: + match = val == f.GetNumber().GetEquals() + case *pf.NumberFilter_In_: + lookup: + for _, v := range f.GetNumber().GetIn().GetValues() { + if val == v { + match = true + break lookup + } + } + } + if f.GetNumber().GetNot() { + return !match, nil + } + return match, nil +} + +// rangeFields is like strings.Split(path, "."), but avoids allocations by +// iterating over each field in place and calling a iterator function. +func rangeFields(path string, f func(field string) (pref.FieldDescriptor, bool)) (pref.FieldDescriptor, bool) { + for { + var field string + if i := strings.IndexByte(path, '.'); i >= 0 { + field, path = path[:i], path[i:] + } else { + field, path = path, "" + } + v, ok := f(field) + if !ok { + return nil, false + } + if len(path) == 0 { + return v, true + } + path = strings.TrimPrefix(path, ".") + } +} + diff --git a/matcher/matcher_test.go b/matcher/matcher_test.go new file mode 100644 index 0000000..eabf345 --- /dev/null +++ b/matcher/matcher_test.go @@ -0,0 +1,85 @@ +package matcher + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + pf "go.linka.cloud/protofilters" + test "go.linka.cloud/protofilters/tests/pb" +) + +func TestFieldFilter(t *testing.T) { + assert := assert.New(t) + m := &test.Test{StringField: "ok"} + ok, err := Match(m, &pf.FieldsFilter{Filters: map[string]*pf.Filter{ + "noop": pf.StringEquals("ok"), + }}) + assert.Error(err) + assert.False(ok) + ok, err = Match(m, &pf.FieldsFilter{Filters: map[string]*pf.Filter{ + "messageField": pf.Null(), + }}) + assert.Error(err) + assert.False(ok) + assert.True(Match(m, &pf.FieldsFilter{Filters: map[string]*pf.Filter{ + "string_field": pf.StringEquals("ok"), + }})) + assert.True(Match(m, &pf.FieldsFilter{Filters: map[string]*pf.Filter{ + "string_field": pf.StringIN("other", "ok"), + }})) + assert.False(Match(m, &pf.FieldsFilter{Filters: map[string]*pf.Filter{ + "string_field": pf.StringIN("other", "noop"), + }})) + assert.True(Match(m, &pf.FieldsFilter{Filters: map[string]*pf.Filter{ + "string_field": pf.StringNotIN(), + "enum_field": pf.StringIN("NONE"), + }})) + assert.False(Match(m, &pf.FieldsFilter{Filters: map[string]*pf.Filter{ + "string_field": pf.StringNotRegex(`[a-z](.+)`), + }})) + m.EnumField = test.Test_Type(42) + assert.False(Match(m, &pf.FieldsFilter{Filters: map[string]*pf.Filter{ + "enum_field": pf.StringIN("OTHER"), + }})) + assert.True(Match(m, &pf.FieldsFilter{Filters: map[string]*pf.Filter{ + "message_field": pf.Null(), + }})) + m.MessageField = m + assert.False(Match(m, &pf.FieldsFilter{Filters: map[string]*pf.Filter{ + "message_field": pf.Null(), + }})) + ok, err = Match(m, &pf.FieldsFilter{Filters: map[string]*pf.Filter{ + "message_field.string_field.message_field": pf.Null(), + }}) + assert.Error(err) + assert.False(ok) + assert.False(Match(m, &pf.FieldsFilter{Filters: map[string]*pf.Filter{ + "message_field.message_field.message_field": pf.Null(), + }})) + assert.True(Match(m, &pf.FieldsFilter{Filters: map[string]*pf.Filter{ + "message_field.message_field.message_field.string_field": pf.StringIN("ok"), + }})) + + assert.False(Match(m, &pf.FieldsFilter{Filters: map[string]*pf.Filter{ + "enum_field": pf.StringIN("OTHER"), + }})) + + m.NumberField = 42 + assert.False(Match(m, &pf.FieldsFilter{Filters: map[string]*pf.Filter{ + "number_field": pf.NumberEquals(0), + }})) + assert.True(Match(m, &pf.FieldsFilter{Filters: map[string]*pf.Filter{ + "number_field": pf.NumberEquals(42), + }})) + assert.False(Match(m, &pf.FieldsFilter{Filters: map[string]*pf.Filter{ + "number_field": pf.NumberIN(0, 22), + }})) + assert.True(Match(m, &pf.FieldsFilter{Filters: map[string]*pf.Filter{ + "enum_field": pf.NumberIN(0, 42), + }})) + assert.False(Match(m, &pf.FieldsFilter{Filters: map[string]*pf.Filter{ + "enum_field": pf.NumberNotIN(0, 42), + }})) + +} diff --git a/tests/gen/gen.go b/tests/gen/gen.go new file mode 100644 index 0000000..b3d35c0 --- /dev/null +++ b/tests/gen/gen.go @@ -0,0 +1,44 @@ +package gen + +import ( + "os" + + "github.com/sirupsen/logrus" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protodesc" + "google.golang.org/protobuf/types/known/anypb" + + test "go.linka.cloud/protofilters/tests/pb" +) + +func Gen() { + t := &test.Test{StringField: "whatever..."} + tf, err := os.Create("test.bin") + if err != nil { + logrus.Fatal(err) + } + defer tf.Close() + a, err := anypb.New(t) + if err != nil { + logrus.Fatal(err) + } + b, err := proto.Marshal(a) + if err != nil { + logrus.Fatal(err) + } + if _, err := tf.Write(b); err != nil { + logrus.Fatal(err) + } + tdf, err := os.Create("test.file-descriptor.bin") + if err != nil { + logrus.Fatal(err) + } + defer tdf.Close() + b, err = proto.Marshal(protodesc.ToFileDescriptorProto(t.ProtoReflect().Descriptor().ParentFile())) + if err != nil { + logrus.Fatal(err) + } + if _, err := tdf.Write(b); err != nil { + logrus.Fatal(err) + } +} diff --git a/tests/main.go b/tests/main.go new file mode 100644 index 0000000..0b57347 --- /dev/null +++ b/tests/main.go @@ -0,0 +1,60 @@ +package main + +import ( + "io/ioutil" + + "github.com/sirupsen/logrus" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/reflect/protodesc" + "google.golang.org/protobuf/reflect/protoregistry" + "google.golang.org/protobuf/types/descriptorpb" + "google.golang.org/protobuf/types/dynamicpb" + "google.golang.org/protobuf/types/known/anypb" + + ff "go.linka.cloud/protofilters" + "go.linka.cloud/protofilters/matcher" +) + +func main() { + // gen.Gen() + b, err := ioutil.ReadFile("test.file-descriptor.bin") + if err != nil { + logrus.Fatal(err) + } + var fdp descriptorpb.FileDescriptorProto + if err := proto.Unmarshal(b, &fdp); err != nil { + logrus.Fatal(err) + } + fd, err := protodesc.NewFile(&fdp, protoregistry.GlobalFiles) + if err != nil { + logrus.Fatal(err) + } + // protodesc.NewFile(d.) + d := dynamicpb.NewMessage(fd.Messages().Get(0)) + b, err = ioutil.ReadFile("test.bin") + if err != nil { + logrus.Fatal(err) + } + a := anypb.Any{} + if err := proto.Unmarshal(b, &a); err != nil { + logrus.Fatal(err) + } + if err := anypb.UnmarshalTo(&a, d, proto.UnmarshalOptions{}); err != nil { + logrus.Fatal(err) + } + logrus.Infof("%+v", d) + logrus.Info(d.ProtoReflect().Descriptor().FullName()) + filter := &ff.FieldFilter{ + Field: "string_field", + // Filter: ff.StringRegex(".*....*"), + Filter: ff.StringIN("", "whatever..."), + } + ok, err := matcher.MatchFilters(d, filter) + if err != nil { + logrus.Fatal(err) + } + if !ok { + logrus.Fatal("reg ex not found") + } + logrus.Infof("%v match %v", d, filter) +}