func FilterIncludesPullRequests(filters []*Filter) bool {
	for _, filter := range filters {
		if f, ok := filter.Strategy.(IsFilter); ok && !f.PullRequestOnly {
			return false
		}
	}
	return true
}
FilterIncludesPullRequests is similar to FilterIncludesIssues only with
respect to pull requests.
package settings
import (
	"fmt"
	"regexp"
	"strconv"
	"strings"
	"time"
	"poule/gh"
	"poule/operations"
	"github.com/Sirupsen/logrus"
	"github.com/google/go-github/github"
	"github.com/pkg/errors"
	"github.com/urfave/cli"
)
const filterFlagName = "filter"
var FilteringFlag = cli.StringSliceFlag{
	Name:  filterFlagName,
	Usage: "filter based on item attributes",
}
func ParseCliFilters(c *cli.Context) ([]*Filter, error) {
	value, err := NewMultiValuedKeysFromSlice(c.StringSlice(filterFlagName))
	if err != nil {
		return nil, err
	}
	return ParseConfigurationFilters(value.ToSerializedFormat())
}
func ParseConfigurationFilters(values map[string]interface{}) (Filters, error) {
	filters := Filters{}
	for filterType, rawValue := range values {
		value, err := filterValue(rawValue)
		if err != nil {
			return []*Filter{}, err
		}
		filter, err := MakeFilter(filterType, value)
		if err != nil {
			return []*Filter{}, err
		}
		filters = append(filters, filter)
	}
	return filters, nil
}
type Filters []*Filter
func (f Filters) Apply(context operations.Context, item gh.Item) bool {
	for _, filter := range f {
		if !filter.Apply(context, item) {
			return false
		}
	}
	return true
}
type Filter struct {
	Strategy fmt.Stringer
}
func (f *Filter) Apply(context operations.Context, item gh.Item) bool {
	switch {
	case item.IsIssue():
		if f, ok := f.Strategy.(issueFilter); ok {
			return f.ApplyIssue(context, item.Issue)
		}
	case item.IsPullRequest():
		if f, ok := f.Strategy.(pullRequestFilter); ok {
			return f.ApplyPullRequest(context, item.PullRequest)
		}
	default:
		panic("unreachable")
	}
	return true
}
type issueFilter interface {
	ApplyIssue(operations.Context, *github.Issue) bool
	String() string
}
type pullRequestFilter interface {
	ApplyPullRequest(operations.Context, *github.PullRequest) bool
	String() string
}
func MakeFilter(filterType, value string) (*Filter, error) {
	typeMapping := map[string]func(string) (*Filter, error){
		"age":      makeAgeFilter,
		"assigned": makeAssignedFilter,
		"comments": makeCommentsFilter,
		"is":       makeIsFilter,
		"labels":   makeWithLabelsFilter,
		"~labels":  makeWithoutLabelsFilter,
		"contains": makeContainsFilter,
	}
	if constructor, ok := typeMapping[filterType]; ok {
		return constructor(value)
	}
	return nil, errors.Errorf("unknown filter type %q", filterType)
}
type ContainsFilter struct {
	regexp *regexp.Regexp
}
func makeContainsFilter(value string) (*Filter, error) {
	regex, err := regexp.Compile(value)
	if err != nil {
		return nil, err
	}
	return asFilter(ContainsFilter{regexp: regex}), nil
}
func (f ContainsFilter) filter(context operations.Context, issueNum int) bool {
	comments, _, err := context.Client.Issues().ListComments(context.Username, context.Repository, issueNum, nil)
	if err != nil {
		logrus.Error(err)
		return false
	}
	for _, comment := range comments {
		body := strings.Replace(*comment.Body, "\r", "", -1)
		if f.regexp.MatchString(body) {
			return true
		}
	}
	return false
}
func (f ContainsFilter) ApplyIssue(context operations.Context, issue *github.Issue) bool {
	return f.filter(context, *(issue.Number))
}
func (f ContainsFilter) ApplyPullRequest(context operations.Context, pullRequest *github.PullRequest) bool {
	return f.filter(context, *(pullRequest.Number))
}
func (f ContainsFilter) String() string {
	return fmt.Sprintf("ContainsFilter(%s)", f.regexp)
}
type AgeFilter struct {
	age ExtDuration
}
func makeAgeFilter(value string) (*Filter, error) {
	d, err := ParseExtDuration(value)
	if err != nil {
		return nil, errors.Errorf("invalid value %q for \"age\" filter", value)
	}
	return asFilter(AgeFilter{d}), nil
}
func (f AgeFilter) ApplyIssue(context operations.Context, issue *github.Issue) bool {
	return time.Since(*issue.CreatedAt) > f.age.Duration()
}
func (f AgeFilter) ApplyPullRequest(context operations.Context, pullRequest *github.PullRequest) bool {
	return time.Since(*pullRequest.CreatedAt) > f.age.Duration()
}
func (f AgeFilter) String() string {
	return fmt.Sprintf("AgeFilter(%s)", f.age)
}
type AssignedFilter struct {
	isAssigned bool
}
func makeAssignedFilter(value string) (*Filter, error) {
	b, err := strconv.ParseBool(value)
	if err != nil {
		return nil, errors.Errorf("invalid value %q for \"assigned\" filter", value)
	}
	return asFilter(AssignedFilter{b}), nil
}
func (f AssignedFilter) ApplyIssue(context operations.Context, issue *github.Issue) bool {
	return f.isAssigned == (issue.Assignee != nil)
}
func (f AssignedFilter) String() string {
	return fmt.Sprintf("AssignedFilter(%t)", f.isAssigned)
}
type CommentsFilter struct {
	filtValue string
	predicate func(int) bool
}
func makeCommentsFilter(value string) (*Filter, error) {
	var count int
	var operation rune
	if n, err := fmt.Sscanf(value, "%c%d", &operation, &count); n != 2 || err != nil {
		return nil, errors.Errorf("invalid value %q for \"comments\" filter", value)
	}
	var predicate func(int) bool
	switch operation {
	case '<':
		predicate = func(n int) bool { return n < count }
		break
	case '=':
		predicate = func(n int) bool { return n == count }
		break
	case '>':
		predicate = func(n int) bool { return n > count }
		break
	default:
		return nil, errors.Errorf("invalid operator %c for \"comments\" filter", operation)
	}
	return asFilter(CommentsFilter{
		filtValue: value,
		predicate: predicate,
	}), nil
}
func (f CommentsFilter) ApplyIssue(context operations.Context, issue *github.Issue) bool {
	return f.predicate(*issue.Comments)
}
func (f CommentsFilter) String() string {
	return fmt.Sprintf("CommentsFilter(%s)", f.filtValue)
}
func (f CommentsFilter) ApplyPullRequest(context operations.Context, pullRequest *github.PullRequest) bool {
	return f.predicate(*pullRequest.Comments)
}
type IsFilter struct {
	PullRequestOnly bool
}
func makeIsFilter(value string) (*Filter, error) {
	switch value {
	case "pr":
		return asFilter(IsFilter{PullRequestOnly: true}), nil
	case "issue":
		return asFilter(IsFilter{PullRequestOnly: false}), nil
	default:
		return nil, errors.Errorf("invalid value %q for \"is\" filter", value)
	}
}
func (f IsFilter) ApplyIssue(context operations.Context, issue *github.Issue) bool {
	return !f.PullRequestOnly && (issue.PullRequestLinks == nil)
}
func (f IsFilter) ApplyPullRequest(context operations.Context, pullRequest *github.PullRequest) bool {
	return f.PullRequestOnly
}
func (f IsFilter) String() string {
	return fmt.Sprintf("IsFilter(PullRequestOnly=%t)", f.PullRequestOnly)
}
type WithLabelsFilter struct {
	labels []string
}
func makeWithLabelsFilter(value string) (*Filter, error) {
	labels := strings.Split(value, ",")
	return asFilter(WithLabelsFilter{labels}), nil
}
func (f WithLabelsFilter) ApplyIssue(context operations.Context, issue *github.Issue) bool {
	return gh.HasAllLabels(f.labels, issue.Labels)
}
func (f WithLabelsFilter) String() string {
	return fmt.Sprintf("WithLabelsFilter(%s)", strings.Join(f.labels, ","))
}
type WithoutLabelsFilter struct {
	labels []string
}
func makeWithoutLabelsFilter(value string) (*Filter, error) {
	labels := strings.Split(value, ",")
	return asFilter(WithoutLabelsFilter{labels}), nil
}
func (f WithoutLabelsFilter) ApplyIssue(context operations.Context, issue *github.Issue) bool {
	return !gh.HasAnyLabels(f.labels, issue.Labels)
}
func (f WithoutLabelsFilter) String() string {
	return fmt.Sprintf("WithoutLabelsFilter(%s)", strings.Join(f.labels, ","))
}
func asFilter(impl fmt.Stringer) *Filter {
	return &Filter{
		Strategy: impl,
	}
}
func filterValue(value interface{}) (string, error) {
	if s, ok := value.(string); ok {
		return s, nil
	}
	if s, ok := value.([]interface{}); ok {
		strslice := []string{}
		for _, v := range s {
			if str, ok := v.(string); ok {
				strslice = append(strslice, str)
			} else {
				return "", errors.Errorf("non-string \"%v\" in filter value", v)
			}
		}
		value = strslice
	}
	if s, ok := value.([]string); ok {
		return strings.Join(s, ","), nil
	}
	return "", errors.Errorf("invalid data type \"%#v\" for filter value", value)
}
func FilterIncludesIssues(filters []*Filter) bool {
	for _, filter := range filters {
		if f, ok := filter.Strategy.(IsFilter); ok && f.PullRequestOnly {
			return false
		}
	}
	return true
}