filter.go

  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}