81 lines
1.7 KiB
Go
81 lines
1.7 KiB
Go
package filter
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
func init() {
|
|
Register(Desc{Kind: "tick", Help: "tick range: N | N-M | N- | -M", New: newTickSpan})
|
|
Register(Desc{Kind: "run", Help: "run range: N | N-M | N- | -M", New: newRunSpan})
|
|
}
|
|
|
|
// Span is an inclusive numeric range over an index-resident counter.
|
|
type Span struct {
|
|
field string
|
|
Lo, Hi uint32
|
|
Query string
|
|
}
|
|
|
|
func newTickSpan(arg string) (Filter, error) { return newSpan("tick", arg) }
|
|
func newRunSpan(arg string) (Filter, error) { return newSpan("run", arg) }
|
|
|
|
func newSpan(field, arg string) (Filter, error) {
|
|
lo, hi, err := parseSpan(arg)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("filter/%s: %w", field, err)
|
|
}
|
|
return &Span{field: field, Lo: lo, Hi: hi, Query: arg}, nil
|
|
}
|
|
|
|
// parseSpan accepts N, N-M, N- and -M; an empty spec is the full range.
|
|
func parseSpan(arg string) (uint32, uint32, error) {
|
|
arg = strings.TrimSpace(arg)
|
|
if arg == "" {
|
|
return 0, ^uint32(0), nil
|
|
}
|
|
lo, hi := arg, arg
|
|
if i := strings.IndexByte(arg, '-'); i >= 0 {
|
|
lo, hi = arg[:i], arg[i+1:]
|
|
}
|
|
var l uint32
|
|
h := ^uint32(0)
|
|
if lo != "" {
|
|
v, err := strconv.ParseUint(lo, 10, 32)
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
l = uint32(v)
|
|
}
|
|
if hi != "" {
|
|
v, err := strconv.ParseUint(hi, 10, 32)
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
h = uint32(v)
|
|
}
|
|
if l > h {
|
|
l, h = h, l
|
|
}
|
|
return l, h, nil
|
|
}
|
|
|
|
func (f *Span) Kind() string { return f.field }
|
|
func (f *Span) Needs() Need { return 0 }
|
|
|
|
func (f *Span) Match(c *Ctx) bool {
|
|
v := c.Meta.Tick
|
|
if f.field == "run" {
|
|
v = c.Meta.Run
|
|
}
|
|
return v >= f.Lo && v <= f.Hi
|
|
}
|
|
|
|
func (f *Span) Label() string {
|
|
if f.Lo == 0 && f.Hi == ^uint32(0) {
|
|
return ""
|
|
}
|
|
return f.field + ":" + f.Query
|
|
}
|