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