package repository import ( "fmt" "github.com/spf13/viper" ) const ( // srrdbGroupsKey is the configuration section holding the group rules: // groups..: [group, ...]. srrdbGroupsKey = "groups" // srrdbStatusColumn is the only column the rules may target. Rows are written // through a fixed statement, so a second column would need a different way of // building the insert. srrdbStatusColumn = "status" // srrdbIgnoreValue marks groups that must not reach the database at all. It is // deliberately not a status of its own: the table rejects it with a CHECK, and // an ignored release has no reason to be stored. srrdbIgnoreValue = "ignore" ) // srrdbGroupRules is the resolved rule set: the status every configured group // receives, and the groups skipped before the insert. type srrdbGroupRules struct { status map[string]string ignore map[string]bool } // srrdbLoadGroupRules reads groups.status.: [group, ...]. // // A group listed under two values is a configuration error rather than a question // of precedence: the values arrive in a map, Go iterates it in an arbitrary order, // and the status a release receives would then change from run to run. func srrdbLoadGroupRules() (*srrdbGroupRules, error) { rules := &srrdbGroupRules{ status: make(map[string]string), ignore: make(map[string]bool), } for column, rawValues := range viper.GetStringMap(srrdbGroupsKey) { if column != srrdbStatusColumn { return nil, fmt.Errorf("%s.%s is not a column of srrdb_releases", srrdbGroupsKey, column) } values, ok := rawValues.(map[string]any) if !ok { return nil, fmt.Errorf("%s.%s must map a value to a list of groups", srrdbGroupsKey, column) } for value, rawList := range values { groups, err := srrdbGroupList(value, rawList) if err != nil { return nil, err } for _, group := range groups { if assigned, exists := rules.status[group]; exists { return nil, fmt.Errorf("group %q is listed under both %q and %q", group, assigned, value) } rules.status[group] = value if value == srrdbIgnoreValue { rules.ignore[group] = true } } } } return rules, nil } // srrdbGroupList converts one value's group list. An element that is not a string // is rejected rather than dropped: YAML reads an unquoted 401 as a number, and // losing that group silently would look like a rule that simply does not work. func srrdbGroupList(value string, raw any) ([]string, error) { switch list := raw.(type) { case []string: return list, nil case []any: groups := make([]string, 0, len(list)) for _, item := range list { group, ok := item.(string) if !ok { return nil, fmt.Errorf("%s.%s.%s holds %v, which is not a group name: quote it in the configuration", srrdbGroupsKey, srrdbStatusColumn, value, item) } groups = append(groups, group) } return groups, nil default: return nil, fmt.Errorf("%s.%s.%s must be a list of groups", srrdbGroupsKey, srrdbStatusColumn, value) } }