// Package config provides the Viper-based implementation of the ConfigProvider interface. package config import ( "fmt" "time" "git.dcentral.systems/toolz/goplt/pkg/config" "github.com/spf13/viper" ) // viperConfig implements the ConfigProvider interface using Viper. type viperConfig struct { v *viper.Viper } // NewViperConfig creates a new Viper-based configuration provider. func NewViperConfig(v *viper.Viper) config.ConfigProvider { return &viperConfig{v: v} } // Get retrieves a configuration value by key. func (vc *viperConfig) Get(key string) any { return vc.v.Get(key) } // Unmarshal unmarshals the entire configuration into the provided struct. func (vc *viperConfig) Unmarshal(v any) error { return vc.v.Unmarshal(v) } // GetString retrieves a string value by key. func (vc *viperConfig) GetString(key string) string { return vc.v.GetString(key) } // GetInt retrieves an integer value by key. func (vc *viperConfig) GetInt(key string) int { return vc.v.GetInt(key) } // GetBool retrieves a boolean value by key. func (vc *viperConfig) GetBool(key string) bool { return vc.v.GetBool(key) } // GetStringSlice retrieves a string slice value by key. func (vc *viperConfig) GetStringSlice(key string) []string { return vc.v.GetStringSlice(key) } // GetDuration retrieves a duration value by key. func (vc *viperConfig) GetDuration(key string) time.Duration { return vc.v.GetDuration(key) } // IsSet checks if a configuration key is set. func (vc *viperConfig) IsSet(key string) bool { return vc.v.IsSet(key) } // LoadConfig loads configuration from files and environment variables. // It follows this precedence order (highest to lowest): // 1. Environment variables // 2. Environment-specific YAML files (development.yaml, production.yaml) // 3. Default YAML file (default.yaml) // // The env parameter determines which environment-specific file to load. // Supported values: "development", "production", or empty string for default only. func LoadConfig(env string) (config.ConfigProvider, error) { v := viper.New() // Set default configuration file name v.SetConfigName("default") v.SetConfigType("yaml") v.AddConfigPath("config") // Read default configuration if err := v.ReadInConfig(); err != nil { return nil, fmt.Errorf("failed to read default config: %w", err) } // Load environment-specific configuration if specified if env != "" { v.SetConfigName(env) // Merge environment-specific config (if it exists) if err := v.MergeInConfig(); err != nil { // Environment-specific file is optional, so we only warn // but don't fail if it doesn't exist if _, ok := err.(viper.ConfigFileNotFoundError); !ok { return nil, fmt.Errorf("failed to merge environment config: %w", err) } } } // Enable environment variable support v.AutomaticEnv() // Environment variables can be set in UPPER_SNAKE_CASE format // and will automatically map to nested keys (e.g., SERVER_PORT -> server.port) // Viper handles this automatically with AutomaticEnv() return NewViperConfig(v), nil }