-
Notifications
You must be signed in to change notification settings - Fork 0
/
template.go
161 lines (131 loc) · 3.29 KB
/
template.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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
package find
import (
"os"
"strings"
)
// String representation of the current system path separator.
var pathSeparator = string(os.PathSeparator)
// Template is a parsed version of each Find filter.
type Template struct {
and *Template
or *Template
base string
not bool
strictLeft bool
strictRight bool
}
// NewTemplate creates new Template from the given string.
//
// String can contains:
//
// *str* - means that searched path should contain str
// str - means that searched path should be str
// *str - means that searched path should ends with str
// str* - means that searched path should starts with str
// !*str* - means that searched path should not contain str
// !str - means that searched path should not be str
// !*str - means that searched path should not end with str
// !str* - means that searched path should not start with str
// str&str1 - means that searched path should be both str and str1
// str|str1 - means that searched path should be str or str1
//
// Option '&' defines nested paths e.g., '*str*&*str1*' - Find will search
// for 'str' first and if it was found 'str1' inside it.
//
// Options '|' and '&' can contain as many elements as you need.
func NewTemplate(str string) *Template {
var t *Template
sep := strings.IndexFunc(str, func(r rune) bool {
if r == '&' || r == '|' {
return true
}
return false
})
if sep == -1 {
return parse(str)
}
switch str[sep] {
case '&':
t = parse(str[:sep])
t.and = NewTemplate(str[sep+1:])
case '|':
t = parse(str[:sep])
t.or = NewTemplate(str[sep+1:])
}
return t
}
// parse parses string into the Template.
func parse(str string) *Template {
t := &Template{}
t.not = strings.HasPrefix(str, "!")
str = strings.TrimPrefix(str, "!")
// If searched string is '*', then it will match
// any path it encounters. 'Not' will be ignored
// in this case.
if str == "*" {
t.strictLeft = false
t.strictRight = false
t.base = str
return t
}
t.strictLeft = !strings.HasPrefix(str, "*")
str = strings.TrimPrefix(str, "*")
t.strictRight = !strings.HasSuffix(str, "*")
t.base = strings.TrimSuffix(str, "*")
return t
}
// Match checks if given str matches the [Template].
func (t *Template) Match(str string) bool {
var match bool
switch {
case t.base == "":
return false
case t.base == "*":
match = true
case strings.Contains(str, t.base):
match = t.match(str)
case t.not:
match = true
}
if t.or != nil && !match {
match = t.or.Match(str)
}
if t.and != nil {
if !match {
return match
}
match = t.and.Match(str)
}
return match
}
func (t *Template) match(str string) bool {
match := true
sub := strings.Split(str, t.base)
left := len(sub) == 1 ||
sub[0] == "" ||
strings.HasSuffix(sub[0], pathSeparator)
right := len(sub) == 1 ||
sub[1] == "" ||
strings.HasPrefix(sub[1], pathSeparator)
switch {
case t.strictLeft && t.strictRight:
match = left && right
case t.strictLeft:
match = left
case t.strictRight:
match = right
}
if t.not {
match = !match
}
return match
}
type Templates []*Template
// NewTemplates parses slice of strings into slice of Templates.
func NewTemplates(t []string) Templates {
ts := make(Templates, 0, len(t))
for _, str := range t {
ts = append(ts, NewTemplate(str))
}
return ts
}