package main import ( "fmt" "net/url" "strings" ) type Filter struct { Path string Value string } func ParseFilters(query url.Values) []Filter { raw := query["filter"] if len(raw) == 0 { return nil } filters := make([]Filter, 0, len(raw)) for _, f := range raw { path, value, ok := strings.Cut(f, ":") if !ok { continue } filters = append(filters, Filter{Path: path, Value: value}) } return filters } func MatchAll(filters []Filter, event *Event) bool { for _, f := range filters { if !matchOne(f, event) { return false } } return true } func matchOne(f Filter, event *Event) bool { val, ok := resolveField(f.Path, event) if !ok { return false } return val == f.Value } func resolveField(path string, event *Event) (string, bool) { parts := strings.Split(path, ".") switch parts[0] { case "id": return event.ID, len(parts) == 1 case "path": return event.Path, len(parts) == 1 case "timestamp": return event.Timestamp.Format("2006-01-02T15:04:05Z07:00"), len(parts) == 1 case "headers": if len(parts) != 2 { return "", false } val, ok := event.Headers[parts[1]] return val, ok case "payload": return navigateJSON(event.Payload, parts[1:]) default: return "", false } } func navigateJSON(val any, parts []string) (string, bool) { if len(parts) == 0 { return fmt.Sprintf("%v", val), true } m, ok := val.(map[string]any) if !ok { return "", false } next, ok := m[parts[0]] if !ok { return "", false } return navigateJSON(next, parts[1:]) }