203 lines
5.2 KiB
Go
203 lines
5.2 KiB
Go
package mercury
|
|
|
|
import (
|
|
"log"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// Search implements a parsed namespace search
|
|
// It parses the input and generates an AST to inform the driver how to select values.
|
|
// * => all spaces
|
|
// mercury.* => all prefixed with `mercury.`
|
|
// mercury.config => only space `mercury.config`
|
|
// mercury.source.*#readonly => all prefixed with `mercury.source.` AND has tag `readonly`
|
|
// test.*|mercury.* => all prefixed with `test.` AND `mercury.`
|
|
// test.* find bin=eq=bar => all prefixed with `test.` AND has an attribute bin that equals bar
|
|
// test.* fields foo,bin => all prefixed with `test.` only show fields foo and bin
|
|
// - count 20 => start a cursor with 20 results
|
|
// - count 20 after <cursor> => continue after cursor for 20 results
|
|
// cursor encodes start points for each of the matched sources
|
|
type Search struct {
|
|
NamespaceSearch
|
|
Find []ops
|
|
Fields []string
|
|
Count uint64
|
|
Offset uint64
|
|
Cursor string
|
|
}
|
|
|
|
type NamespaceSpec interface {
|
|
Value() string
|
|
String() string
|
|
Raw() string
|
|
Match(string) bool
|
|
}
|
|
|
|
// NamespaceSearch list of namespace specs
|
|
type NamespaceSearch []NamespaceSpec
|
|
|
|
// ParseNamespace returns a list of parsed values
|
|
func ParseSearch(text string) (search Search) {
|
|
ns, text, _ := strings.Cut(text, " ")
|
|
var lis NamespaceSearch
|
|
for _, part := range strings.Split(ns, "|") {
|
|
if strings.HasPrefix(part, "trace:") {
|
|
lis = append(lis, NamespaceTrace(part[6:]))
|
|
} else if strings.Contains(part, "*") {
|
|
lis = append(lis, NamespaceStar(part))
|
|
} else {
|
|
lis = append(lis, NamespaceNode(part))
|
|
}
|
|
}
|
|
search.NamespaceSearch = lis
|
|
|
|
field, text, next := strings.Cut(text, " ")
|
|
text = strings.TrimSpace(text)
|
|
for next {
|
|
switch strings.ToLower(field) {
|
|
case "find":
|
|
field, text, _ = strings.Cut(text, " ")
|
|
text = strings.TrimSpace(text)
|
|
search.Find = simpleParse(field)
|
|
|
|
case "fields":
|
|
field, text, _ = strings.Cut(text, " ")
|
|
text = strings.TrimSpace(text)
|
|
search.Fields = strings.Split(field, ",")
|
|
|
|
case "count":
|
|
field, text, _ = strings.Cut(text, " ")
|
|
text = strings.TrimSpace(text)
|
|
search.Count, _ = strconv.ParseUint(field, 10, 64)
|
|
|
|
case "offset":
|
|
field, text, _ = strings.Cut(text, " ")
|
|
text = strings.TrimSpace(text)
|
|
search.Offset, _ = strconv.ParseUint(field, 10, 64)
|
|
|
|
case "after":
|
|
field, text, _ = strings.Cut(text, " ")
|
|
text = strings.TrimSpace(text)
|
|
search.Cursor = field
|
|
}
|
|
field, text, next = strings.Cut(text, " ")
|
|
text = strings.TrimSpace(text)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// String output string value
|
|
func (n NamespaceSearch) String() string {
|
|
lis := make([]string, 0, len(n))
|
|
|
|
for _, v := range n {
|
|
lis = append(lis, v.String())
|
|
}
|
|
return strings.Join(lis, ";")
|
|
}
|
|
|
|
// Match returns true if any match.
|
|
func (n NamespaceSearch) Match(s string) bool {
|
|
for _, m := range n {
|
|
ok, err := filepath.Match(m.Raw(), s)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
if ok {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// NamespaceNode implements a node search value
|
|
type NamespaceNode string
|
|
|
|
// String output string value
|
|
func (n NamespaceNode) String() string { return string(n) }
|
|
|
|
// Quote return quoted value.
|
|
// func (n NamespaceNode) Quote() string { return `'` + n.Value() + `'` }
|
|
|
|
// Value to return the value
|
|
func (n NamespaceNode) Value() string { return string(n) }
|
|
|
|
// Raw return raw value.
|
|
func (n NamespaceNode) Raw() string { return string(n) }
|
|
|
|
// Match returns true if any match.
|
|
func (n NamespaceNode) Match(s string) bool { return match(n, s) }
|
|
|
|
// NamespaceTrace implements a trace search value
|
|
type NamespaceTrace string
|
|
|
|
// String output string value
|
|
func (n NamespaceTrace) String() string { return "trace:" + string(n) }
|
|
|
|
// Quote return quoted value.
|
|
// func (n NamespaceTrace) Quote() string { return `'` + n.Value() + `'` }
|
|
|
|
// Value to return the value
|
|
func (n NamespaceTrace) Value() string { return strings.Replace(string(n), "*", "%", -1) }
|
|
|
|
// Raw return raw value.
|
|
func (n NamespaceTrace) Raw() string { return string(n) }
|
|
|
|
// Match returns true if any match.
|
|
func (n NamespaceTrace) Match(s string) bool { return match(n, s) }
|
|
|
|
// NamespaceStar implements a trace search value
|
|
type NamespaceStar string
|
|
|
|
// String output string value
|
|
func (n NamespaceStar) String() string { return string(n) }
|
|
|
|
// Quote return quoted value.
|
|
// func (n NamespaceStar) Quote() string { return `'` + n.Value() + `'` }
|
|
|
|
// Value to return the value
|
|
func (n NamespaceStar) Value() string { return strings.Replace(string(n), "*", "%", -1) }
|
|
|
|
// Raw return raw value.
|
|
func (n NamespaceStar) Raw() string { return string(n) }
|
|
|
|
// Match returns true if any match.
|
|
func (n NamespaceStar) Match(s string) bool { return match(n, s) }
|
|
|
|
func match(n NamespaceSpec, s string) bool {
|
|
ok, err := filepath.Match(n.Raw(), s)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
return ok
|
|
}
|
|
|
|
type ops struct {
|
|
Left string
|
|
Op string
|
|
Right string
|
|
}
|
|
|
|
func simpleParse(in string) (out []ops) {
|
|
items := strings.Split(in, ",")
|
|
for _, i := range items {
|
|
log.Println(i)
|
|
eq := strings.Split(i, "=")
|
|
switch len(eq) {
|
|
case 2:
|
|
out = append(out, ops{eq[0], "eq", eq[1]})
|
|
case 3:
|
|
if eq[1] == "" {
|
|
eq[1] = "eq"
|
|
}
|
|
out = append(out, ops{eq[0], eq[1], eq[2]})
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|