func fieldMatches(field string, valueMatchers []string, obj map[string]interface{}) bool {
	val, ok := lookupField(field, obj)
	if !ok {
		return false
	}
	strVal := ""
	switch v := val.(type) {
	case nil:
		return false
	case string:
		strVal = v
	case bool:
		if v {
			strVal = "true"
		} else {
			strVal = "false"
		}
	default:  
		return valueMatchers[0] == "*"
	}
	if strVal == "" {
		return false
	}
	if valueMatchers[0] == "*" {
		return true
	}
	for _, match := range valueMatchers {
		if strVal == match {
			return true
		}
	}
	return false
}
fieldMatches returns true if the value of the key `field` in the map `obj`
is one of `values`. Dots in `field` are interpreted as denoting subobjects
-- i.e. the field name "x.y.z" says to check obj["x"]["y"]["z"].
package router
import (
	"strings"
)
func (r *Rule) Matches(msg map[string]interface{}) bool {
	for field, values := range r.Matchers {
		if !fieldMatches(field, values, msg) {
			return false
		}
	}
	return true
}
func (r *Rule) OutputFor(msg map[string]interface{}) map[string]interface{} {
	lookup := func(field string) (interface{}, bool) {
		return lookupField(field, msg)
	}
	subbed := substituteFields(r.Output, lookup)
	subbed["rule"] = r.Name
	return subbed
}
func lookupField(field string, obj map[string]interface{}) (interface{}, bool) {
	if strings.Index(field, ".") == -1 {
		val, ok := obj[field]
		return val, ok
	}
	return lookupFieldPath(strings.Split(field, "."), obj)
}
func lookupFieldPath(fieldPath []string, obj map[string]interface{}) (interface{}, bool) {
	part := fieldPath[0]
	if len(fieldPath) == 1 {
		val, ok := obj[part]
		return val, ok
	}
	if subObj, ok := obj[part].(map[string]interface{}); ok {
		return lookupFieldPath(fieldPath[1:], subObj)
	}
	return "", false
}