package sinks import ( "fmt" "strings" "gitea.maximumdirect.net/ejr/feedkit/config" ) // Factory constructs a sink instance from config. // // This is the mechanism that lets concrete daemons wire in whatever sinks they // want without main.go being full of switch statements. type Factory func(cfg config.SinkConfig) (Sink, error) type Registry struct { byDriver map[string]Factory } func NewRegistry() *Registry { return &Registry{byDriver: map[string]Factory{}} } func (r *Registry) Register(driver string, f Factory) { if r == nil { panic("sinks.Registry.Register: registry cannot be nil") } driver = strings.TrimSpace(driver) if driver == "" { panic("sinks.Registry.Register: driver cannot be empty") } if f == nil { panic(fmt.Sprintf("sinks.Registry.Register: factory cannot be nil (driver=%q)", driver)) } if r.byDriver == nil { r.byDriver = map[string]Factory{} } if _, exists := r.byDriver[driver]; exists { panic(fmt.Sprintf("sinks.Registry.Register: driver %q already registered", driver)) } r.byDriver[driver] = f } func (r *Registry) Build(cfg config.SinkConfig) (Sink, error) { if r == nil { return nil, fmt.Errorf("sinks registry is nil") } driver := strings.TrimSpace(cfg.Driver) f, ok := r.byDriver[driver] if !ok { return nil, fmt.Errorf("unknown sink driver: %q", driver) } sink, err := f(cfg) if err != nil { return nil, fmt.Errorf("build sink %q: %w", driver, err) } if sink == nil { return nil, fmt.Errorf("build sink %q: factory returned nil sink", driver) } return sink, nil }