// SPDX-License-Identifier: EUPL-1.2 // Settings, feature flags, and typed configuration for the Core framework. package core import ( "sync" ) // ConfigVar is a variable that can be set, unset, and queried for its state. type ConfigVar[T any] struct { val T set bool } // Get returns the current value. // // val := v.Get() func (v *ConfigVar[T]) Get() T { return v.val } // Set sets the value and marks it as explicitly set. // // v.Set(true) func (v *ConfigVar[T]) Set(val T) { v.val = val; v.set = true } // IsSet returns true if the value was explicitly set (distinguishes "set to false" from "never set"). // // if v.IsSet() { /* explicitly configured */ } func (v *ConfigVar[T]) IsSet() bool { return v.set } // Unset resets to zero value and marks as not set. // // v.Unset() // v.IsSet() // false func (v *ConfigVar[T]) Unset() { v.set = false var zero T v.val = zero } // NewConfigVar creates a ConfigVar with an initial value marked as set. // // debug := core.NewConfigVar(true) func NewConfigVar[T any](val T) ConfigVar[T] { return ConfigVar[T]{val: val, set: true} } // ConfigOptions holds configuration data. type ConfigOptions struct { Settings map[string]any Features map[string]bool } func (o *ConfigOptions) init() { if o.Settings == nil { o.Settings = make(map[string]any) } if o.Features == nil { o.Features = make(map[string]bool) } } // Config holds configuration settings and feature flags. type Config struct { *ConfigOptions mu sync.RWMutex } // New initialises a Config with empty settings and features. // // cfg := (&core.Config{}).New() func (e *Config) New() *Config { e.ConfigOptions = &ConfigOptions{} e.ConfigOptions.init() return e } // Set stores a configuration value by key. func (e *Config) Set(key string, val any) { e.mu.Lock() if e.ConfigOptions == nil { e.ConfigOptions = &ConfigOptions{} } e.ConfigOptions.init() e.Settings[key] = val e.mu.Unlock() } // Get retrieves a configuration value by key. func (e *Config) Get(key string) Result { e.mu.RLock() defer e.mu.RUnlock() if e.ConfigOptions == nil || e.Settings == nil { return Result{} } val, ok := e.Settings[key] if !ok { return Result{} } return Result{val, true} } // String retrieves a string config value (empty string if missing). // // host := c.Config().String("database.host") func (e *Config) String(key string) string { return ConfigGet[string](e, key) } // Int retrieves an int config value (0 if missing). // // port := c.Config().Int("database.port") func (e *Config) Int(key string) int { return ConfigGet[int](e, key) } // Bool retrieves a bool config value (false if missing). // // debug := c.Config().Bool("debug") func (e *Config) Bool(key string) bool { return ConfigGet[bool](e, key) } // ConfigGet retrieves a typed configuration value. func ConfigGet[T any](e *Config, key string) T { r := e.Get(key) if !r.OK { var zero T return zero } typed, _ := r.Value.(T) return typed } // --- Feature Flags --- // Enable activates a feature flag. // // c.Config().Enable("dark-mode") func (e *Config) Enable(feature string) { e.mu.Lock() if e.ConfigOptions == nil { e.ConfigOptions = &ConfigOptions{} } e.ConfigOptions.init() e.Features[feature] = true e.mu.Unlock() } // Disable deactivates a feature flag. // // c.Config().Disable("dark-mode") func (e *Config) Disable(feature string) { e.mu.Lock() if e.ConfigOptions == nil { e.ConfigOptions = &ConfigOptions{} } e.ConfigOptions.init() e.Features[feature] = false e.mu.Unlock() } // Enabled returns true if a feature flag is active. // // if c.Config().Enabled("dark-mode") { ... } func (e *Config) Enabled(feature string) bool { e.mu.RLock() defer e.mu.RUnlock() if e.ConfigOptions == nil || e.Features == nil { return false } return e.Features[feature] } // EnabledFeatures returns all active feature flag names. // // features := c.Config().EnabledFeatures() func (e *Config) EnabledFeatures() []string { e.mu.RLock() defer e.mu.RUnlock() if e.ConfigOptions == nil || e.Features == nil { return nil } var result []string for k, v := range e.Features { if v { result = append(result, k) } } return result }