Files
zarinpal-analytics-backend/internal/growth/catalog.go

39 lines
1.5 KiB
Go

package growth
import (
"fmt"
"strings"
)
// ValidateCatalog validates the database-backed catalog after it has been
// mapped into the deterministic growth model. Production catalog contents are
// intentionally not embedded here; calendar_events is the sole source.
func ValidateCatalog(events []Event) error {
seen := make(map[string]struct{}, len(events))
for index, event := range events {
if strings.TrimSpace(event.ID) == "" || strings.TrimSpace(event.TitleFA) == "" || strings.TrimSpace(event.Family) == "" || strings.TrimSpace(event.Source) == "" || strings.TrimSpace(event.SourceVersion) == "" {
return fmt.Errorf("growth catalog event %d is incomplete", index)
}
if _, exists := seen[event.ID]; exists {
return fmt.Errorf("growth catalog contains duplicate id %q", event.ID)
}
seen[event.ID] = struct{}{}
if event.StartDate.IsZero() || event.EndDate.Before(event.StartDate) {
return fmt.Errorf("growth catalog event %q has invalid date range", event.ID)
}
if event.Kind != EventOfficial && event.Kind != EventCommercial {
return fmt.Errorf("growth catalog event %q has invalid kind", event.ID)
}
if len(normalizedSet(event.Tags)) == 0 {
return fmt.Errorf("growth catalog event %q lacks tags", event.ID)
}
if event.Universal && len(event.Categories) != 0 {
return fmt.Errorf("growth catalog universal event %q must not contain categories", event.ID)
}
if !event.Universal && len(event.Categories) == 0 {
return fmt.Errorf("growth catalog scoped event %q lacks categories", event.ID)
}
}
return nil
}