Files
shelly-exporter/collector/factory.go
T
lostakj 9db7aa2560
Build Docker image on push / docker (push) Successful in 22s
Initila commit
2026-08-18 00:46:59 +02:00

86 lines
2.4 KiB
Go

// Package collector maps Shelly product types to Prometheus collectors.
package collector
import (
"fmt"
"sort"
"strings"
"sync"
"unicode"
"github.com/prometheus/client_golang/prometheus"
"lostak.dev/shelly-exporter/configuration"
"lostak.dev/shelly-exporter/shelly"
)
// Factory constructs a collector for one configured device.
type Factory func(configuration.DeviceConfiguration, shelly.StatusClient) (prometheus.Collector, error)
var (
factoriesMutex sync.RWMutex
factories = make(map[string]Factory)
)
// RegisterProduct adds support for a product identifier. Product packages call it from init.
func RegisterProduct(product string, factory Factory) error {
product = normalizeProduct(product)
if product == "" {
return fmt.Errorf("product identifier cannot be empty")
}
if factory == nil {
return fmt.Errorf("factory for product %q cannot be nil", product)
}
factoriesMutex.Lock()
defer factoriesMutex.Unlock()
if _, exists := factories[product]; exists {
return fmt.Errorf("product %q is already registered", product)
}
factories[product] = factory
return nil
}
// NewDeviceCollector creates the registered collector for a configured product.
func NewDeviceCollector(config configuration.DeviceConfiguration, client shelly.StatusClient) (prometheus.Collector, error) {
product := normalizeProduct(config.Product)
factoriesMutex.RLock()
factory, exists := factories[product]
factoriesMutex.RUnlock()
if !exists {
return nil, fmt.Errorf("unsupported Shelly product %q (supported: %s)", config.Product, strings.Join(SupportedProducts(), ", "))
}
config.Product = product
return factory(config, client)
}
// SupportedProducts returns all registered identifiers in stable order.
func SupportedProducts() []string {
factoriesMutex.RLock()
defer factoriesMutex.RUnlock()
products := make([]string, 0, len(factories))
for product := range factories {
products = append(products, product)
}
sort.Strings(products)
return products
}
func normalizeProduct(product string) string {
var normalized strings.Builder
separator := false
for _, character := range strings.ToLower(strings.TrimSpace(product)) {
if unicode.IsLetter(character) || unicode.IsDigit(character) {
if separator && normalized.Len() > 0 {
normalized.WriteByte('_')
}
normalized.WriteRune(character)
separator = false
} else {
separator = true
}
}
return normalized.String()
}