This commit is contained in:
Adphi 2021-06-01 20:08:58 +02:00
commit 1b63579ad6
11 changed files with 1693 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.idea
test.*

11
Makefile Normal file
View File

@ -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

189
field_filter.extensions.go Normal file
View File

@ -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,
},
}
}

915
field_filter.pb.go Normal file
View File

@ -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
}

88
field_filter.proto Normal file
View File

@ -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<string, Filter> 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;
}

9
go.mod Normal file
View File

@ -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
)

21
go.sum Normal file
View File

@ -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=

268
matcher/matcher.go Normal file
View File

@ -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, ".")
}
}

85
matcher/matcher_test.go Normal file
View File

@ -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),
}}))
}

44
tests/gen/gen.go Normal file
View File

@ -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)
}
}

61
tests/main.go Normal file
View File

@ -0,0 +1,61 @@
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")
} else {
logrus.Infof("%v match %v", d, filter)
}
}