// Package features provides feature flag management for safe rollback during refactoring package features import ( "os" "strings" "sync" "sync/atomic" ) // FeatureFlag represents a feature flag for controlling new functionality type FeatureFlag struct { name string description string callbacks []func(bool) mu sync.RWMutex enabled atomic.Bool } // FeatureManager manages all feature flags in the application type FeatureManager struct { flags map[string]*FeatureFlag mu sync.RWMutex } var ( // Global feature manager instance manager *FeatureManager managerOnce sync.Once ) // Feature flag names const ( // UseUnifiedConfig enables the new unified configuration system UseUnifiedConfig = "USE_UNIFIED_CONFIG" // UseNewFileStructure enables the new modularized file structure UseNewFileStructure = "USE_NEW_FILE_STRUCTURE" // UseStandardErrors enables the standardized error package UseStandardErrors = "USE_STANDARD_ERRORS" // UseEnhancedLogging enables the enhanced logging system UseEnhancedLogging = "USE_ENHANCED_LOGGING" // UseOptimizedTests enables the consolidated test suite UseOptimizedTests = "USE_OPTIMIZED_TESTS" // UseRedisRESP enables the custom Redis RESP implementation UseRedisRESP = "USE_REDIS_RESP" ) // GetManager returns the global feature manager instance func GetManager() *FeatureManager { managerOnce.Do(func() { manager = &FeatureManager{ flags: make(map[string]*FeatureFlag), } manager.initialize() }) return manager } // initialize sets up default feature flags func (m *FeatureManager) initialize() { // Phase 0: Feature flags setup m.Register(UseUnifiedConfig, "Enable unified configuration package", false) m.Register(UseNewFileStructure, "Enable modularized file structure", false) m.Register(UseStandardErrors, "Enable standardized error handling", false) m.Register(UseEnhancedLogging, "Enable enhanced logging system", false) m.Register(UseOptimizedTests, "Enable optimized test suite", false) m.Register(UseRedisRESP, "Enable custom Redis RESP implementation", false) // Load from environment variables m.LoadFromEnv() } // Register creates a new feature flag func (m *FeatureManager) Register(name, description string, defaultValue bool) { m.mu.Lock() defer m.mu.Unlock() flag := &FeatureFlag{ name: name, description: description, callbacks: make([]func(bool), 0), } flag.enabled.Store(defaultValue) m.flags[name] = flag } // IsEnabled checks if a feature flag is enabled func (m *FeatureManager) IsEnabled(name string) bool { m.mu.RLock() flag, exists := m.flags[name] m.mu.RUnlock() if !exists { return false } return flag.enabled.Load() } // Enable turns on a feature flag func (m *FeatureManager) Enable(name string) { m.setFlag(name, true) } // Disable turns off a feature flag func (m *FeatureManager) Disable(name string) { m.setFlag(name, false) } // Toggle switches a feature flag state func (m *FeatureManager) Toggle(name string) { m.mu.RLock() flag, exists := m.flags[name] m.mu.RUnlock() if exists { newValue := !flag.enabled.Load() m.setFlag(name, newValue) } } // setFlag updates a feature flag value and triggers callbacks func (m *FeatureManager) setFlag(name string, value bool) { m.mu.RLock() flag, exists := m.flags[name] m.mu.RUnlock() if !exists { return } oldValue := flag.enabled.Swap(value) // Only trigger callbacks if value actually changed if oldValue != value { flag.mu.RLock() callbacks := flag.callbacks flag.mu.RUnlock() for _, callback := range callbacks { callback(value) } } } // OnChange registers a callback to be called when a feature flag changes func (m *FeatureManager) OnChange(name string, callback func(bool)) { m.mu.RLock() flag, exists := m.flags[name] m.mu.RUnlock() if exists { flag.mu.Lock() flag.callbacks = append(flag.callbacks, callback) flag.mu.Unlock() } } // LoadFromEnv loads feature flag values from environment variables func (m *FeatureManager) LoadFromEnv() { m.mu.RLock() flags := make(map[string]*FeatureFlag) for name, flag := range m.flags { flags[name] = flag } m.mu.RUnlock() for name, flag := range flags { envVar := "FEATURE_" + name if value := os.Getenv(envVar); value != "" { enabled := strings.EqualFold(value, "true") || value == "1" flag.enabled.Store(enabled) } } } // GetAll returns all feature flags and their states func (m *FeatureManager) GetAll() map[string]bool { m.mu.RLock() defer m.mu.RUnlock() result := make(map[string]bool) for name, flag := range m.flags { result[name] = flag.enabled.Load() } return result } // Reset resets all feature flags to their default values func (m *FeatureManager) Reset() { m.mu.Lock() defer m.mu.Unlock() for _, flag := range m.flags { flag.enabled.Store(false) flag.callbacks = make([]func(bool), 0) } } // Helper functions for common checks // IsUnifiedConfigEnabled checks if unified config is enabled func IsUnifiedConfigEnabled() bool { return GetManager().IsEnabled(UseUnifiedConfig) } // IsNewFileStructureEnabled checks if new file structure is enabled func IsNewFileStructureEnabled() bool { return GetManager().IsEnabled(UseNewFileStructure) } // IsStandardErrorsEnabled checks if standard errors are enabled func IsStandardErrorsEnabled() bool { return GetManager().IsEnabled(UseStandardErrors) } // IsEnhancedLoggingEnabled checks if enhanced logging is enabled func IsEnhancedLoggingEnabled() bool { return GetManager().IsEnabled(UseEnhancedLogging) } // IsOptimizedTestsEnabled checks if optimized tests are enabled func IsOptimizedTestsEnabled() bool { return GetManager().IsEnabled(UseOptimizedTests) } // IsRedisRESPEnabled checks if custom Redis RESP is enabled func IsRedisRESPEnabled() bool { return GetManager().IsEnabled(UseRedisRESP) }