package settings import ( "errors" "fmt" "log" "sync" ) type ValueMonitorParseFunc[T any] func(string) (T, error) type ValueMonitor[T any] struct { mutex sync.RWMutex parent *Settings updates <-chan string key string parserFunc ValueMonitorParseFunc[T] value T } func (v *ValueMonitor[T]) GetValue() T { v.mutex.RLock() defer v.mutex.RUnlock() return v.value } func (v *ValueMonitor[T]) maintenanceThread() { ctx := v.parent.context for { select { case <-ctx.Done(): return case s, ok := <-v.updates: if !ok { return } t, err := v.parserFunc(s) if err != nil { log.Printf("ValueMonitor error parsing key %q value %q from channel: %v\n", v.key, s, err) continue } func() { v.mutex.RLock() defer v.mutex.RUnlock() v.value = t }() } } } func NewValueMonitor[T any](settings *Settings, key string, parserFunc ValueMonitorParseFunc[T]) (*ValueMonitor[T], error) { monitorChannel := make(chan string, 1) err := settings.RegisterKeyToMonitor(key, monitorChannel) if err != nil { close(monitorChannel) return nil, fmt.Errorf("error registering ValueMonitor %w", err) } initialString, ok := <-monitorChannel if !ok { close(monitorChannel) return nil, errors.New("channel closed before reading initial value") } parsed, err := parserFunc(initialString) if err != nil { close(monitorChannel) return nil, fmt.Errorf("error parsing initial value from %q: %v", initialString, err) } go func() { <-settings.context.Done() close(monitorChannel) }() results := &ValueMonitor[T]{ parent: settings, updates: monitorChannel, key: key, parserFunc: parserFunc, value: parsed, } go results.maintenanceThread() return results, nil }