package accessctl import ( "errors" "fmt" "github.com/bmatcuk/doublestar" ) // StringMatcher is used to match against a string. It can use one of several // methods to match. Only one field should be filled at a time. type StringMatcher struct { // Pattern, if set, indicates that the Match method should succeed if this // doublestar pattern matches against the string. Pattern string `yaml:"pattern,omitempty"` // Patterns, if set, indicates that the Match method should succeed if at // least one of these doublestar patterns matches against the string. Patterns []string `yaml:"patterns,omitempty"` } func doublestarMatch(pattern, str string) (bool, error) { ok, err := doublestar.Match(pattern, str) if err != nil { return false, fmt.Errorf("matching %q on pattern %q: %w", str, pattern, err) } return ok, nil } // Match operates similarly to the Match method of the FilterInterface, except // it only takes in strings. func (m StringMatcher) Match(str string) error { switch { case m.Pattern != "": if ok, err := doublestarMatch(m.Pattern, str); err != nil { return err } else if !ok { return ErrFilterNoMatch{ Err: fmt.Errorf("pattern %q does not match %q", m.Pattern, str), } } return nil case len(m.Patterns) > 0: for _, pattern := range m.Patterns { if ok, err := doublestarMatch(pattern, str); err != nil { return err } else if ok { return nil } } return ErrFilterNoMatch{ Err: fmt.Errorf("no patterns in %+v match %q", m.Patterns, str), } default: return errors.New(`one of the following fields must be set: "pattern", "patterns"`) } } // FilterBranch matches a CommitRequest's Branch field using a double-star // pattern. type FilterBranch struct { StringMatcher StringMatcher `yaml:",inline"` } var _ Filter = FilterBranch{} // MatchCommit implements the method for FilterInterface. func (f FilterBranch) MatchCommit(req CommitRequest) error { return f.StringMatcher.Match(req.Branch) } // FilterFilesChanged matches a CommitRequest's FilesChanged field using a // double-star pattern. It only matches if all of the CommitRequest's // FilesChanged match. type FilterFilesChanged struct { StringMatcher StringMatcher `yaml:",inline"` } var _ Filter = FilterFilesChanged{} // MatchCommit implements the method for FilterInterface. func (f FilterFilesChanged) MatchCommit(req CommitRequest) error { for _, path := range req.FilesChanged { if err := f.StringMatcher.Match(path); errors.As(err, new(ErrFilterNoMatch)) { continue } else if err != nil { return err } return nil } return ErrFilterNoMatch{Err: errors.New("no paths matched")} }