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