package dispatch import ( "fmt" "strings" "gitea.maximumdirect.net/ejr/feedkit/config" "gitea.maximumdirect.net/ejr/feedkit/event" ) // CompileRoutes converts config.Config routes into dispatch.Route rules. // // Behavior: // - If cfg.Routes is empty, we default to "all sinks receive all kinds". // (Implemented as one Route per sink with Kinds == nil.) // - If a specific route's kinds: is omitted or empty, that route matches ALL kinds. // (Also compiled as Kinds == nil.) // - Kind strings are normalized via event.ParseKind (lowercase + trim). // // Note: config.Validate() ensures route.sink references a known sink and rejects // blank kind entries. We re-check a few invariants here anyway so CompileRoutes // is safe to call even if a daemon chooses not to call Validate(). func CompileRoutes(cfg *config.Config) ([]Route, error) { if cfg == nil { return nil, fmt.Errorf("dispatch.CompileRoutes: cfg is nil") } if len(cfg.Sinks) == 0 { return nil, fmt.Errorf("dispatch.CompileRoutes: cfg has no sinks") } // Build a quick lookup of sink names (exact match; no normalization). sinkNames := make(map[string]bool, len(cfg.Sinks)) for i, s := range cfg.Sinks { if strings.TrimSpace(s.Name) == "" { return nil, fmt.Errorf("dispatch.CompileRoutes: sinks[%d].name is empty", i) } sinkNames[s.Name] = true } // Default routing: everything to every sink. if len(cfg.Routes) == 0 { out := make([]Route, 0, len(cfg.Sinks)) for _, s := range cfg.Sinks { out = append(out, Route{ SinkName: s.Name, Kinds: nil, // nil/empty map means "all kinds" }) } return out, nil } out := make([]Route, 0, len(cfg.Routes)) for i, r := range cfg.Routes { sink := r.Sink if strings.TrimSpace(sink) == "" { return nil, fmt.Errorf("dispatch.CompileRoutes: routes[%d].sink is required", i) } if !sinkNames[sink] { return nil, fmt.Errorf("dispatch.CompileRoutes: routes[%d].sink references unknown sink %q", i, sink) } // If kinds is omitted/empty, this route matches all kinds. if len(r.Kinds) == 0 { out = append(out, Route{ SinkName: sink, Kinds: nil, }) continue } kinds := make(map[event.Kind]bool, len(r.Kinds)) for j, raw := range r.Kinds { k, err := event.ParseKind(raw) if err != nil { return nil, fmt.Errorf("dispatch.CompileRoutes: routes[%d].kinds[%d]: %w", i, j, err) } kinds[k] = true } out = append(out, Route{ SinkName: sink, Kinds: kinds, }) } return out, nil }