The matcher package is a quick experiment in reflection-driven dynamic matching against arbitrary structs.
The intent is that given a query expression (JSON in the example utility, but equally composable by hand), a struct can be evaluted against one or more conditionals that check whether its properties match certain criteria.
An example JSON query might look like:
[
{
"field": "name",
"operator": "eq",
"value": "Alice"
},
{
"field": "age",
"operator": "gt",
"value": 30
}
]
A struct we want to match against might be defined as such:
type Person struct {
Name string `matcher:"name"`
Age int `matcher:"age"`
}
Usage then, looks something like:
matcher.RegisterDefaults()
clauses := []*matcher.Clause{&matcher.Clause{
Field: "name",
Operator: "eq",
Value: "Alice",
}}
p := Person{
Name: "Alice",
Age: 32,
}
// true, nil
matched, err := matcher.Matches(p, clauses)
Custom types must satisfy the Comparator
interface:
type Comparator interface {
GreaterThan(interface{}, interface{}) bool
LessThan(interface{}, interface{}) bool
EqualTo(interface{}, interface{}) bool
NotEqualTo(interface{}, interface{}) bool
Valid(interface{}) error
}
A concrete example of this is cmp_string.go
Once a comparator wrapper has been implemented for a given type, it needs to be associated with the original type via the registry, e.g.:
// Pass a reflect.Type corresponding to string, plus your comparator interface implementation.
matcher.Register(reflect.ValueOf("foo").Type(), &StringComparator{})