filter.go

  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}