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