Config provides configuration handling similar to Viper.
SetEnvPrefix sets a prefix for environment variables.
{ c.envPrefix = prefix }
AutomaticEnv enables automatic environment variable lookup.
{ c.automatic = true }
BindEnv binds a configuration key to a specific environment variable.
{ c.envBindings[key] = env }
SetDefault sets a default value for a key.
{ c.defaults[key] = value }
SetConfigName defines the base name of the config file.
{ c.cfgName = name }
SetConfigType sets the expected config file extension.
{ c.cfgType = strings.ToLower(t) }
AddConfigPath adds a path to search for the config file.
{ c.cfgPaths = append(c.cfgPaths, path) }
SetConfigFile explicitly sets the config file path.
{ c.file = file }
ReadInConfig reads the configuration file and merges values.
{
if c.file == "" {
if c.cfgName == "" {
return nil
}
for _, p := range c.cfgPaths {
name := filepath.Join(p, c.cfgName)
if c.cfgType != "" {
name += "." + c.cfgType
}
if _, err := os.Stat(name); err == nil {
c.file = name
break
}
}
if c.file == "" {
return os.ErrNotExist
}
}
data, err := os.ReadFile(c.file)
if err != nil {
return err
}
ext := strings.ToLower(filepath.Ext(c.file))
switch ext {
case ".json":
err = json.Unmarshal(data, &c.values)
case ".yaml", ".yml":
err = yaml.Unmarshal(data, &c.values)
case ".toml":
_, err = toml.Decode(string(data), &c.values)
case ".ini":
cfg, e := ini.Load(data)
if e == nil {
m := cfg.Section("").KeysHash()
for k, v := range m {
c.values[k] = v
}
}
err = e
case ".xml":
err = xml.Unmarshal(data, (*map[string]any)(&c.values))
default:
err = errors.New("unsupported config file type")
}
return err
}
{
env, ok := c.envBindings[key]
if !ok {
env = strings.ToUpper(strings.ReplaceAll(key, ".", "_"))
if c.envPrefix != "" {
env = c.envPrefix + "_" + env
}
}
val, exists := os.LookupEnv(env)
return val, exists
}
{
if c.automatic {
if v, ok := c.getEnv(key); ok {
return v, true
}
}
if v, ok := c.values[key]; ok {
return v, true
}
if v, ok := c.getEnv(key); ok {
return v, true
}
v, ok := c.defaults[key]
return v, ok
}
OnConfigChange sets a callback for configuration changes.
{ c.onChange = fn }
WatchConfig starts watching the config file for changes.
{
if c.file == "" {
return nil
}
if c.watcher != nil {
return nil
}
w, err := fsnotify.NewWatcher()
if err != nil {
return err
}
c.watcher = w
go func() {
for ev := range w.Events {
if ev.Op&fsnotify.Write == fsnotify.Write {
c.ReadInConfig()
if c.onChange != nil {
c.onChange()
}
}
}
}()
return w.Add(c.file)
}
GetString returns a string value for the key.
{
if v, ok := c.get(key); ok {
switch val := v.(type) {
case string:
return val
default:
return stringify(val)
}
}
return ""
}
GetInt returns an int value for the key.
{
if v, ok := c.get(key); ok {
switch val := v.(type) {
case int:
return val
case int64:
return int(val)
case float64:
return int(val)
case string:
i, _ := strconv.Atoi(val)
return i
}
}
return 0
}
GetBool returns a boolean value for the key.
{
if v, ok := c.get(key); ok {
switch val := v.(type) {
case bool:
return val
case string:
b, _ := strconv.ParseBool(val)
return b
case int:
return val != 0
case float64:
return val != 0
}
}
return false
}
New creates a new Config instance.
{
return &Config{
defaults: make(map[string]any),
values: make(map[string]any),
envBindings: make(map[string]string),
cfgPaths: []string{"."},
}
}
{
switch t := v.(type) {
case string:
return t
case fmt.Stringer:
return t.String()
default:
return fmt.Sprintf("%v", v)
}
}
{
c := New()
c.SetEnvPrefix("APP")
c.SetDefault("port", 8080)
if c.GetInt("port") != 8080 {
t.Fatalf("expected default 8080")
}
os.Setenv("APP_PORT", "9001")
defer os.Unsetenv("APP_PORT")
if c.GetInt("port") != 9001 {
t.Fatalf("env variable should override default")
}
}
{
tmp, err := os.CreateTemp("", "cfg*.yaml")
if err != nil {
t.Fatal(err)
}
defer os.Remove(tmp.Name())
tmp.WriteString("debug: true\nvalue: 42\n")
tmp.Close()
c := New()
c.SetConfigFile(tmp.Name())
if err := c.ReadInConfig(); err != nil {
t.Fatal(err)
}
if !c.GetBool("debug") {
t.Fatalf("expected debug true")
}
if c.GetInt("value") != 42 {
t.Fatalf("expected value 42")
}
}
{
tmp, err := os.CreateTemp("", "conf*.toml")
if err != nil {
t.Fatal(err)
}
defer os.Remove(tmp.Name())
tmp.WriteString("port=9000\n")
tmp.Close()
dir := filepath.Dir(tmp.Name())
base := filepath.Base(tmp.Name())
name := strings.TrimSuffix(base, filepath.Ext(base))
c := New()
c.SetConfigName(name)
c.SetConfigType("toml")
c.AddConfigPath(dir)
if err := c.ReadInConfig(); err != nil {
t.Fatal(err)
}
if c.GetInt("port") != 9000 {
t.Fatalf("expected port 9000")
}
}
import "encoding/json"
import "encoding/xml"
import "errors"
import "fmt"
import "os"
import "path/filepath"
import "strconv"
import "strings"
import "github.com/BurntSushi/toml"
import "github.com/fsnotify/fsnotify"
import "gopkg.in/ini.v1"
ini
import "gopkg.in/yaml.v3"
import "os"
import "path/filepath"
import "strings"
import "testing"