add filters package (#300)
* add filters package Filters allow a caller to define some comparator- ie, a filter- and use that filter to try matching various inputs.
This commit is contained in:
parent
fa190da682
commit
608d19e821
139
src/pkg/filters/filters.go
Normal file
139
src/pkg/filters/filters.go
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
package filters
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
type comparator int
|
||||||
|
|
||||||
|
const (
|
||||||
|
UnknownComparator comparator = iota
|
||||||
|
// a == b
|
||||||
|
Equal
|
||||||
|
// a > b
|
||||||
|
Greater
|
||||||
|
// a < b
|
||||||
|
Less
|
||||||
|
// a < b < c
|
||||||
|
Between
|
||||||
|
// "foo" contains "f"
|
||||||
|
Contains
|
||||||
|
// "f" is found in "foo"
|
||||||
|
In
|
||||||
|
)
|
||||||
|
|
||||||
|
const delimiter = ","
|
||||||
|
|
||||||
|
func join(s ...string) string {
|
||||||
|
return strings.Join(s, delimiter)
|
||||||
|
}
|
||||||
|
|
||||||
|
func split(s string) []string {
|
||||||
|
return strings.Split(s, delimiter)
|
||||||
|
}
|
||||||
|
|
||||||
|
func norm(s string) string {
|
||||||
|
return strings.ToLower(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter contains a comparator func and the target to
|
||||||
|
// compare values against. Filter.Matches(v) returns
|
||||||
|
// true if Filter.Comparer(filter.target, v) is true.
|
||||||
|
type Filter struct {
|
||||||
|
Comparator comparator `json:"comparator"`
|
||||||
|
Category any `json:"category"` // a caller-provided identifier. Probably an iota or string const.
|
||||||
|
Target string `json:"target"` // the value to compare against
|
||||||
|
Negate bool `json:"negate"` // when true, negate the comparator result
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewEquals creates a filter which Matches(v) is true if
|
||||||
|
// target == v
|
||||||
|
func NewEquals(negate bool, category any, target string) Filter {
|
||||||
|
return Filter{Equal, category, norm(target), negate}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGreater creates a filter which Matches(v) is true if
|
||||||
|
// target > v
|
||||||
|
func NewGreater(negate bool, category any, target string) Filter {
|
||||||
|
return Filter{Greater, category, norm(target), negate}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLess creates a filter which Matches(v) is true if
|
||||||
|
// target < v
|
||||||
|
func NewLess(negate bool, category any, target string) Filter {
|
||||||
|
return Filter{Less, category, norm(target), negate}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewBetween creates a filter which Matches(v) is true if
|
||||||
|
// lesser < v && v < greater
|
||||||
|
func NewBetween(negate bool, category any, lesser, greater string) Filter {
|
||||||
|
return Filter{Between, category, norm(join(lesser, greater)), negate}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewContains creates a filter which Matches(v) is true if
|
||||||
|
// super.Contains(v)
|
||||||
|
func NewContains(negate bool, category any, super string) Filter {
|
||||||
|
return Filter{Contains, category, norm(super), negate}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewIn creates a filter which Matches(v) is true if
|
||||||
|
// v.Contains(substr)
|
||||||
|
func NewIn(negate bool, category any, substr string) Filter {
|
||||||
|
return Filter{In, category, norm(substr), negate}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks whether the filter matches the input
|
||||||
|
func (f Filter) Matches(input string) bool {
|
||||||
|
var cmp func(string, string) bool
|
||||||
|
switch f.Comparator {
|
||||||
|
case Equal:
|
||||||
|
cmp = equals
|
||||||
|
case Greater:
|
||||||
|
cmp = greater
|
||||||
|
case Less:
|
||||||
|
cmp = less
|
||||||
|
case Between:
|
||||||
|
cmp = between
|
||||||
|
case Contains:
|
||||||
|
cmp = contains
|
||||||
|
case In:
|
||||||
|
cmp = in
|
||||||
|
}
|
||||||
|
result := cmp(f.Target, norm(input))
|
||||||
|
if f.Negate {
|
||||||
|
result = !result
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// true if t == i
|
||||||
|
func equals(target, input string) bool {
|
||||||
|
return target == input
|
||||||
|
}
|
||||||
|
|
||||||
|
// true if t > i
|
||||||
|
func greater(target, input string) bool {
|
||||||
|
return target > input
|
||||||
|
}
|
||||||
|
|
||||||
|
// true if t < i
|
||||||
|
func less(target, input string) bool {
|
||||||
|
return target < input
|
||||||
|
}
|
||||||
|
|
||||||
|
// assumes target is a delimited string.
|
||||||
|
// true if both:
|
||||||
|
// - less(target[0], input)
|
||||||
|
// - greater(target[1], input)
|
||||||
|
func between(target, input string) bool {
|
||||||
|
parts := split(target)
|
||||||
|
return less(parts[0], input) && greater(parts[1], input)
|
||||||
|
}
|
||||||
|
|
||||||
|
// true if target contains input as a substring.
|
||||||
|
func contains(target, input string) bool {
|
||||||
|
return strings.Contains(target, input)
|
||||||
|
}
|
||||||
|
|
||||||
|
// true if input contains target as a substring.
|
||||||
|
func in(target, input string) bool {
|
||||||
|
return strings.Contains(input, target)
|
||||||
|
}
|
||||||
147
src/pkg/filters/filters_test.go
Normal file
147
src/pkg/filters/filters_test.go
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
package filters_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/alcionai/corso/pkg/filters"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FiltersSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFiltersSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(FiltersSuite))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *FiltersSuite) TestEquals() {
|
||||||
|
make := filters.NewEquals
|
||||||
|
f := make(false, "", "foo")
|
||||||
|
nf := make(true, "", "foo")
|
||||||
|
|
||||||
|
table := []struct {
|
||||||
|
input string
|
||||||
|
expectF assert.BoolAssertionFunc
|
||||||
|
expectNF assert.BoolAssertionFunc
|
||||||
|
}{
|
||||||
|
{"foo", assert.True, assert.False},
|
||||||
|
{"bar", assert.False, assert.True},
|
||||||
|
}
|
||||||
|
for _, test := range table {
|
||||||
|
suite.T().Run(test.input, func(t *testing.T) {
|
||||||
|
test.expectF(t, f.Matches(test.input), "filter")
|
||||||
|
test.expectNF(t, nf.Matches(test.input), "negated filter")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *FiltersSuite) TestGreater() {
|
||||||
|
make := filters.NewGreater
|
||||||
|
f := make(false, "", "5")
|
||||||
|
nf := make(true, "", "5")
|
||||||
|
|
||||||
|
table := []struct {
|
||||||
|
input string
|
||||||
|
expectF assert.BoolAssertionFunc
|
||||||
|
expectNF assert.BoolAssertionFunc
|
||||||
|
}{
|
||||||
|
{"4", assert.True, assert.False},
|
||||||
|
{"5", assert.False, assert.True},
|
||||||
|
{"6", assert.False, assert.True},
|
||||||
|
}
|
||||||
|
for _, test := range table {
|
||||||
|
suite.T().Run(test.input, func(t *testing.T) {
|
||||||
|
test.expectF(t, f.Matches(test.input), "filter")
|
||||||
|
test.expectNF(t, nf.Matches(test.input), "negated filter")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *FiltersSuite) TestLess() {
|
||||||
|
make := filters.NewLess
|
||||||
|
f := make(false, "", "5")
|
||||||
|
nf := make(true, "", "5")
|
||||||
|
|
||||||
|
table := []struct {
|
||||||
|
input string
|
||||||
|
expectF assert.BoolAssertionFunc
|
||||||
|
expectNF assert.BoolAssertionFunc
|
||||||
|
}{
|
||||||
|
{"6", assert.True, assert.False},
|
||||||
|
{"5", assert.False, assert.True},
|
||||||
|
{"4", assert.False, assert.True},
|
||||||
|
}
|
||||||
|
for _, test := range table {
|
||||||
|
suite.T().Run(test.input, func(t *testing.T) {
|
||||||
|
test.expectF(t, f.Matches(test.input), "filter")
|
||||||
|
test.expectNF(t, nf.Matches(test.input), "negated filter")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *FiltersSuite) TestBetween() {
|
||||||
|
make := filters.NewBetween
|
||||||
|
f := make(false, "", "abc", "def")
|
||||||
|
nf := make(true, "", "abc", "def")
|
||||||
|
|
||||||
|
table := []struct {
|
||||||
|
input string
|
||||||
|
expectF assert.BoolAssertionFunc
|
||||||
|
expectNF assert.BoolAssertionFunc
|
||||||
|
}{
|
||||||
|
{"cd", assert.True, assert.False},
|
||||||
|
{"a", assert.False, assert.True},
|
||||||
|
{"1", assert.False, assert.True},
|
||||||
|
{"f", assert.False, assert.True},
|
||||||
|
}
|
||||||
|
for _, test := range table {
|
||||||
|
suite.T().Run(test.input, func(t *testing.T) {
|
||||||
|
test.expectF(t, f.Matches(test.input), "filter")
|
||||||
|
test.expectNF(t, nf.Matches(test.input), "negated filter")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *FiltersSuite) TestContains() {
|
||||||
|
make := filters.NewContains
|
||||||
|
f := make(false, "", "smurfs")
|
||||||
|
nf := make(true, "", "smurfs")
|
||||||
|
|
||||||
|
table := []struct {
|
||||||
|
input string
|
||||||
|
expectF assert.BoolAssertionFunc
|
||||||
|
expectNF assert.BoolAssertionFunc
|
||||||
|
}{
|
||||||
|
{"murf", assert.True, assert.False},
|
||||||
|
{"frum", assert.False, assert.True},
|
||||||
|
}
|
||||||
|
for _, test := range table {
|
||||||
|
suite.T().Run(test.input, func(t *testing.T) {
|
||||||
|
test.expectF(t, f.Matches(test.input), "filter")
|
||||||
|
test.expectNF(t, nf.Matches(test.input), "negated filter")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *FiltersSuite) TestIn() {
|
||||||
|
make := filters.NewIn
|
||||||
|
f := make(false, "", "murf")
|
||||||
|
nf := make(true, "", "murf")
|
||||||
|
|
||||||
|
table := []struct {
|
||||||
|
input string
|
||||||
|
expectF assert.BoolAssertionFunc
|
||||||
|
expectNF assert.BoolAssertionFunc
|
||||||
|
}{
|
||||||
|
{"smurfs", assert.True, assert.False},
|
||||||
|
{"sfrums", assert.False, assert.True},
|
||||||
|
}
|
||||||
|
for _, test := range table {
|
||||||
|
suite.T().Run(test.input, func(t *testing.T) {
|
||||||
|
test.expectF(t, f.Matches(test.input), "filter")
|
||||||
|
test.expectNF(t, nf.Matches(test.input), "negated filter")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user