package rules // Pipeline evaluates a sequence of rules, stopping on first rejection. type Pipeline struct { rules []Rule } // NewPipeline creates a rules pipeline from the given rules. func NewPipeline(rules []Rule) *Pipeline { return &Pipeline{rules: rules} } // Evaluate runs all rules in order. Returns the first rejection or a merged accept. func (p *Pipeline) Evaluate(ctx EmailContext) RuleResult { merged := Accept() for _, r := range p.rules { result := r.Evaluate(ctx) if !result.Accepted { return result } for k, v := range result.Metadata { if merged.Metadata == nil { merged.Metadata = map[string]string{k: v} } else { merged.Metadata[k] = v } } } return merged }