-
Notifications
You must be signed in to change notification settings - Fork 0
/
match.go
115 lines (104 loc) · 2.47 KB
/
match.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// Content managed by Project Forge, see [projectforge.md] for details.
package result
import (
"fmt"
"reflect"
"strings"
"golang.org/x/exp/slices"
)
type Match struct {
Key string `json:"k"`
Value string `json:"v"`
}
func (m *Match) ValueSplit(q string) []string {
ql := strings.ToLower(q)
vl := strings.ToLower(m.Value)
cut := m.Value
idx := strings.Index(vl, ql)
if idx == -1 {
return []string{cut}
}
var ret []string
for idx > -1 {
if idx > 0 {
ret = append(ret, cut[:idx])
}
ret = append(ret, cut[idx:idx+len(ql)])
cut = cut[idx+len(ql):]
vl = vl[idx+len(ql):]
idx = strings.Index(vl, ql)
}
if len(cut) > 0 {
ret = append(ret, cut)
}
return ret
}
type Matches []*Match
func (m Matches) Sort() {
slices.SortFunc(m, func(l *Match, r *Match) bool {
return l.Key < r.Key
})
}
// nolint
func MatchesFor(key string, x any, q string) Matches {
v := reflect.ValueOf(x)
if v.Kind() == reflect.Ptr {
if v.IsNil() {
return nil
}
v = reflect.Indirect(v)
}
appendKey := func(s string) string {
if key == "" {
return s
}
return key + "." + s
}
maybe := func(cond bool, v string) Matches {
if cond {
return Matches{{Key: key, Value: v}}
}
return nil
}
switch v.Kind() {
case reflect.Bool:
return nil
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
i := fmt.Sprint(v.Int())
return maybe(strings.Contains(i, q), i)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
i := fmt.Sprint(v.Uint())
return maybe(strings.Contains(i, q), i)
case reflect.Float32, reflect.Float64:
f := fmt.Sprint(v.Float())
return maybe(strings.Contains(f, q), f)
case reflect.Map:
var ret Matches
x := v.MapRange()
for x.Next() {
ret = append(ret, MatchesFor(appendKey(x.Key().String()), x.Value().Interface(), q)...)
}
return ret
case reflect.Array, reflect.Slice:
var ret Matches
for idx := 0; idx < v.Len(); idx++ {
ret = append(ret, MatchesFor(appendKey(fmt.Sprint(idx)), v.Index(idx), q)...)
}
return ret
case reflect.String:
return maybe(strings.Contains(strings.ToLower(v.String()), q), v.String())
case reflect.Struct:
var ret Matches
for i := 0; i < v.NumField(); i++ {
if f := v.Field(i); f.CanSet() {
n := v.Type().Field(i).Name
if m := MatchesFor(appendKey(n), v.Field(i).Interface(), q); m != nil {
ret = append(ret, m...)
}
}
}
return ret
default:
return Matches{{Key: key, Value: "error: " + v.Kind().String()}}
}
}