package validators import ( "fmt" "strings" "gitea.maximumdirect.net/eric/notarius/internal/core/artifacts" "gitea.maximumdirect.net/eric/notarius/internal/framework/contracts" ) const ( ReasonApproved = "approved" ) func Approved(candidateIndex int) contracts.ValidationDecision { return contracts.ValidationDecision{ CandidateIndex: candidateIndex, Approved: true, ReasonCode: ReasonApproved, Message: ReasonApproved, } } func Rejected(candidateIndex int, reasonCode string, message string) contracts.ValidationDecision { return contracts.ValidationDecision{ CandidateIndex: candidateIndex, Approved: false, ReasonCode: strings.TrimSpace(reasonCode), Message: strings.TrimSpace(message), } } func EnforceDecisionCardinality(candidates []artifacts.Candidate, decisions []contracts.ValidationDecision) error { if len(candidates) != len(decisions) { return fmt.Errorf("validator returned %d decisions for %d candidates", len(decisions), len(candidates)) } expected := make(map[int]struct{}, len(candidates)) for _, candidate := range candidates { if _, ok := expected[candidate.Index]; ok { return fmt.Errorf("candidate index %d is duplicated", candidate.Index) } expected[candidate.Index] = struct{}{} } seen := make(map[int]struct{}, len(decisions)) for _, decision := range decisions { if _, ok := expected[decision.CandidateIndex]; !ok { return fmt.Errorf("validator returned decision for unknown candidate index %d", decision.CandidateIndex) } if _, ok := seen[decision.CandidateIndex]; ok { return fmt.Errorf("validator returned duplicate decision for candidate index %d", decision.CandidateIndex) } seen[decision.CandidateIndex] = struct{}{} } for candidateIndex := range expected { if _, ok := seen[candidateIndex]; !ok { return fmt.Errorf("validator did not return decision for candidate index %d", candidateIndex) } } return nil }