package rules import ( "context" "fmt" ) // AndRule combines multiple rules with AND logic type AndRule struct { Rules []Rule `json:"and"` // Child rules (extractors can be null/omitted) Archivers []ArchiverConfig `json:"extractors,omitempty"` // Optional: only present on root rules, can be null for child rules (JSON tag kept for backward compatibility) } // Matches evaluates all child rules and returns archivers only if ALL match func (r *AndRule) Matches(ctx context.Context, metadata *URLMetadata) ([]ArchiverConfig, error) { if len(r.Rules) == 0 { return nil, nil } // Evaluate all child rules - they should return empty slice if they match (even without archivers) // We only care about match/no-match, not archivers from child rules for _, childRule := range r.Rules { archivers, err := childRule.Matches(ctx, metadata) if err != nil { return nil, fmt.Errorf("error evaluating child rule: %w", err) } // If child rule doesn't match (returns nil), AND fails // Empty slice means matched, nil means didn't match if archivers == nil { return nil, nil } } // All child rules matched, return root rule's archivers return r.Archivers, nil } // IsValid validates that the rule is properly defined func (r *AndRule) IsValid() error { if len(r.Rules) == 0 { return fmt.Errorf("and rule: at least one child rule is required") } // Validate all child rules for i, childRule := range r.Rules { if err := childRule.IsValid(); err != nil { return fmt.Errorf("and rule: child rule %d is invalid: %w", i, err) } } return nil }