package accessctl import ( "fmt" "github.com/bmatcuk/doublestar" ) // AccessControl represents an access control object being defined in the // Config. type AccessControl struct { Pattern string `yaml:"pattern"` Condition Condition `yaml:"condition"` } // ErrNoApplicableAccessControls is returned from ApplicableAccessControls when // a changed path has no applicable AccessControls which match it. type ErrNoApplicableAccessControls struct { Path string } func (err ErrNoApplicableAccessControls) Error() string { return fmt.Sprintf("no AccessControls which apply to changed file %q", err.Path) } // ApplicableAccessControls returns a subset of the given AccessControls which // are applicable to the given file paths (ie those whose Conditions must be met // in order for the changes to go through. func ApplicableAccessControls(accessControls []AccessControl, filesChanged []string) ([]AccessControl, error) { applicableSet := map[AccessControl]struct{}{} for _, path := range filesChanged { var any bool for _, ac := range accessControls { if ok, err := doublestar.PathMatch(ac.Pattern, path); err != nil { return nil, fmt.Errorf("error matching path %q to patterrn %q: %w", path, ac.Pattern, err) } else if ok { applicableSet[ac] = struct{}{} any = true break } } if !any { return nil, ErrNoApplicableAccessControls{Path: path} } } applicable := make([]AccessControl, 0, len(applicableSet)) for ac := range applicableSet { applicable = append(applicable, ac) } return applicable, nil }