153 lines
4.5 KiB
Go
153 lines
4.5 KiB
Go
package ezconf
|
|
|
|
import (
|
|
"os"
|
|
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// EnvVar represents a single environment variable with its metadata
|
|
type EnvVar struct {
|
|
Name string // The environment variable name (e.g., "LOG_LEVEL")
|
|
Description string // Description of what this variable does
|
|
Required bool // Whether this variable is required
|
|
Default string // Default value if not set
|
|
CurrentValue string // Current value from environment (empty if not set)
|
|
Group string // Group name for organizing variables (e.g., "Database", "Logging")
|
|
}
|
|
|
|
// configStruct holds a config struct pointer and its group name for parsing
|
|
type configStruct struct {
|
|
configPtr any
|
|
groupName string
|
|
}
|
|
|
|
// ConfigLoader manages configuration loading from multiple sources
|
|
type ConfigLoader struct {
|
|
configFuncs map[string]ConfigFunc // Map of config names to ConfigFromEnv functions
|
|
configStructs []configStruct // Config struct pointers for tag parsing
|
|
extraEnvVars []EnvVar // Additional environment variables to track
|
|
envVars []EnvVar // All extracted environment variables
|
|
configs map[string]any // Loaded configurations
|
|
}
|
|
|
|
// ConfigFunc is a function that loads configuration from environment variables
|
|
type ConfigFunc func() (any, error)
|
|
|
|
// New creates a new ConfigLoader
|
|
func New() *ConfigLoader {
|
|
return &ConfigLoader{
|
|
configFuncs: make(map[string]ConfigFunc),
|
|
configStructs: make([]configStruct, 0),
|
|
extraEnvVars: make([]EnvVar, 0),
|
|
envVars: make([]EnvVar, 0),
|
|
configs: make(map[string]any),
|
|
}
|
|
}
|
|
|
|
// AddConfigFunc adds a ConfigFromEnv function to be called during loading.
|
|
// The name parameter is used as a key to retrieve the loaded config later.
|
|
func (cl *ConfigLoader) AddConfigFunc(name string, fn ConfigFunc) error {
|
|
if fn == nil {
|
|
return errors.New("config function cannot be nil")
|
|
}
|
|
if name == "" {
|
|
return errors.New("config name cannot be empty")
|
|
}
|
|
cl.configFuncs[name] = fn
|
|
return nil
|
|
}
|
|
|
|
// AddConfigStruct adds a config struct pointer for parsing ezconf tags.
|
|
// The configPtr must be a pointer to a struct with ezconf struct tags.
|
|
// The groupName is used for organizing environment variables in output.
|
|
func (cl *ConfigLoader) AddConfigStruct(configPtr any, groupName string) error {
|
|
if configPtr == nil {
|
|
return errors.New("config pointer cannot be nil")
|
|
}
|
|
if groupName == "" {
|
|
groupName = "Other"
|
|
}
|
|
cl.configStructs = append(cl.configStructs, configStruct{
|
|
configPtr: configPtr,
|
|
groupName: groupName,
|
|
})
|
|
return nil
|
|
}
|
|
|
|
// AddEnvVar adds an additional environment variable to track
|
|
func (cl *ConfigLoader) AddEnvVar(envVar EnvVar) {
|
|
cl.extraEnvVars = append(cl.extraEnvVars, envVar)
|
|
}
|
|
|
|
// ParseEnvVars extracts environment variables from config struct tags and extra vars.
|
|
// This can be called without having actual environment variables set.
|
|
func (cl *ConfigLoader) ParseEnvVars() error {
|
|
// Clear existing env vars to prevent duplicates
|
|
cl.envVars = make([]EnvVar, 0)
|
|
|
|
// Parse config structs for ezconf tags
|
|
for _, cs := range cl.configStructs {
|
|
envVars, err := ParseConfigStruct(cs.configPtr)
|
|
if err != nil {
|
|
return errors.Wrap(err, "failed to parse config struct")
|
|
}
|
|
|
|
// Set group name for these variables
|
|
for i := range envVars {
|
|
envVars[i].Group = cs.groupName
|
|
}
|
|
|
|
cl.envVars = append(cl.envVars, envVars...)
|
|
}
|
|
|
|
// Add extra env vars
|
|
cl.envVars = append(cl.envVars, cl.extraEnvVars...)
|
|
|
|
// Populate current values from environment
|
|
for i := range cl.envVars {
|
|
cl.envVars[i].CurrentValue = os.Getenv(cl.envVars[i].Name)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// LoadConfigs executes the config functions to load actual configurations.
|
|
// This should be called after environment variables are properly set.
|
|
func (cl *ConfigLoader) LoadConfigs() error {
|
|
// Load configurations
|
|
for name, fn := range cl.configFuncs {
|
|
cfg, err := fn()
|
|
if err != nil {
|
|
return errors.Wrapf(err, "failed to load config: %s", name)
|
|
}
|
|
cl.configs[name] = cfg
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Load loads all configurations and extracts environment variables
|
|
func (cl *ConfigLoader) Load() error {
|
|
if err := cl.ParseEnvVars(); err != nil {
|
|
return err
|
|
}
|
|
return cl.LoadConfigs()
|
|
}
|
|
|
|
// GetConfig returns a loaded configuration by name
|
|
func (cl *ConfigLoader) GetConfig(name string) (any, bool) {
|
|
cfg, ok := cl.configs[name]
|
|
return cfg, ok
|
|
}
|
|
|
|
// GetAllConfigs returns all loaded configurations
|
|
func (cl *ConfigLoader) GetAllConfigs() map[string]any {
|
|
return cl.configs
|
|
}
|
|
|
|
// GetEnvVars returns all extracted environment variables
|
|
func (cl *ConfigLoader) GetEnvVars() []EnvVar {
|
|
return cl.envVars
|
|
}
|