package sinks import ( "fmt" "strings" "gitea.maximumdirect.net/ejr/feedkit/config" ) // RegisterBuiltins registers sink drivers included in this binary. // // In feedkit, these are "infrastructure primitives" — they are not domain-specific. // Individual daemons can choose to call this (or register their own custom sinks). func RegisterBuiltins(r *Registry) { // Stdout sink: great for debugging, piping to jq, etc. r.Register("stdout", func(cfg config.SinkConfig) (Sink, error) { return NewStdoutSink(cfg.Name), nil }) // File sink: writes/archives events somewhere on disk. r.Register("file", func(cfg config.SinkConfig) (Sink, error) { return NewFileSinkFromConfig(cfg) }) // Postgres sink: persists events durably. r.Register("postgres", func(cfg config.SinkConfig) (Sink, error) { return NewPostgresSinkFromConfig(cfg) }) // NATS sink: publishes events to a broker for downstream consumers. r.Register("nats", func(cfg config.SinkConfig) (Sink, error) { return NewNATSSinkFromConfig(cfg) }) } // ---- helpers for validating sink params ---- // // These helpers live in sinks (not config) on purpose: // - config is domain-agnostic and should not embed driver-specific validation helpers. // - sinks are adapters; validating their own params here keeps the logic near the driver. func requireStringParam(cfg config.SinkConfig, key string) (string, error) { v, ok := cfg.Params[key] if !ok { return "", fmt.Errorf("sink %q: params.%s is required", cfg.Name, key) } s, ok := v.(string) if !ok { return "", fmt.Errorf("sink %q: params.%s must be a string", cfg.Name, key) } if strings.TrimSpace(s) == "" { return "", fmt.Errorf("sink %q: params.%s cannot be empty", cfg.Name, key) } return s, nil }