Config struct

Config provides configuration handling similar to Viper.

Fields:

  • defaults (map[string]any)
  • values (map[string]any)
  • envPrefix (string)
  • envBindings (map[string]string)
  • cfgName (string)
  • cfgType (string)
  • cfgPaths ([]string)
  • file (string)
  • automatic (bool)
  • watcher (*fsnotify.Watcher)
  • onChange (func())

Methods:

SetEnvPrefix

SetEnvPrefix sets a prefix for environment variables.


Parameters:
  • prefix string

Show/Hide Method Body
{ c.envPrefix = prefix }

AutomaticEnv

AutomaticEnv enables automatic environment variable lookup.


Show/Hide Method Body
{ c.automatic = true }

BindEnv

BindEnv binds a configuration key to a specific environment variable.


Parameters:
  • key string
  • env string

Show/Hide Method Body
{ c.envBindings[key] = env }

SetDefault

SetDefault sets a default value for a key.


Parameters:
  • key string
  • value any

Show/Hide Method Body
{ c.defaults[key] = value }

SetConfigName

SetConfigName defines the base name of the config file.


Parameters:
  • name string

Show/Hide Method Body
{ c.cfgName = name }

SetConfigType

SetConfigType sets the expected config file extension.


Parameters:
  • t string

Show/Hide Method Body
{ c.cfgType = strings.ToLower(t) }

AddConfigPath

AddConfigPath adds a path to search for the config file.


Parameters:
  • path string

Show/Hide Method Body
{ c.cfgPaths = append(c.cfgPaths, path) }

SetConfigFile

SetConfigFile explicitly sets the config file path.


Parameters:
  • file string

Show/Hide Method Body
{ c.file = file }

ReadInConfig

ReadInConfig reads the configuration file and merges values.


Returns:
  • error

Show/Hide Method Body
{
	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
}

getEnv


Parameters:
  • key string

Returns:
  • string
  • bool

Show/Hide Method Body
{
	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
}

get


Parameters:
  • key string

Returns:
  • any
  • bool

Show/Hide Method Body
{
	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

OnConfigChange sets a callback for configuration changes.


Parameters:
  • fn func()

Show/Hide Method Body
{ c.onChange = fn }

WatchConfig

WatchConfig starts watching the config file for changes.


Returns:
  • error

Show/Hide Method Body
{
	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

GetString returns a string value for the key.


Parameters:
  • key string

Returns:
  • string

Show/Hide Method Body
{
	if v, ok := c.get(key); ok {
		switch val := v.(type) {
		case string:
			return val
		default:
			return stringify(val)
		}
	}
	return ""
}

GetInt

GetInt returns an int value for the key.


Parameters:
  • key string

Returns:
  • int

Show/Hide Method Body
{
	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

GetBool returns a boolean value for the key.


Parameters:
  • key string

Returns:
  • bool

Show/Hide Method Body
{
	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 function

New creates a new Config instance.

Returns:

  • *Config
Show/Hide Function Body
{
	return &Config{
		defaults:    make(map[string]any),
		values:      make(map[string]any),
		envBindings: make(map[string]string),
		cfgPaths:    []string{"."},
	}
}

stringify function

Parameters:

  • v any

Returns:

  • string
Show/Hide Function Body
{
	switch t := v.(type) {
	case string:
		return t
	case fmt.Stringer:
		return t.String()
	default:
		return fmt.Sprintf("%v", v)
	}
}

TestDefaultsAndEnv function

Parameters:

  • t *testing.T
Show/Hide Function Body
{
	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")
	}
}

TestReadConfigFile function

Parameters:

  • t *testing.T
Show/Hide Function Body
{
	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")
	}
}

TestConfigNameAndPath function

Parameters:

  • t *testing.T
Show/Hide Function Body
{
	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")
	}
}

encoding/json import

Import example:

import "encoding/json"

encoding/xml import

Import example:

import "encoding/xml"

errors import

Import example:

import "errors"

fmt import

Import example:

import "fmt"

os import

Import example:

import "os"

path/filepath import

Import example:

import "path/filepath"

strconv import

Import example:

import "strconv"

strings import

Import example:

import "strings"

github.com/BurntSushi/toml import

Import example:

import "github.com/BurntSushi/toml"

github.com/fsnotify/fsnotify import

Import example:

import "github.com/fsnotify/fsnotify"

gopkg.in/ini.v1 import

Import example:

import "gopkg.in/ini.v1"

Imported as:

ini

gopkg.in/yaml.v3 import

Import example:

import "gopkg.in/yaml.v3"

os import

Import example:

import "os"

path/filepath import

Import example:

import "path/filepath"

strings import

Import example:

import "strings"

testing import

Import example:

import "testing"