1package cache
2
3import (
4 "strings"
5
6 "github.com/MichaelMure/git-bug/bug"
7)
8
9// Filter is a predicate that match a subset of bugs
10type Filter func(repoCache *RepoCache, excerpt *BugExcerpt) bool
11
12// StatusFilter return a Filter that match a bug status
13func StatusFilter(query string) (Filter, error) {
14 status, err := bug.StatusFromString(query)
15 if err != nil {
16 return nil, err
17 }
18
19 return func(repoCache *RepoCache, excerpt *BugExcerpt) bool {
20 return excerpt.Status == status
21 }, nil
22}
23
24// AuthorFilter return a Filter that match a bug author
25func AuthorFilter(query string) Filter {
26 return func(repoCache *RepoCache, excerpt *BugExcerpt) bool {
27 query = strings.ToLower(query)
28
29 // Normal identity
30 if excerpt.AuthorId != "" {
31 author, ok := repoCache.identitiesExcerpts[excerpt.AuthorId]
32 if !ok {
33 panic("missing identity in the cache")
34 }
35
36 return query == author.Id ||
37 query == author.HumanId() ||
38 strings.Contains(strings.ToLower(author.Name), query) ||
39 strings.Contains(strings.ToLower(author.Login), query)
40 }
41
42 // Legacy identity support
43 return strings.Contains(strings.ToLower(excerpt.LegacyAuthor.Name), query) ||
44 strings.Contains(strings.ToLower(excerpt.LegacyAuthor.Login), query)
45 }
46}
47
48// LabelFilter return a Filter that match a label
49func LabelFilter(label string) Filter {
50 return func(repoCache *RepoCache, excerpt *BugExcerpt) bool {
51 for _, l := range excerpt.Labels {
52 if string(l) == label {
53 return true
54 }
55 }
56 return false
57 }
58}
59
60// ActorFilter return a Filter that match a bug actor
61func ActorFilter(query string) Filter {
62 return func(repoCache *RepoCache, excerpt *BugExcerpt) bool {
63 query = strings.ToLower(query)
64
65 for _, id := range excerpt.Actors {
66 identityExcerpt, ok := repoCache.identitiesExcerpts[id]
67 if !ok {
68 panic("missing identity in the cache")
69 }
70
71 if query == identityExcerpt.Id ||
72 query == identityExcerpt.HumanId() ||
73 strings.Contains(strings.ToLower(identityExcerpt.Name), query) ||
74 query == strings.ToLower(identityExcerpt.Login) {
75 return true
76 }
77 }
78 return false
79 }
80}
81
82// ParticipantFilter return a Filter that match a bug participant
83func ParticipantFilter(query string) Filter {
84 return func(repoCache *RepoCache, excerpt *BugExcerpt) bool {
85 query = strings.ToLower(query)
86
87 for _, id := range excerpt.Participants {
88 identityExcerpt, ok := repoCache.identitiesExcerpts[id]
89 if !ok {
90 panic("missing identity in the cache")
91 }
92
93 if query == identityExcerpt.Id ||
94 query == identityExcerpt.HumanId() ||
95 strings.Contains(strings.ToLower(identityExcerpt.Name), query) ||
96 query == strings.ToLower(identityExcerpt.Login) {
97 return true
98 }
99 }
100 return false
101 }
102}
103
104// TitleFilter return a Filter that match if the title contains the given query
105func TitleFilter(query string) Filter {
106 return func(repo *RepoCache, excerpt *BugExcerpt) bool {
107 return strings.Contains(
108 strings.ToLower(excerpt.Title),
109 strings.ToLower(query),
110 )
111 }
112}
113
114// NoLabelFilter return a Filter that match the absence of labels
115func NoLabelFilter() Filter {
116 return func(repoCache *RepoCache, excerpt *BugExcerpt) bool {
117 return len(excerpt.Labels) == 0
118 }
119}
120
121// Filters is a collection of Filter that implement a complex filter
122type Filters struct {
123 Status []Filter
124 Author []Filter
125 Actor []Filter
126 Participant []Filter
127 Label []Filter
128 Title []Filter
129 NoFilters []Filter
130}
131
132// Match check if a bug match the set of filters
133func (f *Filters) Match(repoCache *RepoCache, excerpt *BugExcerpt) bool {
134 if match := f.orMatch(f.Status, repoCache, excerpt); !match {
135 return false
136 }
137
138 if match := f.orMatch(f.Author, repoCache, excerpt); !match {
139 return false
140 }
141
142 if match := f.orMatch(f.Participant, repoCache, excerpt); !match {
143 return false
144 }
145
146 if match := f.orMatch(f.Actor, repoCache, excerpt); !match {
147 return false
148 }
149
150 if match := f.andMatch(f.Label, repoCache, excerpt); !match {
151 return false
152 }
153
154 if match := f.andMatch(f.NoFilters, repoCache, excerpt); !match {
155 return false
156 }
157
158 if match := f.andMatch(f.Title, repoCache, excerpt); !match {
159 return false
160 }
161
162 return true
163}
164
165// Check if any of the filters provided match the bug
166func (*Filters) orMatch(filters []Filter, repoCache *RepoCache, excerpt *BugExcerpt) bool {
167 if len(filters) == 0 {
168 return true
169 }
170
171 match := false
172 for _, f := range filters {
173 match = match || f(repoCache, excerpt)
174 }
175
176 return match
177}
178
179// Check if all of the filters provided match the bug
180func (*Filters) andMatch(filters []Filter, repoCache *RepoCache, excerpt *BugExcerpt) bool {
181 if len(filters) == 0 {
182 return true
183 }
184
185 match := true
186 for _, f := range filters {
187 match = match && f(repoCache, excerpt)
188 }
189
190 return match
191}