feedkit now contains a reusable core, while weatherfeeder is a concrete implementation that includes weather-specific functions.
50 lines
1.4 KiB
Go
50 lines
1.4 KiB
Go
package sources
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"gitea.maximumdirect.net/ejr/feedkit/config"
|
|
)
|
|
|
|
// Factory constructs a configured Source instance from config.
|
|
//
|
|
// This is how concrete daemons (weatherfeeder/newsfeeder/...) register their
|
|
// domain-specific source drivers (Open-Meteo, NWS, RSS, etc.) while feedkit
|
|
// remains domain-agnostic.
|
|
type Factory func(cfg config.SourceConfig) (Source, error)
|
|
|
|
type Registry struct {
|
|
byDriver map[string]Factory
|
|
}
|
|
|
|
func NewRegistry() *Registry {
|
|
return &Registry{byDriver: map[string]Factory{}}
|
|
}
|
|
|
|
// Register associates a driver name (e.g. "openmeteo_observation") with a factory.
|
|
//
|
|
// The driver string is the "lookup key" used by config.sources[].driver.
|
|
func (r *Registry) Register(driver string, f Factory) {
|
|
driver = strings.TrimSpace(driver)
|
|
if driver == "" {
|
|
// Panic is appropriate here: registering an empty driver is always a programmer error,
|
|
// and it will lead to extremely confusing runtime behavior if allowed.
|
|
panic("sources.Registry.Register: driver cannot be empty")
|
|
}
|
|
if f == nil {
|
|
panic(fmt.Sprintf("sources.Registry.Register: factory cannot be nil (driver=%q)", driver))
|
|
}
|
|
|
|
r.byDriver[driver] = f
|
|
}
|
|
|
|
// Build constructs a Source from a SourceConfig by looking up cfg.Driver.
|
|
func (r *Registry) Build(cfg config.SourceConfig) (Source, error) {
|
|
f, ok := r.byDriver[cfg.Driver]
|
|
if !ok {
|
|
return nil, fmt.Errorf("unknown source driver: %q", cfg.Driver)
|
|
}
|
|
return f(cfg)
|
|
}
|