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 author.Match(query)
37 }
38
39 // Legacy identity support
40 return strings.Contains(strings.ToLower(excerpt.LegacyAuthor.Name), query) ||
41 strings.Contains(strings.ToLower(excerpt.LegacyAuthor.Login), query)
42 }
43}
44
45// LabelFilter return a Filter that match a label
46func LabelFilter(label string) Filter {
47 return func(repoCache *RepoCache, excerpt *BugExcerpt) bool {
48 for _, l := range excerpt.Labels {
49 if string(l) == label {
50 return true
51 }
52 }
53 return false
54 }
55}
56
57// ActorFilter return a Filter that match a bug actor
58func ActorFilter(query string) Filter {
59 return func(repoCache *RepoCache, excerpt *BugExcerpt) bool {
60 query = strings.ToLower(query)
61
62 for _, id := range excerpt.Actors {
63 identityExcerpt, ok := repoCache.identitiesExcerpts[id]
64 if !ok {
65 panic("missing identity in the cache")
66 }
67
68 if identityExcerpt.Match(query) {
69 return true
70 }
71 }
72 return false
73 }
74}
75
76// ParticipantFilter return a Filter that match a bug participant
77func ParticipantFilter(query string) Filter {
78 return func(repoCache *RepoCache, excerpt *BugExcerpt) bool {
79 query = strings.ToLower(query)
80
81 for _, id := range excerpt.Participants {
82 identityExcerpt, ok := repoCache.identitiesExcerpts[id]
83 if !ok {
84 panic("missing identity in the cache")
85 }
86
87 if identityExcerpt.Match(query) {
88 return true
89 }
90 }
91 return false
92 }
93}
94
95// TitleFilter return a Filter that match if the title contains the given query
96func TitleFilter(query string) Filter {
97 return func(repo *RepoCache, excerpt *BugExcerpt) bool {
98 return strings.Contains(
99 strings.ToLower(excerpt.Title),
100 strings.ToLower(query),
101 )
102 }
103}
104
105// NoLabelFilter return a Filter that match the absence of labels
106func NoLabelFilter() Filter {
107 return func(repoCache *RepoCache, excerpt *BugExcerpt) bool {
108 return len(excerpt.Labels) == 0
109 }
110}
111
112// Filters is a collection of Filter that implement a complex filter
113type Filters struct {
114 Status []Filter
115 Author []Filter
116 Actor []Filter
117 Participant []Filter
118 Label []Filter
119 Title []Filter
120 NoFilters []Filter
121}
122
123// Match check if a bug match the set of filters
124func (f *Filters) Match(repoCache *RepoCache, excerpt *BugExcerpt) bool {
125 if match := f.orMatch(f.Status, repoCache, excerpt); !match {
126 return false
127 }
128
129 if match := f.orMatch(f.Author, repoCache, excerpt); !match {
130 return false
131 }
132
133 if match := f.orMatch(f.Participant, repoCache, excerpt); !match {
134 return false
135 }
136
137 if match := f.orMatch(f.Actor, repoCache, excerpt); !match {
138 return false
139 }
140
141 if match := f.andMatch(f.Label, repoCache, excerpt); !match {
142 return false
143 }
144
145 if match := f.andMatch(f.NoFilters, repoCache, excerpt); !match {
146 return false
147 }
148
149 if match := f.andMatch(f.Title, repoCache, excerpt); !match {
150 return false
151 }
152
153 return true
154}
155
156// Check if any of the filters provided match the bug
157func (*Filters) orMatch(filters []Filter, repoCache *RepoCache, excerpt *BugExcerpt) bool {
158 if len(filters) == 0 {
159 return true
160 }
161
162 match := false
163 for _, f := range filters {
164 match = match || f(repoCache, excerpt)
165 }
166
167 return match
168}
169
170// Check if all of the filters provided match the bug
171func (*Filters) andMatch(filters []Filter, repoCache *RepoCache, excerpt *BugExcerpt) bool {
172 if len(filters) == 0 {
173 return true
174 }
175
176 match := true
177 for _, f := range filters {
178 match = match && f(repoCache, excerpt)
179 }
180
181 return match
182}